From 5c5f8a688ff3c3e02695e6094f54b3f3ceea2eed Mon Sep 17 00:00:00 2001 From: Youssef1313 Date: Sun, 3 Apr 2022 09:19:13 +0200 Subject: [PATCH 001/508] Add new line before 'where' constraints in Quick Info --- ...DisplayService.SymbolDescriptionBuilder.cs | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/Features/CSharp/Portable/LanguageServices/CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs b/src/Features/CSharp/Portable/LanguageServices/CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs index 31f2839c0402d..5196408c7166e 100644 --- a/src/Features/CSharp/Portable/LanguageServices/CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs +++ b/src/Features/CSharp/Portable/LanguageServices/CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -114,7 +115,29 @@ protected override Task> GetInitializerSourceP } protected override ImmutableArray ToMinimalDisplayParts(ISymbol symbol, SemanticModel semanticModel, int position, SymbolDisplayFormat format) - => CodeAnalysis.CSharp.SymbolDisplay.ToMinimalDisplayParts(symbol, semanticModel, position, format); + { + var displayParts = CodeAnalysis.CSharp.SymbolDisplay.ToMinimalDisplayParts(symbol, semanticModel, position, format); + var typeParameter = symbol.GetTypeParameters(); + if (typeParameter.Length == 0) + { + return displayParts; + } + + // For readability, we add every 'where' on its own line. + var builder = ImmutableArray.CreateBuilder(); + for (var i = 0; i < displayParts.Length; i++) + { + var part = displayParts[i]; + if (part.Kind == SymbolDisplayPartKind.Keyword && part.ToString() == SyntaxFacts.GetText(SyntaxKind.WhereKeyword)) + { + builder.AddRange(LineBreak()); + } + + builder.Add(part); + } + + return builder.ToImmutable(); + } protected override string GetNavigationHint(ISymbol symbol) => symbol == null ? null : CodeAnalysis.CSharp.SymbolDisplay.ToDisplayString(symbol, SymbolDisplayFormat.MinimallyQualifiedFormat); From 658a8d5b97f7aa13157da0ebac1ed476156fc2c9 Mon Sep 17 00:00:00 2001 From: Youssef1313 Date: Thu, 23 Jun 2022 13:14:39 +0200 Subject: [PATCH 002/508] Add four spaces --- .../CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Features/CSharp/Portable/LanguageServices/CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs b/src/Features/CSharp/Portable/LanguageServices/CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs index 5196408c7166e..f2d5dc9efd5b3 100644 --- a/src/Features/CSharp/Portable/LanguageServices/CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs +++ b/src/Features/CSharp/Portable/LanguageServices/CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs @@ -131,6 +131,7 @@ protected override ImmutableArray ToMinimalDisplayParts(ISymb if (part.Kind == SymbolDisplayPartKind.Keyword && part.ToString() == SyntaxFacts.GetText(SyntaxKind.WhereKeyword)) { builder.AddRange(LineBreak()); + builder.AddRange(Space(4)); } builder.Add(part); From 1036dc5ce0739bcd95c1c189474f851e41f75058 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 12 Jan 2023 23:33:51 +0200 Subject: [PATCH 003/508] Improve preprocessor symbols --- .../Compilation/CSharpSemanticModel.cs | 35 ++++++++++++++++--- .../Compilation/SyntaxTreeSemanticModel.cs | 27 ++++++++++++-- .../PublicModel/PreprocessingSymbol.cs | 25 +++++++++---- .../CSharp/Portable/Syntax/SyntaxKindFacts.cs | 9 +++++ .../Shared/Extensions/ISymbolExtensions_2.cs | 4 +++ .../SymbolMapping/ISymbolMappingService.cs | 4 +-- .../FindAllReferencesHandlerTests.cs | 25 +++++++++++++ 7 files changed, 114 insertions(+), 15 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index 8588bac47319b..dd6e80ab207ed 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -60,7 +60,7 @@ internal static bool CanGetSemanticInfo(CSharpSyntaxNode node, bool allowNamedAr { Debug.Assert(node != null); - if (!isSpeculative && IsInStructuredTriviaOtherThanCrefOrNameAttribute(node)) + if (!isSpeculative && IsInStructuredTriviaNotContainingIdentifiers(node)) { return false; } @@ -955,6 +955,13 @@ public SymbolInfo GetSpeculativeSymbolInfo(int position, CrefSyntax cref, Symbol { return CSharpTypeInfo.None; } + else if (IsInStructuredTriviaNotContainingIdentifiers(expression, out var decisiveParentSyntaxKind)) + { + if (SyntaxFacts.IsIdentifierContainerDirectiveTrivia(decisiveParentSyntaxKind)) + { + return CSharpTypeInfo.None; + } + } else if (SyntaxFacts.IsDeclarationExpressionType(expression, out DeclarationExpressionSyntax parent)) { switch (parent.Designation.Kind()) @@ -1233,15 +1240,29 @@ internal bool IsInTree(SyntaxNode node) return node.SyntaxTree == this.SyntaxTree; } - private static bool IsInStructuredTriviaOtherThanCrefOrNameAttribute(CSharpSyntaxNode node) + private static bool IsInStructuredTriviaNotContainingIdentifiers(CSharpSyntaxNode node) + { + return IsInStructuredTriviaNotContainingIdentifiers(node, out _); + } + private static bool IsInStructuredTriviaNotContainingIdentifiers(CSharpSyntaxNode node, out SyntaxKind decisiveParentSyntaxKind) { + decisiveParentSyntaxKind = default; while (node != null) { - if (node.Kind() == SyntaxKind.XmlCrefAttribute || node.Kind() == SyntaxKind.XmlNameAttribute) + decisiveParentSyntaxKind = node.Kind(); + switch (decisiveParentSyntaxKind) + { + case SyntaxKind.XmlCrefAttribute: + case SyntaxKind.XmlNameAttribute: + return false; + } + + if (SyntaxFacts.IsIdentifierContainerDirectiveTrivia(decisiveParentSyntaxKind)) { return false; } - else if (node.IsStructuredTrivia) + + if (node.IsStructuredTrivia) { return true; } @@ -4827,7 +4848,11 @@ public PreprocessingSymbolInfo GetPreprocessingSymbolInfo(IdentifierNameSyntax n if (node.Ancestors().Any(n => SyntaxFacts.IsPreprocessorDirective(n.Kind()))) { bool isDefined = this.SyntaxTree.IsPreprocessorSymbolDefined(node.Identifier.ValueText, node.Identifier.SpanStart); - return new PreprocessingSymbolInfo(new Symbols.PublicModel.PreprocessingSymbol(node.Identifier.ValueText), isDefined); + var preprocessingSymbol = new Symbols.PublicModel.PreprocessingSymbol( + node.Identifier.ValueText, + Compilation.Assembly.ISymbol as IAssemblySymbol, + Compilation.SourceModule.ISymbol as IModuleSymbol); + return new PreprocessingSymbolInfo(preprocessingSymbol, isDefined); } return PreprocessingSymbolInfo.None; diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs index 6ca3308a3287c..bdada0c604320 100644 --- a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs @@ -9,6 +9,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -206,7 +207,6 @@ internal override IOperation GetOperationWorker(CSharpSyntaxNode node, Cancellat SymbolInfo result; XmlNameAttributeSyntax attrSyntax; - CrefSyntax crefSyntax; if (model != null) { @@ -268,11 +268,23 @@ internal override IOperation GetOperationWorker(CSharpSyntaxNode node, Cancellat } } } - else if ((crefSyntax = node as CrefSyntax) != null) + else if (node is CrefSyntax crefSyntax) { int adjustedPosition = GetAdjustedNodePosition(crefSyntax); result = GetCrefSymbolInfo(adjustedPosition, crefSyntax, options, HasParameterList(crefSyntax)); } + else if (IsWithinIdentifierContainerDirectiveTrivia(node)) + { + var identifierName = node as IdentifierNameSyntax; + + result = SymbolInfo.None; + + var info = this.GetPreprocessingSymbolInfo(node); + if (info.Symbol is not null) + { + result = new SymbolInfo(info.Symbol); + } + } else { // if expression is not part of a member context then caller may really just have a @@ -284,6 +296,17 @@ internal override IOperation GetOperationWorker(CSharpSyntaxNode node, Cancellat return result; } + private static bool IsWithinIdentifierContainerDirectiveTrivia(CSharpSyntaxNode node) + { + return node is IdentifierNameSyntax + && node.FirstAncestorOrSelf(IsIdentifierContainerDirectiveTrivia) + is not null; + } + private static bool IsIdentifierContainerDirectiveTrivia(CSharpSyntaxNode node) + { + return SyntaxFacts.IsIdentifierContainerDirectiveTrivia(node.Kind()); + } + internal override SymbolInfo GetCollectionInitializerSymbolInfoWorker(InitializerExpressionSyntax collectionInitializer, ExpressionSyntax node, CancellationToken cancellationToken = default(CancellationToken)) { var model = this.GetMemberModel(collectionInitializer); diff --git a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs index 065b926fd69e9..a235885ecbf7f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs @@ -12,10 +12,14 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols.PublicModel internal sealed class PreprocessingSymbol : IPreprocessingSymbol { private readonly string _name; + private readonly IAssemblySymbol _assembly; + private readonly IModuleSymbol _module; - internal PreprocessingSymbol(string name) + internal PreprocessingSymbol(string name, IAssemblySymbol assembly, IModuleSymbol module) { _name = name; + _assembly = assembly; + _module = module; } ISymbol ISymbol.OriginalDefinition => this; @@ -65,11 +69,20 @@ bool ISymbol.Equals(ISymbol? other, CodeAnalysis.SymbolEqualityComparer equality Accessibility ISymbol.DeclaredAccessibility => Accessibility.NotApplicable; - void ISymbol.Accept(SymbolVisitor visitor) => throw new System.NotSupportedException(); + void ISymbol.Accept(SymbolVisitor visitor) + { + visitor.DefaultVisit(this); + } - TResult ISymbol.Accept(SymbolVisitor visitor) => throw new System.NotSupportedException(); + TResult ISymbol.Accept(SymbolVisitor visitor) + { + return visitor.DefaultVisit(this)!; + } - TResult ISymbol.Accept(SymbolVisitor visitor, TArgument argument) => throw new System.NotSupportedException(); + TResult ISymbol.Accept(SymbolVisitor visitor, TArgument argument) + { + return visitor.DefaultVisit(this, argument); + } string? ISymbol.GetDocumentationCommentId() => null; @@ -105,9 +118,9 @@ ImmutableArray ISymbol.ToMinimalDisplayParts(SemanticModel se int ISymbol.MetadataToken => 0; - IAssemblySymbol? ISymbol.ContainingAssembly => null; + IAssemblySymbol? ISymbol.ContainingAssembly => _assembly; - IModuleSymbol? ISymbol.ContainingModule => null; + IModuleSymbol? ISymbol.ContainingModule => _module; INamespaceSymbol? ISymbol.ContainingNamespace => null; diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs index d6624d597b04b..498ca0631c7e4 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs @@ -1779,5 +1779,14 @@ public static bool IsDocumentationCommentTrivia(SyntaxKind kind) return kind == SyntaxKind.SingleLineDocumentationCommentTrivia || kind == SyntaxKind.MultiLineDocumentationCommentTrivia; } + + public static bool IsIdentifierContainerDirectiveTrivia(SyntaxKind kind) + { + return kind + is SyntaxKind.IfDirectiveTrivia + or SyntaxKind.ElifDirectiveTrivia + or SyntaxKind.DefineDirectiveTrivia + or SyntaxKind.UndefDirectiveTrivia; + } } } diff --git a/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs b/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs index 0a5c49dc63f88..93ecb15b352c0 100644 --- a/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs +++ b/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs @@ -153,6 +153,10 @@ MethodKind.Conversion or case SymbolKind.TypeParameter: return Glyph.TypeParameter; + // Temporary fix + case SymbolKind.Preprocessing: + return Glyph.Label; + default: throw new ArgumentException(FeaturesResources.The_symbol_does_not_have_an_icon, nameof(symbol)); } diff --git a/src/Features/Core/Portable/SymbolMapping/ISymbolMappingService.cs b/src/Features/Core/Portable/SymbolMapping/ISymbolMappingService.cs index 13d443a5ed8aa..7a4a95f0d0255 100644 --- a/src/Features/Core/Portable/SymbolMapping/ISymbolMappingService.cs +++ b/src/Features/Core/Portable/SymbolMapping/ISymbolMappingService.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.SymbolMapping internal interface ISymbolMappingService : IWorkspaceService { /// - /// Given a and the document whence the corresponding + /// Given a and the document whence the corresponding /// came, locate an identical symbol in the correct solution for performing common symbol operations /// (e.g. find references) as defined by this service. /// @@ -22,7 +22,7 @@ internal interface ISymbolMappingService : IWorkspaceService Task MapSymbolAsync(Document document, SymbolKey symbolId, CancellationToken cancellationToken = default); /// - /// Given an and the document whence the corresponding + /// Given an and the document whence the corresponding /// came, locate an identical symbol in the correct solution for performing common symbol operations /// (e.g. find references) as defined by this service. /// diff --git a/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs index 62fb7855dc599..943ab970a3621 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs @@ -292,6 +292,31 @@ public async Task TestFindAllReferencesAsync_StaticClassification() Assert.Equal(9, textRuns.Count()); } + [Fact] + public async Task TestFindAllReferencesAsync_PreprocessingSymbol() + { + var markup = +@"#define {|reference:PREPROCESSING_SYMBOL|} +#define MORE_PREPROCESSING_SYMBOL + +#if {|reference:PREPROCESSING_SYMBOL|} +namespace SimpleNamespace; +#elif true && (!false || {|caret:|}{|reference:PREPROCESSING_SYMBOL|}) +namespace AnotherNamespace; +#elif MORE_PREPROCESSING_SYMBOL +namespace MoreSimpleNamespace; +#else +namespace ComplexNamespace; +#endif +"; + await using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); + + var results = await RunFindAllReferencesAsync(testLspServer, testLspServer.GetLocations("caret").First()); + + // Do not assert the glyph + AssertHighlightCount(results, expectedDefinitionCount: 0, expectedWrittenReferenceCount: 0, expectedReferenceCount: 4); + } + private static LSP.ReferenceParams CreateReferenceParams(LSP.Location caret, IProgress progress) => new LSP.ReferenceParams() { From ea86eab5b4263aa06f2e47b8d7411c57e7844106 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 14 Jan 2023 11:02:05 +0200 Subject: [PATCH 004/508] Implement finder and enhance preprocessing symbols --- .../Compilation/CSharpSemanticModel.cs | 44 +++++++++-- .../Portable/Compilation/SemanticModel.cs | 16 ++++ .../Core/Portable/PublicAPI.Unshipped.txt | 2 + .../Portable/Compilation/SemanticModel.vb | 45 +++++++++-- .../Portable/Symbols/PreprocessingSymbol.vb | 44 +++++++++-- ...alBasicSyntaxTree.ConditionalSymbolsMap.vb | 11 +++ .../Portable/Syntax/VisualBasicSyntaxTree.vb | 6 +- .../Portable/VisualBasicExtensions.vb | 6 ++ .../FindAllReferencesHandlerTests.cs | 2 +- .../FindReferences/FindReferenceCache.cs | 6 ++ .../Finders/AbstractReferenceFinder.cs | 22 +++++- .../PreprocessingSymbolReferenceFinder.cs | 75 +++++++++++++++++++ .../Finders/ReferenceFinders.cs | 2 + .../Services/SyntaxFacts/CSharpSyntaxKinds.cs | 3 + .../SyntaxFacts/ISyntaxFactsExtensions.cs | 29 ++++++- .../Core/Services/SyntaxFacts/ISyntaxKinds.cs | 3 + .../SyntaxFacts/VisualBasicSyntaxKinds.vb | 3 + 17 files changed, 295 insertions(+), 24 deletions(-) create mode 100644 src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index dd6e80ab207ed..ad09222984694 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -4837,6 +4837,24 @@ private ImmutableArray CreateReducedExtensionMethodIfPossible(BoundDeleg /// The node. public abstract AwaitExpressionInfo GetAwaitExpressionInfo(AwaitExpressionSyntax node); + /// + /// If the given token is within a preprocessing directive, gets the preprocessing symbol info for it. + /// Define and undefine directive trivia parents are supported too. + /// + /// Preprocessing symbol syntax token. + public new PreprocessingSymbolInfo GetPreprocessingSymbolInfo(SyntaxToken token) + { + var parent = token.Parent as CSharpSyntaxNode; + CheckSyntaxNode(parent); + + if (parent.Kind() is SyntaxKind.DefineDirectiveTrivia or SyntaxKind.UndefDirectiveTrivia) + return CreatePreprocessingSymbolInfo(token); + + if (parent is IdentifierNameSyntax identifier) + return GetPreprocessingSymbolInfo(identifier); + + return PreprocessingSymbolInfo.None; + } /// /// If the given node is within a preprocessing directive, gets the preprocessing symbol info for it. /// @@ -4847,17 +4865,26 @@ public PreprocessingSymbolInfo GetPreprocessingSymbolInfo(IdentifierNameSyntax n if (node.Ancestors().Any(n => SyntaxFacts.IsPreprocessorDirective(n.Kind()))) { - bool isDefined = this.SyntaxTree.IsPreprocessorSymbolDefined(node.Identifier.ValueText, node.Identifier.SpanStart); - var preprocessingSymbol = new Symbols.PublicModel.PreprocessingSymbol( - node.Identifier.ValueText, - Compilation.Assembly.ISymbol as IAssemblySymbol, - Compilation.SourceModule.ISymbol as IModuleSymbol); - return new PreprocessingSymbolInfo(preprocessingSymbol, isDefined); + return CreatePreprocessingSymbolInfo(node.Identifier); } return PreprocessingSymbolInfo.None; } + private PreprocessingSymbolInfo CreatePreprocessingSymbolInfo(in SyntaxToken identifier) + { + bool isDefined = SyntaxTree.IsPreprocessorSymbolDefined(identifier.ValueText, identifier.SpanStart); + var preprocessingSymbol = CreatePreprocessingSymbol(identifier); + return new(preprocessingSymbol, isDefined); + } + private Symbols.PublicModel.PreprocessingSymbol CreatePreprocessingSymbol(in SyntaxToken identifier) + { + return new( + identifier.ValueText, + Compilation.Assembly.ISymbol as IAssemblySymbol, + Compilation.SourceModule.ISymbol as IModuleSymbol); + } + /// /// Options to control the internal working of GetSymbolInfoWorker. Not currently exposed /// to public clients, but could be if desired. @@ -5066,6 +5093,11 @@ protected sealed override IAliasSymbol GetAliasInfoCore(SyntaxNode node, Cancell return node is IdentifierNameSyntax nameSyntax ? GetAliasInfo(nameSyntax, cancellationToken) : null; } + protected sealed override PreprocessingSymbolInfo GetPreprocessingSymbolInfoCore(SyntaxToken token) + { + return GetPreprocessingSymbolInfo(token); + } + protected sealed override PreprocessingSymbolInfo GetPreprocessingSymbolInfoCore(SyntaxNode node) { return node is IdentifierNameSyntax nameSyntax diff --git a/src/Compilers/Core/Portable/Compilation/SemanticModel.cs b/src/Compilers/Core/Portable/Compilation/SemanticModel.cs index 45ecd74cdac89..d3aa79b9d0939 100644 --- a/src/Compilers/Core/Portable/Compilation/SemanticModel.cs +++ b/src/Compilers/Core/Portable/Compilation/SemanticModel.cs @@ -841,6 +841,15 @@ public bool IsEventUsableAsField(int position, IEventSymbol eventSymbol) /// protected abstract bool IsEventUsableAsFieldCore(int position, IEventSymbol eventSymbol); + /// + /// If is an identifier token for a preprocessor symbol, return the corresponding + /// to it. + /// + /// The syntax token to get semantic information for. + public PreprocessingSymbolInfo GetPreprocessingSymbolInfo(SyntaxToken token) + { + return GetPreprocessingSymbolInfoCore(token); + } /// /// If is an identifier name syntax node, return the corresponding /// to it. @@ -851,6 +860,13 @@ public PreprocessingSymbolInfo GetPreprocessingSymbolInfo(SyntaxNode nameSyntax) return GetPreprocessingSymbolInfoCore(nameSyntax); } + /// + /// If is an identifier token for a preprocessor symbol, return the corresponding + /// to it. + /// + /// The syntax token to get semantic information for. + protected abstract PreprocessingSymbolInfo GetPreprocessingSymbolInfoCore(SyntaxToken token); + /// /// If is an identifier name syntax node, return the corresponding /// to it. diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 7be29a3050bed..37c29b7ddecfa 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -1,5 +1,6 @@ *REMOVED*override abstract Microsoft.CodeAnalysis.CompilationOptions.GetHashCode() -> int *REMOVED*static Microsoft.CodeAnalysis.ModuleMetadata.CreateFromMetadata(System.IntPtr metadata, int size, System.IDisposable! owner, bool disposeOwner) -> Microsoft.CodeAnalysis.ModuleMetadata! +abstract Microsoft.CodeAnalysis.SemanticModel.GetPreprocessingSymbolInfoCore(Microsoft.CodeAnalysis.SyntaxToken token) -> Microsoft.CodeAnalysis.PreprocessingSymbolInfo Microsoft.CodeAnalysis.Compilation.CreateBuiltinOperator(string! name, Microsoft.CodeAnalysis.ITypeSymbol! returnType, Microsoft.CodeAnalysis.ITypeSymbol! leftType, Microsoft.CodeAnalysis.ITypeSymbol! rightType) -> Microsoft.CodeAnalysis.IMethodSymbol! Microsoft.CodeAnalysis.Compilation.CreateBuiltinOperator(string! name, Microsoft.CodeAnalysis.ITypeSymbol! returnType, Microsoft.CodeAnalysis.ITypeSymbol! operandType) -> Microsoft.CodeAnalysis.IMethodSymbol! Microsoft.CodeAnalysis.Diagnostics.CodeBlockAnalysisContext.IsGeneratedCode.get -> bool @@ -21,6 +22,7 @@ Microsoft.CodeAnalysis.ScopedKind Microsoft.CodeAnalysis.ScopedKind.None = 0 -> Microsoft.CodeAnalysis.ScopedKind Microsoft.CodeAnalysis.ScopedKind.ScopedRef = 1 -> Microsoft.CodeAnalysis.ScopedKind Microsoft.CodeAnalysis.ScopedKind.ScopedValue = 2 -> Microsoft.CodeAnalysis.ScopedKind +Microsoft.CodeAnalysis.SemanticModel.GetPreprocessingSymbolInfo(Microsoft.CodeAnalysis.SyntaxToken token) -> Microsoft.CodeAnalysis.PreprocessingSymbolInfo Microsoft.CodeAnalysis.SymbolDisplayLocalOptions.IncludeModifiers = 4 -> Microsoft.CodeAnalysis.SymbolDisplayLocalOptions Microsoft.CodeAnalysis.SymbolDisplayParameterOptions.IncludeModifiers = 2 -> Microsoft.CodeAnalysis.SymbolDisplayParameterOptions Microsoft.CodeAnalysis.SyntaxNode.ContainsDirective(int rawKind) -> bool diff --git a/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb b/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb index f6e947c7be76c..31a7fbfddbae5 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb @@ -2915,6 +2915,29 @@ _Default: Friend MustOverride Function GetAwaitExpressionInfoWorker(awaitExpression As AwaitExpressionSyntax, Optional cancellationToken As CancellationToken = Nothing) As AwaitExpressionInfo + ''' + ''' If the given token is within a preprocessing directive, gets the preprocessing symbol info for it. + ''' + ''' Preprocessing symbol syntax token. + Public Shadows Function GetPreprocessingSymbolInfo(token As SyntaxToken) As VisualBasicPreprocessingSymbolInfo + + Dim parent = DirectCast(token.Parent, VisualBasicSyntaxNode) + CheckSyntaxNode(parent) + + If parent.Kind() = SyntaxKind.ConstKeyword Then + Dim symbolInfo As VisualBasicPreprocessingSymbolInfo = token.SyntaxTree.GetPreprocessingSymbolInfo(token) + + Return RetrieveOrConstructPreprocessingSymbolInfo(symbolInfo, token) + End If + + Dim identifierParent = TryCast(parent, IdentifierNameSyntax) + If identifierParent IsNot Nothing Then + Return GetPreprocessingSymbolInfo(identifierParent) + End If + + Return VisualBasicPreprocessingSymbolInfo.None + End Function + ''' ''' If the given node is within a preprocessing directive, gets the preprocessing symbol info for it. ''' @@ -2925,17 +2948,23 @@ _Default: If SyntaxFacts.IsWithinPreprocessorConditionalExpression(node) Then Dim symbolInfo As VisualBasicPreprocessingSymbolInfo = node.SyntaxTree.GetPreprocessingSymbolInfo(node) - If symbolInfo.Symbol IsNot Nothing Then - Debug.Assert(CaseInsensitiveComparison.Equals(symbolInfo.Symbol.Name, node.Identifier.ValueText)) - Return symbolInfo - End If - - Return New VisualBasicPreprocessingSymbolInfo(New PreprocessingSymbol(node.Identifier.ValueText), constantValueOpt:=Nothing, isDefined:=False) + Return RetrieveOrConstructPreprocessingSymbolInfo(symbolInfo, node.Identifier) End If Return VisualBasicPreprocessingSymbolInfo.None End Function + Private Function RetrieveOrConstructPreprocessingSymbolInfo(symbolInfo As VisualBasicPreprocessingSymbolInfo, token As SyntaxToken) As VisualBasicPreprocessingSymbolInfo + + If symbolInfo.Symbol IsNot Nothing Then + Debug.Assert(CaseInsensitiveComparison.Equals(symbolInfo.Symbol.Name, token.ValueText)) + Return symbolInfo + End If + + Return New VisualBasicPreprocessingSymbolInfo(New PreprocessingSymbol(token.ValueText, Compilation.Assembly, Compilation.SourceModule), constantValueOpt:=Nothing, isDefined:=False) + + End Function + ''' ''' Options to control the internal working of GetSemanticInfoWorker. Not currently exposed ''' to public clients, but could be if desired. @@ -3196,6 +3225,10 @@ _Default: Return Nothing End Function + Protected NotOverridable Overrides Function GetPreprocessingSymbolInfoCore(token As SyntaxToken) As PreprocessingSymbolInfo + Return GetPreprocessingSymbolInfo(token) + End Function + Protected NotOverridable Overrides Function GetPreprocessingSymbolInfoCore(node As SyntaxNode) As PreprocessingSymbolInfo Dim nameSyntax = TryCast(node, IdentifierNameSyntax) If nameSyntax IsNot Nothing Then diff --git a/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb index 20d7d30a8d5ec..66aebd5e5f76e 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb @@ -13,10 +13,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Implements IPreprocessingSymbol Private ReadOnly _name As String + Private ReadOnly _assembly As IAssemblySymbol + Private ReadOnly _module As IModuleSymbol Friend Sub New(name As String) + Me.New(name, Nothing, Nothing) + End Sub + + Friend Sub New(name As String, assembly As IAssemblySymbol, moduleSymbol As IModuleSymbol) MyBase.New() _name = name + _assembly = assembly + _module = moduleSymbol End Sub Public Overrides ReadOnly Property Name As String @@ -91,6 +99,30 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Public Overrides ReadOnly Property ContainingAssembly As AssemblySymbol + Get + Return TryCast(ISymbolContainingAssembly, AssemblySymbol) + End Get + End Property + + Friend Overloads ReadOnly Property ISymbolContainingAssembly As IAssemblySymbol Implements ISymbol.ContainingAssembly + Get + Return _assembly + End Get + End Property + + Public Overrides ReadOnly Property ContainingModule As ModuleSymbol + Get + Return TryCast(ISymbolContainingModule, ModuleSymbol) + End Get + End Property + + Friend Overloads ReadOnly Property ISymbolContainingModule As IModuleSymbol Implements ISymbol.ContainingModule + Get + Return _module + End Get + End Property + Public Overrides Function Equals(obj As Object) As Boolean If obj Is Me Then Return True @@ -109,27 +141,27 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Function Public Overloads Overrides Sub Accept(visitor As SymbolVisitor) - Throw New NotSupportedException() + visitor.DefaultVisit(Me) End Sub Public Overloads Overrides Sub Accept(visitor As VisualBasicSymbolVisitor) - Throw New NotSupportedException() + visitor.DefaultVisit(Me) End Sub Public Overloads Overrides Function Accept(Of TResult)(visitor As SymbolVisitor(Of TResult)) As TResult - Throw New NotSupportedException() + visitor.DefaultVisit(Me) End Function Public Overrides Function Accept(Of TArgument, TResult)(visitor As SymbolVisitor(Of TArgument, TResult), argument As TArgument) As TResult - Throw New NotSupportedException() + visitor.DefaultVisit(Me, argument) End Function Public Overloads Overrides Function Accept(Of TResult)(visitor As VisualBasicSymbolVisitor(Of TResult)) As TResult - Throw New NotSupportedException() + visitor.DefaultVisit(Me) End Function Friend Overloads Overrides Function Accept(Of TArgument, TResult)(visitor As VisualBasicSymbolVisitor(Of TArgument, TResult), arg As TArgument) As TResult - Throw New NotSupportedException() + visitor.DefaultVisit(Me, arg) End Function End Class diff --git a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.ConditionalSymbolsMap.vb b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.ConditionalSymbolsMap.vb index a5caea689051e..00fbd1d991770 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.ConditionalSymbolsMap.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.ConditionalSymbolsMap.vb @@ -139,8 +139,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic #End Region + Friend Function GetPreprocessingSymbolInfo(conditionalSymbolName As String, node As SyntaxToken) As VisualBasicPreprocessingSymbolInfo + Dim constValue As InternalSyntax.CConst = GetPreprocessorSymbolValue(conditionalSymbolName, node) + Return GetPreprocessingSymbolInfo(conditionalSymbolName, constValue) + End Function + Friend Function GetPreprocessingSymbolInfo(conditionalSymbolName As String, node As IdentifierNameSyntax) As VisualBasicPreprocessingSymbolInfo Dim constValue As InternalSyntax.CConst = GetPreprocessorSymbolValue(conditionalSymbolName, node) + Return GetPreprocessingSymbolInfo(conditionalSymbolName, constValue) + End Function + + Private Function GetPreprocessingSymbolInfo(conditionalSymbolName As String, constValue As InternalSyntax.CConst) As VisualBasicPreprocessingSymbolInfo If constValue Is Nothing Then Return VisualBasicPreprocessingSymbolInfo.None End If @@ -148,6 +157,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ' Get symbol name at preprocessor definition, i.e. #Const directive. ' NOTE: symbolName and conditionalSymbolName might have different case, we want the definition name. Dim symbolName = _conditionalsMap.Keys.First(Function(key) IdentifierComparison.Equals(key, conditionalSymbolName)) + ' NOTE: Due to not having access to a semantic model when buidling this preprocessing symbol's info, + ' we allow only simply defining the symbol name, and ignoring its source module and assembly Return New VisualBasicPreprocessingSymbolInfo(New PreprocessingSymbol(name:=symbolName), constantValueOpt:=constValue.ValueAsObject, isDefined:=True) End Function diff --git a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.vb b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.vb index ad16c66e37b07..96b9970df3dcc 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.vb @@ -598,7 +598,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function Friend Function GetPreprocessingSymbolInfo(identifierNode As IdentifierNameSyntax) As VisualBasicPreprocessingSymbolInfo - Dim conditionalSymbolName As String = identifierNode.Identifier.ValueText + Return GetPreprocessingSymbolInfo(identifierNode.Identifier) + End Function + + Friend Function GetPreprocessingSymbolInfo(identifierNode As SyntaxToken) As VisualBasicPreprocessingSymbolInfo + Dim conditionalSymbolName As String = identifierNode.ValueText Dim conditionalSymbols As ConditionalSymbolsMap = Me.ConditionalSymbols Return If(conditionalSymbols Is Nothing, VisualBasicPreprocessingSymbolInfo.None, conditionalSymbols.GetPreprocessingSymbolInfo(conditionalSymbolName, identifierNode)) diff --git a/src/Compilers/VisualBasic/Portable/VisualBasicExtensions.vb b/src/Compilers/VisualBasic/Portable/VisualBasicExtensions.vb index f650e9b00560f..194196ec8c056 100644 --- a/src/Compilers/VisualBasic/Portable/VisualBasicExtensions.vb +++ b/src/Compilers/VisualBasic/Portable/VisualBasicExtensions.vb @@ -266,6 +266,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return vbTree.GetPreprocessingSymbolInfo(identifierNode) End Function + + Friend Function GetPreprocessingSymbolInfo(syntaxTree As SyntaxTree, identifierNode As SyntaxToken) As VisualBasicPreprocessingSymbolInfo + Dim vbTree = DirectCast(syntaxTree, VisualBasicSyntaxTree) + Return vbTree.GetPreprocessingSymbolInfo(identifierNode) + End Function + Friend Function Errors(trivia As SyntaxTrivia) As InternalSyntax.SyntaxDiagnosticInfoList Return New InternalSyntax.SyntaxDiagnosticInfoList(DirectCast(trivia.UnderlyingNode, InternalSyntax.VisualBasicSyntaxNode)) diff --git a/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs index 943ab970a3621..3b4056abd8ae8 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs @@ -314,7 +314,7 @@ namespace ComplexNamespace; var results = await RunFindAllReferencesAsync(testLspServer, testLspServer.GetLocations("caret").First()); // Do not assert the glyph - AssertHighlightCount(results, expectedDefinitionCount: 0, expectedWrittenReferenceCount: 0, expectedReferenceCount: 4); + AssertHighlightCount(results, expectedDefinitionCount: 0, expectedWrittenReferenceCount: 0, expectedReferenceCount: 3); } private static LSP.ReferenceParams CreateReferenceParams(LSP.Location caret, IProgress progress) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferenceCache.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferenceCache.cs index 9079eb03214d5..d69696096e50b 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferenceCache.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferenceCache.cs @@ -29,6 +29,7 @@ public static FindReferenceCache GetCache(SemanticModel model) private readonly SemanticModel _semanticModel; private readonly ConcurrentDictionary _symbolInfoCache = new(); + private readonly ConcurrentDictionary _preprocessingSymbolInfoCache = new(); private readonly ConcurrentDictionary> _identifierCache; private ImmutableHashSet? _aliasNameSet; @@ -50,6 +51,11 @@ public SymbolInfo GetSymbolInfo(SyntaxNode node, CancellationToken cancellationT return _symbolInfoCache.GetOrAdd(node, static (n, arg) => arg._semanticModel.GetSymbolInfo(n, arg.cancellationToken), (_semanticModel, cancellationToken)); } + public PreprocessingSymbolInfo GetPreprocessingSymbolInfo(SyntaxToken token, CancellationToken cancellationToken) + { + return _preprocessingSymbolInfoCache.GetOrAdd(token, static (t, arg) => arg._semanticModel.GetPreprocessingSymbolInfo(t), (_semanticModel, cancellationToken)); + } + public IAliasSymbol? GetAliasInfo( ISemanticFactsService semanticFacts, SyntaxToken token, CancellationToken cancellationToken) { diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs index 3f0e01c2635d1..6e236b598b072 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs @@ -36,9 +36,15 @@ public abstract Task> DetermineDocumentsToSearchAsync( public abstract ValueTask> FindReferencesInDocumentAsync( ISymbol symbol, FindReferencesDocumentState state, FindReferencesSearchOptions options, CancellationToken cancellationToken); - private static ValueTask<(bool matched, CandidateReason reason)> SymbolsMatchAsync( + private static async ValueTask<(bool matched, CandidateReason reason)> SymbolsMatchAsync( ISymbol symbol, FindReferencesDocumentState state, SyntaxToken token, CancellationToken cancellationToken) { + if (symbol is IPreprocessingSymbol preprocessingSearchSymbol) + { + var matched = await PreprocessingSymbolsMatchAsync(preprocessingSearchSymbol, state, token, cancellationToken).ConfigureAwait(false); + return (matched, reason: CandidateReason.None); + } + // delegates don't have exposed symbols for their constructors. so when you do `new MyDel()`, that's only a // reference to a type (as we don't have any real constructor symbols that can actually cascade to). So // don't do any special finding in that case. @@ -47,11 +53,23 @@ public abstract ValueTask> FindReferencesInDocume : state.SyntaxFacts.TryGetBindableParent(token); parent ??= token.Parent!; - return SymbolsMatchAsync(symbol, state, parent, cancellationToken); + return await SymbolsMatchAsync(symbol, state, parent, cancellationToken).ConfigureAwait(false); } protected static async ValueTask<(bool matched, CandidateReason reason)> SymbolsMatchAsync( ISymbol searchSymbol, FindReferencesDocumentState state, SyntaxNode node, CancellationToken cancellationToken) + { + return await CommonSymbolsMatchAsync(searchSymbol, state, node, cancellationToken).ConfigureAwait(false); + } + private static async ValueTask PreprocessingSymbolsMatchAsync( + IPreprocessingSymbol searchSymbol, FindReferencesDocumentState state, SyntaxToken token, CancellationToken cancellationToken) + { + var symbolInfo = state.Cache.GetPreprocessingSymbolInfo(token, cancellationToken); + + return await SymbolFinder.OriginalSymbolsMatchAsync(state.Solution, searchSymbol, symbolInfo.Symbol, cancellationToken).ConfigureAwait(false); + } + private static async ValueTask<(bool matched, CandidateReason reason)> CommonSymbolsMatchAsync( + ISymbol searchSymbol, FindReferencesDocumentState state, SyntaxNode node, CancellationToken cancellationToken) { var symbolInfo = state.Cache.GetSymbolInfo(node, cancellationToken); diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs new file mode 100644 index 0000000000000..08c761a463e52 --- /dev/null +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs @@ -0,0 +1,75 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.PooledObjects; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.FindSymbols.Finders; + +internal class PreprocessingSymbolReferenceFinder : AbstractReferenceFinder +{ + protected sealed override async ValueTask> FindReferencesInDocumentAsync( + IPreprocessingSymbol symbol, + FindReferencesDocumentState state, + FindReferencesSearchOptions options, + CancellationToken cancellationToken) + { + var tokens = await FindMatchingIdentifierTokensAsync(state, symbol.Name, cancellationToken).ConfigureAwait(false); + + var normalReferences = await FindReferencesInTokensAsync( + symbol, state, + tokens.WhereAsArray(MatchesPreprocessingReference, state), + cancellationToken).ConfigureAwait(false); + + return normalReferences; + + static bool MatchesPreprocessingReference(SyntaxToken token, FindReferencesDocumentState state) + { + var syntaxFacts = state.SyntaxFacts; + + // Quickly evaluate the common case that the parent is a #define or #undef directive + var tokenParent = token.Parent; + Debug.Assert(tokenParent is not null); + + var parentKind = tokenParent.RawKind; + if (parentKind == syntaxFacts.SyntaxKinds.DefineDirectiveTrivia) + return true; + + if (parentKind == syntaxFacts.SyntaxKinds.UndefDirectiveTrivia) + return true; + + // Only inside an #if or #elif directive are preprocessor symbols used + return syntaxFacts.SpansIfOrElseIfPreprocessorDirective(tokenParent); + } + } + + protected override bool CanFind(IPreprocessingSymbol symbol) => true; + + protected override Task> DetermineDocumentsToSearchAsync( + IPreprocessingSymbol symbol, + HashSet? globalAliases, + Project project, + IImmutableSet? documents, + FindReferencesSearchOptions options, + CancellationToken cancellationToken) + { + // NOTE: We intentionally search for multiple documents and consider multiple preprocessing + // symbols with the same name to be the same preprocessing symbol. This is because + // the symbols could either be defined in the compilation options, the project file + // or the document itself that includes the #define directive. In all of the above cases + // the preprocessing symbol is evaluated by name, and thus it's considered to be a global + // symbol that can be used based on whether it's currently defined in the given document + // and line. + // After all, writing any name for a preprocessing symbol, defined or not, is valid and will + // be computed during preprocessing evaluation of the tree + return FindDocumentsAsync(project, documents, cancellationToken, symbol.Name); + } +} diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ReferenceFinders.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ReferenceFinders.cs index 7afe3bc6cb545..6f0816b6074d4 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ReferenceFinders.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ReferenceFinders.cs @@ -23,6 +23,7 @@ internal static class ReferenceFinders public static readonly IReferenceFinder Operator = new OperatorSymbolReferenceFinder(); public static readonly IReferenceFinder OrdinaryMethod = new OrdinaryMethodReferenceFinder(); public static readonly IReferenceFinder Parameter = new ParameterSymbolReferenceFinder(); + public static readonly IReferenceFinder Preprocessing = new PreprocessingSymbolReferenceFinder(); public static readonly IReferenceFinder Property = new PropertySymbolReferenceFinder(); public static readonly IReferenceFinder PropertyAccessor = new PropertyAccessorSymbolReferenceFinder(); public static readonly IReferenceFinder RangeVariable = new RangeVariableSymbolReferenceFinder(); @@ -53,6 +54,7 @@ static ReferenceFinders() Operator, OrdinaryMethod, Parameter, + Preprocessing, Property, PropertyAccessor, RangeVariable, diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs index f5bdccb168896..b4bbbb66fb2e5 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs @@ -29,6 +29,9 @@ public TSyntaxKind Convert(int kind) where TSyntaxKind : struct public int? MultiLineDocCommentTrivia => (int)SyntaxKind.MultiLineDocumentationCommentTrivia; public int? ShebangDirectiveTrivia => (int)SyntaxKind.ShebangDirectiveTrivia; public int IfDirectiveTrivia => (int)SyntaxKind.IfDirectiveTrivia; + public int ElseIfDirectiveTrivia => (int)SyntaxKind.ElifDirectiveTrivia; + public int DefineDirectiveTrivia => (int)SyntaxKind.DefineDirectiveTrivia; + public int? UndefDirectiveTrivia => (int)SyntaxKind.UndefDirectiveTrivia; public int CloseBraceToken => (int)SyntaxKind.CloseBraceToken; public int ColonToken => (int)SyntaxKind.ColonToken; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs index 7cb1844e4e8c8..79e82a49ad61b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs @@ -205,8 +205,8 @@ public static bool SpansPreprocessorDirective(this ISyntaxFacts syntaxFacts, IEn /// /// Determines if there is preprocessor trivia *between* any of the /// provided. The will be deduped and then ordered by position. - /// Specifically, the first token will not have it's leading trivia checked, and the last - /// token will not have it's trailing trivia checked. All other trivia will be checked to + /// Specifically, the first token will not have its leading trivia checked, and the last + /// token will not have its trailing trivia checked. All other trivia will be checked to /// see if it contains a preprocessor directive. /// public static bool SpansPreprocessorDirective(this ISyntaxFacts syntaxFacts, IEnumerable tokens) @@ -248,6 +248,31 @@ public static bool SpansPreprocessorDirective(this ISyntaxFacts syntaxFacts, IEn private static bool SpansPreprocessorDirective(this ISyntaxFacts syntaxFacts, SyntaxTriviaList list) => list.Any(syntaxFacts.IsPreprocessorDirective); + public static bool SpansIfOrElseIfPreprocessorDirective(this ISyntaxFacts syntaxFacts, SyntaxToken token) + { + var parent = token.Parent; + return syntaxFacts.SpansIfOrElseIfPreprocessorDirective(parent); + } + public static bool SpansIfOrElseIfPreprocessorDirective(this ISyntaxFacts syntaxFacts, SyntaxNode? node) + { + var kinds = syntaxFacts.SyntaxKinds; + + var ifDirectiveKind = kinds.IfDirectiveTrivia; + var elseIfDirectiveKind = kinds.ElseIfDirectiveTrivia; + + while (node is not null) + { + if (node.RawKind == ifDirectiveKind) + return true; + if (node.RawKind == elseIfDirectiveKind) + return true; + + node = node.Parent; + } + + return false; + } + public static bool IsLegalIdentifier(this ISyntaxFacts syntaxFacts, string name) { if (name.Length == 0) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs index d11d639bbf1cf..ac760a0f84c5c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs @@ -35,6 +35,9 @@ internal interface ISyntaxKinds int? ShebangDirectiveTrivia { get; } int IfDirectiveTrivia { get; } + int ElseIfDirectiveTrivia { get; } + int DefineDirectiveTrivia { get; } + int? UndefDirectiveTrivia { get; } #endregion diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb index 671e4d93e9a1a..6da98bcc4a914 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb @@ -31,6 +31,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Public ReadOnly Property ShebangDirectiveTrivia As Integer? Implements ISyntaxKinds.ShebangDirectiveTrivia Public ReadOnly Property IfDirectiveTrivia As Integer = SyntaxKind.IfDirectiveTrivia Implements ISyntaxKinds.IfDirectiveTrivia + Public ReadOnly Property ElseIfDirectiveTrivia As Integer = SyntaxKind.ElseIfDirectiveTrivia Implements ISyntaxKinds.ElseIfDirectiveTrivia + Public ReadOnly Property DefineDirectiveTrivia As Integer = SyntaxKind.ConstDirectiveTrivia Implements ISyntaxKinds.DefineDirectiveTrivia + Public ReadOnly Property UndefDirectiveTrivia As Integer? Implements ISyntaxKinds.UndefDirectiveTrivia Public ReadOnly Property CloseBraceToken As Integer = SyntaxKind.CloseBraceToken Implements ISyntaxKinds.CloseBraceToken Public ReadOnly Property ColonToken As Integer = SyntaxKind.ColonToken Implements ISyntaxKinds.ColonToken From e926812e59d287549a21777b37388813b4d0b2cb Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 14 Jan 2023 11:18:49 +0200 Subject: [PATCH 005/508] Return default visit results on VB accept methods --- .../VisualBasic/Portable/Symbols/PreprocessingSymbol.vb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb index 66aebd5e5f76e..c1c9269e4f764 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb @@ -149,19 +149,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Sub Public Overloads Overrides Function Accept(Of TResult)(visitor As SymbolVisitor(Of TResult)) As TResult - visitor.DefaultVisit(Me) + Return visitor.DefaultVisit(Me) End Function Public Overrides Function Accept(Of TArgument, TResult)(visitor As SymbolVisitor(Of TArgument, TResult), argument As TArgument) As TResult - visitor.DefaultVisit(Me, argument) + Return visitor.DefaultVisit(Me, argument) End Function Public Overloads Overrides Function Accept(Of TResult)(visitor As VisualBasicSymbolVisitor(Of TResult)) As TResult - visitor.DefaultVisit(Me) + Return visitor.DefaultVisit(Me) End Function Friend Overloads Overrides Function Accept(Of TArgument, TResult)(visitor As VisualBasicSymbolVisitor(Of TArgument, TResult), arg As TArgument) As TResult - visitor.DefaultVisit(Me, arg) + Return visitor.DefaultVisit(Me, arg) End Function End Class From d69950d23919f4e471716d578972492e6fa5dfef Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 15 Jan 2023 12:43:00 +0200 Subject: [PATCH 006/508] Improve filtering through syntax tree index --- .../Portable/Syntax/SyntaxKindFacts.vb | 14 ++++++++++++++ .../FindAllReferencesHandlerTests.cs | 5 +++++ .../PreprocessingSymbolReferenceFinder.cs | 18 ++++++++++++------ .../SyntaxTree/SyntaxTreeIndex.ContextInfo.cs | 14 +++++++++++--- .../SyntaxTree/SyntaxTreeIndex_Create.cs | 5 ++++- .../SyntaxTree/SyntaxTreeIndex_Forwarders.cs | 1 + .../Services/SyntaxFacts/CSharpSyntaxFacts.cs | 3 +++ .../Core/Services/SyntaxFacts/ISyntaxFacts.cs | 1 + .../SyntaxFacts/VisualBasicSyntaxFacts.vb | 4 ++++ 9 files changed, 55 insertions(+), 10 deletions(-) diff --git a/src/Compilers/VisualBasic/Portable/Syntax/SyntaxKindFacts.vb b/src/Compilers/VisualBasic/Portable/Syntax/SyntaxKindFacts.vb index c0a4c30eae930..66fd0e6dd8b31 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/SyntaxKindFacts.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/SyntaxKindFacts.vb @@ -877,6 +877,20 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function + Public Shared Function IsIdentifierContainerDirectiveTrivia(token As SyntaxKind) As Boolean + Select Case token + + Case SyntaxKind.IfDirectiveTrivia, + SyntaxKind.ElseIfDirectiveTrivia, + SyntaxKind.ConstDirectiveTrivia + Return True + + Case Else + Return False + End Select + + End Function + End Class End Namespace diff --git a/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs index 3b4056abd8ae8..ffe39bcfe08b9 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs @@ -308,6 +308,11 @@ namespace MoreSimpleNamespace; #else namespace ComplexNamespace; #endif + +// PREPROCESSING_SYMBOL +class PREPROCESSING_SYMBOL +{ +} "; await using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs index 08c761a463e52..e6e1ea4ab670d 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -10,7 +9,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.PooledObjects; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindSymbols.Finders; @@ -35,10 +33,10 @@ static bool MatchesPreprocessingReference(SyntaxToken token, FindReferencesDocum { var syntaxFacts = state.SyntaxFacts; - // Quickly evaluate the common case that the parent is a #define or #undef directive var tokenParent = token.Parent; Debug.Assert(tokenParent is not null); + // Quickly evaluate the common case that the parent is a #define or #undef directive var parentKind = tokenParent.RawKind; if (parentKind == syntaxFacts.SyntaxKinds.DefineDirectiveTrivia) return true; @@ -46,14 +44,14 @@ static bool MatchesPreprocessingReference(SyntaxToken token, FindReferencesDocum if (parentKind == syntaxFacts.SyntaxKinds.UndefDirectiveTrivia) return true; - // Only inside an #if or #elif directive are preprocessor symbols used + // Only inside an #if or #elif directive are preprocessing symbols used return syntaxFacts.SpansIfOrElseIfPreprocessorDirective(tokenParent); } } protected override bool CanFind(IPreprocessingSymbol symbol) => true; - protected override Task> DetermineDocumentsToSearchAsync( + protected override async Task> DetermineDocumentsToSearchAsync( IPreprocessingSymbol symbol, HashSet? globalAliases, Project project, @@ -70,6 +68,14 @@ protected override Task> DetermineDocumentsToSearchAsyn // and line. // After all, writing any name for a preprocessing symbol, defined or not, is valid and will // be computed during preprocessing evaluation of the tree - return FindDocumentsAsync(project, documents, cancellationToken, symbol.Name); + + return await FindDocumentsWithPredicateAsync(project, documents, HasIdentifierContainerPreprocessorDirectiveTrivia, cancellationToken) + .ConfigureAwait(false); + + bool HasIdentifierContainerPreprocessorDirectiveTrivia(SyntaxTreeIndex syntaxTreeIndex) + { + return syntaxTreeIndex.ContainsIdentifierContainerPreprocessingDirective + && syntaxTreeIndex.ProbablyContainsIdentifier(symbol.Name); + } } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs index 3ecb5731408d8..89b8b9c635405 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs @@ -33,7 +33,8 @@ public ContextInfo( bool containsImplicitObjectCreation, bool containsGlobalSuppressMessageAttribute, bool containsConversion, - bool containsGlobalKeyword) + bool containsGlobalKeyword, + bool containsIdentifierContainerPreprocessingDirective) : this(predefinedTypes, predefinedOperators, ConvertToContainingNodeFlag( containsForEachStatement, @@ -50,7 +51,8 @@ public ContextInfo( containsImplicitObjectCreation, containsGlobalSuppressMessageAttribute, containsConversion, - containsGlobalKeyword)) + containsGlobalKeyword, + containsIdentifierContainerPreprocessingDirective)) { } @@ -76,7 +78,8 @@ private static ContainingNodes ConvertToContainingNodeFlag( bool containsImplicitObjectCreation, bool containsGlobalSuppressMessageAttribute, bool containsConversion, - bool containsGlobalKeyword) + bool containsGlobalKeyword, + bool containsIdentifierContainerPreprocessingDirective) { var containingNodes = ContainingNodes.None; @@ -95,6 +98,7 @@ private static ContainingNodes ConvertToContainingNodeFlag( containingNodes |= containsGlobalSuppressMessageAttribute ? ContainingNodes.ContainsGlobalSuppressMessageAttribute : 0; containingNodes |= containsConversion ? ContainingNodes.ContainsConversion : 0; containingNodes |= containsGlobalKeyword ? ContainingNodes.ContainsGlobalKeyword : 0; + containingNodes |= containsIdentifierContainerPreprocessingDirective ? ContainingNodes.ContainsIdentifierContainerPreprocessingDirective : 0; return containingNodes; } @@ -150,6 +154,9 @@ public bool ContainsGlobalSuppressMessageAttribute public bool ContainsConversion => (_containingNodes & ContainingNodes.ContainsConversion) == ContainingNodes.ContainsConversion; + public bool ContainsIdentifierContainerPreprocessingDirective + => (_containingNodes & ContainingNodes.ContainsIdentifierContainerPreprocessingDirective) == ContainingNodes.ContainsIdentifierContainerPreprocessingDirective; + public void WriteTo(ObjectWriter writer) { writer.WriteInt32(_predefinedTypes); @@ -193,6 +200,7 @@ private enum ContainingNodes ContainsGlobalSuppressMessageAttribute = 1 << 12, ContainsConversion = 1 << 13, ContainsGlobalKeyword = 1 << 14, + ContainsIdentifierContainerPreprocessingDirective = 1 << 15, } } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs index fa683ab0971d2..7a04041754f19 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs @@ -70,6 +70,7 @@ private static SyntaxTreeIndex CreateIndex( var containsGlobalSuppressMessageAttribute = false; var containsConversion = false; var containsGlobalKeyword = false; + var containsIdentifierContainerPreprocessorDirective = false; var predefinedTypes = (int)PredefinedType.None; var predefinedOperators = (int)PredefinedOperator.None; @@ -89,6 +90,7 @@ private static SyntaxTreeIndex CreateIndex( containsQueryExpression = containsQueryExpression || syntaxFacts.IsQueryExpression(node); containsElementAccess = containsElementAccess || (syntaxFacts.IsElementAccessExpression(node) || syntaxFacts.IsImplicitElementAccess(node)); containsIndexerMemberCref = containsIndexerMemberCref || syntaxFacts.IsIndexerMemberCref(node); + containsIdentifierContainerPreprocessorDirective = containsIdentifierContainerPreprocessorDirective || syntaxFacts.IsIdentifierContainerPreprocessorDirectiveTrivia(node); containsDeconstruction = containsDeconstruction || syntaxFacts.IsDeconstructionAssignment(node) || syntaxFacts.IsDeconstructionForEachStatement(node); @@ -186,7 +188,8 @@ private static SyntaxTreeIndex CreateIndex( containsImplicitObjectCreation, containsGlobalSuppressMessageAttribute, containsConversion, - containsGlobalKeyword), + containsGlobalKeyword, + containsIdentifierContainerPreprocessorDirective), globalAliasInfo); } finally diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs index b4fc9449f9337..3b03b03135a40 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs @@ -34,6 +34,7 @@ internal sealed partial class SyntaxTreeIndex public bool ContainsThisConstructorInitializer => _contextInfo.ContainsThisConstructorInitializer; public bool ContainsTupleExpressionOrTupleType => _contextInfo.ContainsTupleExpressionOrTupleType; public bool ContainsUsingStatement => _contextInfo.ContainsUsingStatement; + public bool ContainsIdentifierContainerPreprocessingDirective => _contextInfo.ContainsIdentifierContainerPreprocessingDirective; /// /// Gets the set of global aliases that point to something with the provided name and arity. diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs index ae114ae74942d..5787022699ede 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs @@ -115,6 +115,9 @@ public bool IsPreProcessorDirectiveContext(SyntaxTree syntaxTree, int position, => syntaxTree.IsPreProcessorDirectiveContext( position, syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken, includeDirectives: true), cancellationToken); + public bool IsIdentifierContainerPreprocessorDirectiveTrivia(SyntaxNode node) + => SyntaxFacts.IsIdentifierContainerDirectiveTrivia(node.Kind()); + public bool IsEntirelyWithinStringOrCharOrNumericLiteral([NotNullWhen(true)] SyntaxTree? syntaxTree, int position, CancellationToken cancellationToken) { if (syntaxTree == null) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs index e762f3a608805..c82ab2412bb62 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs @@ -161,6 +161,7 @@ internal interface ISyntaxFacts /// bool IsPreprocessorKeyword(SyntaxToken token); bool IsPreProcessorDirectiveContext(SyntaxTree syntaxTree, int position, CancellationToken cancellationToken); + bool IsIdentifierContainerPreprocessorDirectiveTrivia(SyntaxNode node); bool IsLiteral(SyntaxToken token); bool IsStringLiteralOrInterpolatedStringLiteral(SyntaxToken token); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb index 6badbc40e16a2..297345f5108d1 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb @@ -124,6 +124,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Return syntaxTree.IsInPreprocessorDirectiveContext(position, cancellationToken) End Function + Public Function IsIdentifierContainerPreprocessorDirectiveTrivia(syntaxNode As SyntaxNode) As Boolean Implements ISyntaxFacts.IsIdentifierContainerPreprocessorDirectiveTrivia + Return SyntaxFacts.IsIdentifierContainerDirectiveTrivia(syntaxNode.Kind()) + End Function + Public Function IsEntirelyWithinStringOrCharOrNumericLiteral(syntaxTree As SyntaxTree, position As Integer, cancellationToken As CancellationToken) As Boolean Implements ISyntaxFacts.IsEntirelyWithinStringOrCharOrNumericLiteral If syntaxTree Is Nothing Then Return False From 9c147294d2e2d76a33b0eeef404c870a32235d5c Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 15 Jan 2023 12:52:34 +0200 Subject: [PATCH 007/508] Add missing unshipped public API --- src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index 96ed633ceeb31..c75c061d0643a 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -20,3 +20,4 @@ static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ScopedType(Microsoft.CodeAnal static Microsoft.CodeAnalysis.CSharpExtensions.ContainsDirective(this Microsoft.CodeAnalysis.SyntaxNode! node, Microsoft.CodeAnalysis.CSharp.SyntaxKind kind) -> bool virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitScopedType(Microsoft.CodeAnalysis.CSharp.Syntax.ScopedTypeSyntax! node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitScopedType(Microsoft.CodeAnalysis.CSharp.Syntax.ScopedTypeSyntax! node) -> TResult? +static Microsoft.CodeAnalysis.CSharp.SyntaxFacts.IsIdentifierContainerDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.SyntaxKind kind) -> bool From f1d34ed474654ed637e8fab8eb540d89a7c24d94 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 15 Jan 2023 16:03:09 +0200 Subject: [PATCH 008/508] Fix missing public API and test cases --- src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt | 1 + .../CSharp/Impl/LanguageService/CSharpHelpContextService.cs | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt b/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt index 92c2ec8a95542..6180e0ba64670 100644 --- a/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt @@ -1,2 +1,3 @@ *REMOVED*Overrides Microsoft.CodeAnalysis.VisualBasic.VisualBasicCompilationOptions.GetHashCode() -> Integer Microsoft.CodeAnalysis.VisualBasicExtensions.ContainsDirective(node As Microsoft.CodeAnalysis.SyntaxNode, kind As Microsoft.CodeAnalysis.VisualBasic.SyntaxKind) -> Boolean +Microsoft.CodeAnalysis.VisualBasic.SyntaxFacts.IsIdentifierContainerDirectiveTrivia(token As Microsoft.CodeAnalysis.VisualBasic.SyntaxKind) -> Boolean diff --git a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs index 742c5ff0a2929..93a6f821cb8cd 100644 --- a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs +++ b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs @@ -546,7 +546,11 @@ private static string FormatNamespaceOrTypeSymbol(INamespaceOrTypeSymbol symbol) public override string? FormatSymbol(ISymbol? symbol) { - if (symbol == null) + if (symbol is null) + return null; + + // Currently consider not finding help for a preprocessing symbol + if (symbol is IPreprocessingSymbol) return null; if (symbol is ITypeSymbol or INamespaceSymbol) From f5bd2ffe0b909066208f1dfb8376dbcbd177e296 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 15 Jan 2023 22:55:21 +0200 Subject: [PATCH 009/508] Simplify and adjust as commented --- .../Portable/PublicAPI.Unshipped.txt | 2 +- .../FindReferences/FindReferenceCache.cs | 5 +- .../Finders/AbstractReferenceFinder.cs | 18 ------ .../PreprocessingSymbolReferenceFinder.cs | 56 +++++++++++++++++-- .../SyntaxTree/SyntaxTreeIndex.ContextInfo.cs | 14 +---- .../SyntaxTree/SyntaxTreeIndex_Create.cs | 5 +- .../SyntaxTree/SyntaxTreeIndex_Forwarders.cs | 1 - 7 files changed, 58 insertions(+), 43 deletions(-) diff --git a/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt b/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt index 6180e0ba64670..80498177e0ccf 100644 --- a/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt @@ -1,3 +1,3 @@ *REMOVED*Overrides Microsoft.CodeAnalysis.VisualBasic.VisualBasicCompilationOptions.GetHashCode() -> Integer Microsoft.CodeAnalysis.VisualBasicExtensions.ContainsDirective(node As Microsoft.CodeAnalysis.SyntaxNode, kind As Microsoft.CodeAnalysis.VisualBasic.SyntaxKind) -> Boolean -Microsoft.CodeAnalysis.VisualBasic.SyntaxFacts.IsIdentifierContainerDirectiveTrivia(token As Microsoft.CodeAnalysis.VisualBasic.SyntaxKind) -> Boolean +Shared Microsoft.CodeAnalysis.VisualBasic.SyntaxFacts.IsIdentifierContainerDirectiveTrivia(token As Microsoft.CodeAnalysis.VisualBasic.SyntaxKind) -> Boolean diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferenceCache.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferenceCache.cs index d69696096e50b..9f2954d283bcf 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferenceCache.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferenceCache.cs @@ -29,7 +29,6 @@ public static FindReferenceCache GetCache(SemanticModel model) private readonly SemanticModel _semanticModel; private readonly ConcurrentDictionary _symbolInfoCache = new(); - private readonly ConcurrentDictionary _preprocessingSymbolInfoCache = new(); private readonly ConcurrentDictionary> _identifierCache; private ImmutableHashSet? _aliasNameSet; @@ -51,9 +50,9 @@ public SymbolInfo GetSymbolInfo(SyntaxNode node, CancellationToken cancellationT return _symbolInfoCache.GetOrAdd(node, static (n, arg) => arg._semanticModel.GetSymbolInfo(n, arg.cancellationToken), (_semanticModel, cancellationToken)); } - public PreprocessingSymbolInfo GetPreprocessingSymbolInfo(SyntaxToken token, CancellationToken cancellationToken) + public PreprocessingSymbolInfo GetPreprocessingSymbolInfo(SyntaxToken token) { - return _preprocessingSymbolInfoCache.GetOrAdd(token, static (t, arg) => arg._semanticModel.GetPreprocessingSymbolInfo(t), (_semanticModel, cancellationToken)); + return _semanticModel.GetPreprocessingSymbolInfo(token); } public IAliasSymbol? GetAliasInfo( diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs index 6e236b598b072..63a9421604210 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs @@ -39,12 +39,6 @@ public abstract ValueTask> FindReferencesInDocume private static async ValueTask<(bool matched, CandidateReason reason)> SymbolsMatchAsync( ISymbol symbol, FindReferencesDocumentState state, SyntaxToken token, CancellationToken cancellationToken) { - if (symbol is IPreprocessingSymbol preprocessingSearchSymbol) - { - var matched = await PreprocessingSymbolsMatchAsync(preprocessingSearchSymbol, state, token, cancellationToken).ConfigureAwait(false); - return (matched, reason: CandidateReason.None); - } - // delegates don't have exposed symbols for their constructors. so when you do `new MyDel()`, that's only a // reference to a type (as we don't have any real constructor symbols that can actually cascade to). So // don't do any special finding in that case. @@ -58,18 +52,6 @@ public abstract ValueTask> FindReferencesInDocume protected static async ValueTask<(bool matched, CandidateReason reason)> SymbolsMatchAsync( ISymbol searchSymbol, FindReferencesDocumentState state, SyntaxNode node, CancellationToken cancellationToken) - { - return await CommonSymbolsMatchAsync(searchSymbol, state, node, cancellationToken).ConfigureAwait(false); - } - private static async ValueTask PreprocessingSymbolsMatchAsync( - IPreprocessingSymbol searchSymbol, FindReferencesDocumentState state, SyntaxToken token, CancellationToken cancellationToken) - { - var symbolInfo = state.Cache.GetPreprocessingSymbolInfo(token, cancellationToken); - - return await SymbolFinder.OriginalSymbolsMatchAsync(state.Solution, searchSymbol, symbolInfo.Symbol, cancellationToken).ConfigureAwait(false); - } - private static async ValueTask<(bool matched, CandidateReason reason)> CommonSymbolsMatchAsync( - ISymbol searchSymbol, FindReferencesDocumentState state, SyntaxNode node, CancellationToken cancellationToken) { var symbolInfo = state.Cache.GetSymbolInfo(node, cancellationToken); diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs index e6e1ea4ab670d..710dabc3955a8 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.FindSymbols.Finders; @@ -22,7 +23,7 @@ protected sealed override async ValueTask> FindRe { var tokens = await FindMatchingIdentifierTokensAsync(state, symbol.Name, cancellationToken).ConfigureAwait(false); - var normalReferences = await FindReferencesInTokensAsync( + var normalReferences = await FindPreprocessingReferencesInTokensAsync( symbol, state, tokens.WhereAsArray(MatchesPreprocessingReference, state), cancellationToken).ConfigureAwait(false); @@ -69,13 +70,58 @@ protected override async Task> DetermineDocumentsToSear // After all, writing any name for a preprocessing symbol, defined or not, is valid and will // be computed during preprocessing evaluation of the tree - return await FindDocumentsWithPredicateAsync(project, documents, HasIdentifierContainerPreprocessorDirectiveTrivia, cancellationToken) + return await FindDocumentsAsync(project, documents, HasDirectiveProbablyContainsIdentifier, symbol, cancellationToken) .ConfigureAwait(false); - bool HasIdentifierContainerPreprocessorDirectiveTrivia(SyntaxTreeIndex syntaxTreeIndex) + static async ValueTask HasDirectiveProbablyContainsIdentifier(Document document, IPreprocessingSymbol symbol, CancellationToken ct) { - return syntaxTreeIndex.ContainsIdentifierContainerPreprocessingDirective - && syntaxTreeIndex.ProbablyContainsIdentifier(symbol.Name); + var root = await document.GetSyntaxRootAsync(ct).ConfigureAwait(false); + if (root is not { ContainsDirectives: true }) + return false; + + var syntaxTreeIndex = await document.GetSyntaxTreeIndexAsync(ct).ConfigureAwait(false); + return syntaxTreeIndex.ProbablyContainsIdentifier(symbol.Name); + } + } + + private static async ValueTask> FindPreprocessingReferencesInTokensAsync( + ISymbol symbol, + FindReferencesDocumentState state, + ImmutableArray tokens, + CancellationToken cancellationToken) + { + using var _ = ArrayBuilder.GetInstance(out var locations); + foreach (var token in tokens) + { + cancellationToken.ThrowIfCancellationRequested(); + + var matched = await PreprocessingSymbolMatchesAsync( + symbol, state, token, cancellationToken).ConfigureAwait(false); + if (matched) + { + var finderLocation = CreateFinderLocation(state, token, cancellationToken); + + locations.Add(finderLocation); + } } + + return locations.ToImmutable(); } + + private static async ValueTask PreprocessingSymbolMatchesAsync(ISymbol symbol, FindReferencesDocumentState state, SyntaxToken token, CancellationToken cancellationToken) + { + var preprocessingSearchSymbol = symbol as IPreprocessingSymbol; + Debug.Assert(preprocessingSearchSymbol is not null); + + return await PreprocessingSymbolsMatchAsync(preprocessingSearchSymbol, state, token, cancellationToken).ConfigureAwait(false); + } + private static async ValueTask PreprocessingSymbolsMatchAsync( + IPreprocessingSymbol searchSymbol, FindReferencesDocumentState state, SyntaxToken token, CancellationToken cancellationToken) + { + var symbolInfo = state.Cache.GetPreprocessingSymbolInfo(token); + return await SymbolFinder.OriginalSymbolsMatchAsync(state.Solution, searchSymbol, symbolInfo.Symbol, cancellationToken).ConfigureAwait(false); + } + + private static FinderLocation CreateFinderLocation(FindReferencesDocumentState state, SyntaxToken token, CancellationToken cancellationToken) + => CreateFinderLocation(state, token, CandidateReason.None, cancellationToken); } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs index 89b8b9c635405..3ecb5731408d8 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs @@ -33,8 +33,7 @@ public ContextInfo( bool containsImplicitObjectCreation, bool containsGlobalSuppressMessageAttribute, bool containsConversion, - bool containsGlobalKeyword, - bool containsIdentifierContainerPreprocessingDirective) + bool containsGlobalKeyword) : this(predefinedTypes, predefinedOperators, ConvertToContainingNodeFlag( containsForEachStatement, @@ -51,8 +50,7 @@ public ContextInfo( containsImplicitObjectCreation, containsGlobalSuppressMessageAttribute, containsConversion, - containsGlobalKeyword, - containsIdentifierContainerPreprocessingDirective)) + containsGlobalKeyword)) { } @@ -78,8 +76,7 @@ private static ContainingNodes ConvertToContainingNodeFlag( bool containsImplicitObjectCreation, bool containsGlobalSuppressMessageAttribute, bool containsConversion, - bool containsGlobalKeyword, - bool containsIdentifierContainerPreprocessingDirective) + bool containsGlobalKeyword) { var containingNodes = ContainingNodes.None; @@ -98,7 +95,6 @@ private static ContainingNodes ConvertToContainingNodeFlag( containingNodes |= containsGlobalSuppressMessageAttribute ? ContainingNodes.ContainsGlobalSuppressMessageAttribute : 0; containingNodes |= containsConversion ? ContainingNodes.ContainsConversion : 0; containingNodes |= containsGlobalKeyword ? ContainingNodes.ContainsGlobalKeyword : 0; - containingNodes |= containsIdentifierContainerPreprocessingDirective ? ContainingNodes.ContainsIdentifierContainerPreprocessingDirective : 0; return containingNodes; } @@ -154,9 +150,6 @@ public bool ContainsGlobalSuppressMessageAttribute public bool ContainsConversion => (_containingNodes & ContainingNodes.ContainsConversion) == ContainingNodes.ContainsConversion; - public bool ContainsIdentifierContainerPreprocessingDirective - => (_containingNodes & ContainingNodes.ContainsIdentifierContainerPreprocessingDirective) == ContainingNodes.ContainsIdentifierContainerPreprocessingDirective; - public void WriteTo(ObjectWriter writer) { writer.WriteInt32(_predefinedTypes); @@ -200,7 +193,6 @@ private enum ContainingNodes ContainsGlobalSuppressMessageAttribute = 1 << 12, ContainsConversion = 1 << 13, ContainsGlobalKeyword = 1 << 14, - ContainsIdentifierContainerPreprocessingDirective = 1 << 15, } } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs index 7a04041754f19..fa683ab0971d2 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs @@ -70,7 +70,6 @@ private static SyntaxTreeIndex CreateIndex( var containsGlobalSuppressMessageAttribute = false; var containsConversion = false; var containsGlobalKeyword = false; - var containsIdentifierContainerPreprocessorDirective = false; var predefinedTypes = (int)PredefinedType.None; var predefinedOperators = (int)PredefinedOperator.None; @@ -90,7 +89,6 @@ private static SyntaxTreeIndex CreateIndex( containsQueryExpression = containsQueryExpression || syntaxFacts.IsQueryExpression(node); containsElementAccess = containsElementAccess || (syntaxFacts.IsElementAccessExpression(node) || syntaxFacts.IsImplicitElementAccess(node)); containsIndexerMemberCref = containsIndexerMemberCref || syntaxFacts.IsIndexerMemberCref(node); - containsIdentifierContainerPreprocessorDirective = containsIdentifierContainerPreprocessorDirective || syntaxFacts.IsIdentifierContainerPreprocessorDirectiveTrivia(node); containsDeconstruction = containsDeconstruction || syntaxFacts.IsDeconstructionAssignment(node) || syntaxFacts.IsDeconstructionForEachStatement(node); @@ -188,8 +186,7 @@ private static SyntaxTreeIndex CreateIndex( containsImplicitObjectCreation, containsGlobalSuppressMessageAttribute, containsConversion, - containsGlobalKeyword, - containsIdentifierContainerPreprocessorDirective), + containsGlobalKeyword), globalAliasInfo); } finally diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs index 3b03b03135a40..b4fc9449f9337 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs @@ -34,7 +34,6 @@ internal sealed partial class SyntaxTreeIndex public bool ContainsThisConstructorInitializer => _contextInfo.ContainsThisConstructorInitializer; public bool ContainsTupleExpressionOrTupleType => _contextInfo.ContainsTupleExpressionOrTupleType; public bool ContainsUsingStatement => _contextInfo.ContainsUsingStatement; - public bool ContainsIdentifierContainerPreprocessingDirective => _contextInfo.ContainsIdentifierContainerPreprocessingDirective; /// /// Gets the set of global aliases that point to something with the provided name and arity. From 7d807ff011d62e527611d275500082014d1feb99 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 16 Jan 2023 00:21:37 +0200 Subject: [PATCH 010/508] Add more extensive tests --- ...indReferencesTests.PreprocessingSymbols.vb | 216 ++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb new file mode 100644 index 0000000000000..5a5dc36a6886e --- /dev/null +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb @@ -0,0 +1,216 @@ + +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports System.Threading.Tasks +Imports Microsoft.CodeAnalysis.Remote.Testing + +Namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences + + Partial Public Class FindReferencesTests + + + Public Async Function TestPreprocessingSymbolBasic(kind As TestKind, host As TestHost) As Task + Dim input = + + + +#define [|PREPROCESSING_SYMBOL|] +#define MORE_PREPROCESSING_SYMBOL + +#if [|PREPROCESSING_SYMBOL|] +namespace SimpleNamespace; +#elif true && (!false || [|PRE$$PROCESSING_SYMBOL|]) +namespace AnotherNamespace; +#elif MORE_PREPROCESSING_SYMBOL +namespace MoreSimpleNamespace; +#else +namespace ComplexNamespace; +#endif + +// PREPROCESSING_SYMBOL +class PREPROCESSING_SYMBOL +{ +} + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + + Public Async Function TestNotPreprocessingSymbol(kind As TestKind, host As TestHost) As Task + Dim input = + + + +#define PREPROCESSING_SYMBOL +#define MORE_PREPROCESSING_SYMBOL + +#if PREPROCESSING_SYMBOL +namespace SimpleNamespace; +#elif true && (!false || PREPROCESSING_SYMBOL) +namespace AnotherNamespace; +#elif MORE_PREPROCESSING_SYMBOL +namespace MoreSimpleNamespace; +#else +namespace ComplexNamespace; +#endif + +// PREPROCESSING_SYMBOL +class {|Definition:PREPROCES$$SING_SYMBOL|} +{ +} + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + + Public Async Function TestPreprocessingSymbolRegion(kind As TestKind, host As TestHost) As Task + Dim input = + + + +#define PREPROCESSING_SYMBOL + +class Class +{ + #region PREP$$ROCESSING_SYMBOL + public void Method() { } + #endregion PREPROCESSING_SYMBOL +} + +#if NOT_PREPROCESSING_SYMBOL +#elif PREPROCESSING_SYMBOL +#endif + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + + Public Async Function TestPreprocessingSymbolMultipleDocuments(kind As TestKind, host As TestHost) As Task + Dim input = + + + +#define [|PREPROCESSING_SYMBOL|] +#undef [|PREPROCESSING_SYMBOL|] +#undef [|PREPROCESSING_SYMBOL|] +#define [|PREPROCESSING_SYMBOL|] + +class Class +{ + #region PREPROCESSING_SYMBOL + public void Method() { } + #endregion PREPROCESSING_SYMBOL +} + +#if NOT_PREPROCESSING_SYMBOL +#elif [|PREPROC$$ESSING_SYMBOL|] +#endif + + +#if ![|PREPROCESSING_SYMBOL|] +#define [|PREPROCESSING_SYMBOL|] +#elif [|PREPROCESSING_SYMBOL|] +#undef [|PREPROCESSING_SYMBOL|] +#endif + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + + Public Async Function TestPreprocessingSymbolMultipleProjects1(kind As TestKind, host As TestHost) As Task + Dim input = + + + +#if NOT_PREPROCESSING_SYMBOL +#elif [|PREPROCESSING_SYMBO$$L|] +#endif + + +#if ![|PREPROCESSING_SYMBOL|] +#define [|PREPROCESSING_SYMBOL|] +#elif [|PREPROCESSING_SYMBOL|] +#undef [|PREPROCESSING_SYMBOL|] +#endif + + + + +#define [|PREPROCESSING_SYMBOL|] + +#if [|PREPROCESSING_SYMBOL|] +#undef [|PREPROCESSING_SYMBOL|] +#endif + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + + Public Async Function TestPreprocessingSymbolMultipleProjects2(kind As TestKind, host As TestHost) As Task + Dim input = + + + +#define [|PREPROCESSING_SYMBOL|] + +#if [|PREPROCESSING_SYMBOL|] +#undef [|PREPROCESSING_SYMBOL|] +#endif + + + + +#Const [|PREPROCESSING_SYMBOL|] = True + + +#If DEBUG +#Else If NOT_PREPROCESSING_SYMBOL Or [|$$PREPROCESSING_SYMBOL|] +#End If + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + Public Async Function TestPreprocessingSymbolUsedInSourceGeneratedDocument(kind As TestKind, host As TestHost) As Task + Dim input = + + + +#if ![|PREPROCESSING_SYMBOL|] +#define [|PREPROCESSING$$_SYMBOL|] +#elif [|PREPROCESSING_SYMBOL|] +#undef [|PREPROCESSING_SYMBOL|] +#endif + + +#if ![|PREPROCESSING_SYMBOL|] +#define [|PREPROCESSING_SYMBOL|] +#elif [|PREPROCESSING_SYMBOL|] +#undef [|PREPROCESSING_SYMBOL|] +#endif + + + + Await TestAPIAndFeature(input, kind, host) + End Function + End Class +End Namespace From 2b9783b486628f54cf886ccab8c139c7edd78519 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 18 Jan 2023 20:54:30 +0200 Subject: [PATCH 011/508] Support finding all symbols out-of-process --- .../Portable/Compilation/CSharpCompilation.cs | 5 ++++ .../PublicModel/PreprocessingSymbol.cs | 6 ++--- .../Core/Portable/Compilation/Compilation.cs | 15 +++++++++++ .../Core/Portable/PublicAPI.Unshipped.txt | 6 ++++- .../Core/Portable/Symbols/SymbolVisitor.cs | 5 ++++ .../Core/Portable/Symbols/SymbolVisitor`1.cs | 5 ++++ .../Core/Portable/Symbols/SymbolVisitor`2.cs | 5 ++++ .../Compilation/VisualBasicCompilation.vb | 4 +++ .../Portable/Symbols/PreprocessingSymbol.vb | 12 ++++----- .../Portable/Symbols/SymbolVisitor.vb | 4 +++ .../Portable/Symbols/SymbolVisitor`1.vb | 4 +++ .../Portable/Symbols/SymbolVisitor`2.vb | 3 +++ .../Core/CompilerExtensions.projitems | 1 + .../SymbolKey/SymbolKey.NamespaceSymbolKey.cs | 2 +- .../SymbolKey.PreprocessingSymbolKey.cs | 26 +++++++++++++++++++ .../SymbolKey/SymbolKey.SymbolKeyReader.cs | 1 + .../SymbolKey/SymbolKey.SymbolKeyWriter.cs | 9 ++++++- 17 files changed, 101 insertions(+), 12 deletions(-) create mode 100644 src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index 47b7daf1124c7..c31faa3ee7b45 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -308,6 +308,11 @@ protected override INamespaceSymbol CommonCreateErrorNamespaceSymbol(INamespaceS name).GetPublicSymbol(); } + protected override IPreprocessingSymbol CommonCreatePreprocessingSymbol(string name) + { + return new Symbols.PublicModel.PreprocessingSymbol(name, CommonAssembly, CommonSourceModule); + } + #region Constructors and Factories private static readonly CSharpCompilationOptions s_defaultOptions = new CSharpCompilationOptions(OutputKind.ConsoleApplication); diff --git a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs index a235885ecbf7f..a16e438c71a80 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs @@ -71,17 +71,17 @@ bool ISymbol.Equals(ISymbol? other, CodeAnalysis.SymbolEqualityComparer equality void ISymbol.Accept(SymbolVisitor visitor) { - visitor.DefaultVisit(this); + visitor.VisitPreprocessing(this); } TResult ISymbol.Accept(SymbolVisitor visitor) { - return visitor.DefaultVisit(this)!; + return visitor.VisitPreprocessing(this)!; } TResult ISymbol.Accept(SymbolVisitor visitor, TArgument argument) { - return visitor.DefaultVisit(this, argument); + return visitor.VisitPreprocessing(this, argument); } string? ISymbol.GetDocumentationCommentId() => null; diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs index 3293b10e7c5ea..b302e4cd94311 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -367,6 +367,21 @@ public INamespaceSymbol CreateErrorNamespaceSymbol(INamespaceSymbol container, s protected abstract INamespaceSymbol CommonCreateErrorNamespaceSymbol(INamespaceSymbol container, string name); + /// + /// Returns a new IPreprocessingSymbol representing a preprocessing symbol with the given name. + /// + public IPreprocessingSymbol CreatePreprocessingSymbol(string name) + { + if (name is null) + { + throw new ArgumentNullException(nameof(name)); + } + + return CommonCreatePreprocessingSymbol(name); + } + + protected abstract IPreprocessingSymbol CommonCreatePreprocessingSymbol(string name); + #region Name internal const string UnspecifiedModuleAssemblyName = "?"; diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 37c29b7ddecfa..b0d5cc3283080 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -3,6 +3,7 @@ abstract Microsoft.CodeAnalysis.SemanticModel.GetPreprocessingSymbolInfoCore(Microsoft.CodeAnalysis.SyntaxToken token) -> Microsoft.CodeAnalysis.PreprocessingSymbolInfo Microsoft.CodeAnalysis.Compilation.CreateBuiltinOperator(string! name, Microsoft.CodeAnalysis.ITypeSymbol! returnType, Microsoft.CodeAnalysis.ITypeSymbol! leftType, Microsoft.CodeAnalysis.ITypeSymbol! rightType) -> Microsoft.CodeAnalysis.IMethodSymbol! Microsoft.CodeAnalysis.Compilation.CreateBuiltinOperator(string! name, Microsoft.CodeAnalysis.ITypeSymbol! returnType, Microsoft.CodeAnalysis.ITypeSymbol! operandType) -> Microsoft.CodeAnalysis.IMethodSymbol! +Microsoft.CodeAnalysis.Compilation.CreatePreprocessingSymbol(string! name) -> Microsoft.CodeAnalysis.IPreprocessingSymbol! Microsoft.CodeAnalysis.Diagnostics.CodeBlockAnalysisContext.IsGeneratedCode.get -> bool Microsoft.CodeAnalysis.Diagnostics.CodeBlockStartAnalysisContext.IsGeneratedCode.get -> bool Microsoft.CodeAnalysis.Diagnostics.OperationAnalysisContext.IsGeneratedCode.get -> bool @@ -35,4 +36,7 @@ Microsoft.CodeAnalysis.Operations.IAttributeOperation Microsoft.CodeAnalysis.Operations.IAttributeOperation.Operation.get -> Microsoft.CodeAnalysis.IOperation! static Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph.Create(Microsoft.CodeAnalysis.Operations.IAttributeOperation! attribute, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitAttribute(Microsoft.CodeAnalysis.Operations.IAttributeOperation! operation) -> void -virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitAttribute(Microsoft.CodeAnalysis.Operations.IAttributeOperation! operation, TArgument argument) -> TResult? \ No newline at end of file +virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitAttribute(Microsoft.CodeAnalysis.Operations.IAttributeOperation! operation, TArgument argument) -> TResult? +virtual Microsoft.CodeAnalysis.SymbolVisitor.VisitPreprocessing(Microsoft.CodeAnalysis.IPreprocessingSymbol! symbol) -> void +virtual Microsoft.CodeAnalysis.SymbolVisitor.VisitPreprocessing(Microsoft.CodeAnalysis.IPreprocessingSymbol! symbol, TArgument argument) -> TResult +virtual Microsoft.CodeAnalysis.SymbolVisitor.VisitPreprocessing(Microsoft.CodeAnalysis.IPreprocessingSymbol! symbol) -> TResult? \ No newline at end of file diff --git a/src/Compilers/Core/Portable/Symbols/SymbolVisitor.cs b/src/Compilers/Core/Portable/Symbols/SymbolVisitor.cs index 0516496a3a2f0..1783500633a8b 100644 --- a/src/Compilers/Core/Portable/Symbols/SymbolVisitor.cs +++ b/src/Compilers/Core/Portable/Symbols/SymbolVisitor.cs @@ -109,5 +109,10 @@ public virtual void VisitTypeParameter(ITypeParameterSymbol symbol) { DefaultVisit(symbol); } + + public virtual void VisitPreprocessing(IPreprocessingSymbol symbol) + { + DefaultVisit(symbol); + } } } diff --git a/src/Compilers/Core/Portable/Symbols/SymbolVisitor`1.cs b/src/Compilers/Core/Portable/Symbols/SymbolVisitor`1.cs index 119e8be223cdf..e0a2836ac4070 100644 --- a/src/Compilers/Core/Portable/Symbols/SymbolVisitor`1.cs +++ b/src/Compilers/Core/Portable/Symbols/SymbolVisitor`1.cs @@ -112,5 +112,10 @@ public abstract class SymbolVisitor { return DefaultVisit(symbol); } + + public virtual TResult? VisitPreprocessing(IPreprocessingSymbol symbol) + { + return DefaultVisit(symbol); + } } } diff --git a/src/Compilers/Core/Portable/Symbols/SymbolVisitor`2.cs b/src/Compilers/Core/Portable/Symbols/SymbolVisitor`2.cs index ea9d2eba5e8fd..09cde048362d0 100644 --- a/src/Compilers/Core/Portable/Symbols/SymbolVisitor`2.cs +++ b/src/Compilers/Core/Portable/Symbols/SymbolVisitor`2.cs @@ -117,5 +117,10 @@ public virtual TResult VisitTypeParameter(ITypeParameterSymbol symbol, TArgument { return DefaultVisit(symbol, argument); } + + public virtual TResult VisitPreprocessing(IPreprocessingSymbol symbol, TArgument argument) + { + return DefaultVisit(symbol, argument); + } } } diff --git a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb index de1ec1f00e2a8..082ffb81374f2 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb @@ -2823,6 +2823,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic name) End Function + Protected Overrides Function CommonCreatePreprocessingSymbol(name As String) As IPreprocessingSymbol + Return New PreprocessingSymbol(name, CommonAssembly, CommonSourceModule) + End Function + Protected Overrides Function CommonCreateArrayTypeSymbol(elementType As ITypeSymbol, rank As Integer, elementNullableAnnotation As NullableAnnotation) As IArrayTypeSymbol Return CreateArrayTypeSymbol(elementType.EnsureVbSymbolOrNothing(Of TypeSymbol)(NameOf(elementType)), rank) End Function diff --git a/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb index c1c9269e4f764..62838c619ea81 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb @@ -141,27 +141,27 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Function Public Overloads Overrides Sub Accept(visitor As SymbolVisitor) - visitor.DefaultVisit(Me) + visitor.VisitPreprocessing(Me) End Sub Public Overloads Overrides Sub Accept(visitor As VisualBasicSymbolVisitor) - visitor.DefaultVisit(Me) + visitor.VisitPreprocessing(Me) End Sub Public Overloads Overrides Function Accept(Of TResult)(visitor As SymbolVisitor(Of TResult)) As TResult - Return visitor.DefaultVisit(Me) + Return visitor.VisitPreprocessing(Me) End Function Public Overrides Function Accept(Of TArgument, TResult)(visitor As SymbolVisitor(Of TArgument, TResult), argument As TArgument) As TResult - Return visitor.DefaultVisit(Me, argument) + Return visitor.VisitPreprocessing(Me, argument) End Function Public Overloads Overrides Function Accept(Of TResult)(visitor As VisualBasicSymbolVisitor(Of TResult)) As TResult - Return visitor.DefaultVisit(Me) + Return visitor.VisitPreprocessing(Me) End Function Friend Overloads Overrides Function Accept(Of TArgument, TResult)(visitor As VisualBasicSymbolVisitor(Of TArgument, TResult), arg As TArgument) As TResult - Return visitor.DefaultVisit(Me, arg) + Return visitor.VisitPreprocessing(Me, arg) End Function End Class diff --git a/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor.vb b/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor.vb index 6e38667c80374..9ed9bb5f958f0 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor.vb @@ -76,5 +76,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Public Overridable Sub VisitTypeParameter(symbol As TypeParameterSymbol) DefaultVisit(symbol) End Sub + + Public Overridable Sub VisitPreprocessing(symbol As PreprocessingSymbol) + DefaultVisit(symbol) + End Sub End Class End Namespace diff --git a/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor`1.vb b/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor`1.vb index 8cf9f2417b560..380768ba4f95f 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor`1.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor`1.vb @@ -75,5 +75,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Public Overridable Function VisitTypeParameter(symbol As TypeParameterSymbol) As TResult Return DefaultVisit(symbol) End Function + + Public Overridable Function VisitPreprocessing(symbol As PreprocessingSymbol) As TResult + Return DefaultVisit(symbol) + End Function End Class End Namespace diff --git a/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor`2.vb b/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor`2.vb index 4f8a6fcb94fd5..073a5f51920d8 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor`2.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor`2.vb @@ -95,5 +95,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return DefaultVisit(symbol, arg) End Function + Public Overridable Function VisitPreprocessing(symbol As PreprocessingSymbol, arg As TArgument) As TResult + Return DefaultVisit(symbol, arg) + End Function End Class End Namespace diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems index 35d6a13ea3e33..6d71767f7d30b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems @@ -422,6 +422,7 @@ + diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.NamespaceSymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.NamespaceSymbolKey.cs index b81b9d0e4ee0d..41c2070412143 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.NamespaceSymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.NamespaceSymbolKey.cs @@ -76,7 +76,7 @@ protected sealed override SymbolKeyResolution Resolve( if (containingSymbolFailureReason != null) { - failureReason = $"({nameof(EventSymbolKey)} {nameof(containingSymbolResolution)} failed -> {containingSymbolFailureReason})"; + failureReason = $"({nameof(NamespaceSymbolKey)} {nameof(containingSymbolResolution)} failed -> {containingSymbolFailureReason})"; return default; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs new file mode 100644 index 0000000000000..a1603bfe4f442 --- /dev/null +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis; + +internal partial struct SymbolKey +{ + private class PreprocessingSymbolKey : AbstractSymbolKey + { + public static readonly PreprocessingSymbolKey Instance = new(); + + public sealed override void Create(IPreprocessingSymbol symbol, SymbolKeyWriter visitor) + { + visitor.WriteString(symbol.Name); + } + + protected sealed override SymbolKeyResolution Resolve(SymbolKeyReader reader, IPreprocessingSymbol? contextualSymbol, out string? failureReason) + { + var preprocessingName = reader.ReadString(); + failureReason = null; + var preprocessingSymbol = reader.Compilation.CreatePreprocessingSymbol(preprocessingName); + return new(preprocessingSymbol); + } + } +} diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyReader.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyReader.cs index 1826bd1cfed1f..bc2fee663f051 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyReader.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyReader.cs @@ -548,6 +548,7 @@ private SymbolKeyResolution ReadWorker(SymbolKeyType type, out string? failureRe SymbolKeyType.AnonymousType => AnonymousTypeSymbolKey.Instance.Resolve(this, out failureReason), SymbolKeyType.AnonymousFunctionOrDelegate => AnonymousFunctionOrDelegateSymbolKey.Resolve(this, out failureReason), SymbolKeyType.TypeParameterOrdinal => TypeParameterOrdinalSymbolKey.Resolve(this, out failureReason), + SymbolKeyType.Preprocessing => PreprocessingSymbolKey.Instance.Resolve(this, out failureReason), _ => throw new NotImplementedException(), }; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs index 9765db25cb617..e0f5301a3efe6 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs @@ -48,6 +48,7 @@ private enum SymbolKeyType Reference = '#', Null = '!', TypeParameterOrdinal = '@', + Preprocessing = '*', } private class SymbolKeyWriter : SymbolVisitor, IDisposable @@ -473,7 +474,7 @@ public override void VisitEvent(IEventSymbol eventSymbol) public override void VisitTypeParameter(ITypeParameterSymbol typeParameterSymbol) { // If it's a reference to a method type parameter, and we're currently writing - // out a signture, then only write out the ordinal of type parameter. This + // out a signature, then only write out the ordinal of type parameter. This // helps prevent recursion problems in cases like "Goo(T t). if (ShouldWriteTypeParameterOrdinal(typeParameterSymbol, out var methodIndex)) { @@ -487,6 +488,12 @@ public override void VisitTypeParameter(ITypeParameterSymbol typeParameterSymbol } } + public override void VisitPreprocessing(IPreprocessingSymbol preprocessingSymbol) + { + WriteType(SymbolKeyType.Preprocessing); + PreprocessingSymbolKey.Instance.Create(preprocessingSymbol, this); + } + public bool ShouldWriteTypeParameterOrdinal(ISymbol symbol, out int methodIndex) { if (symbol.Kind == SymbolKind.TypeParameter) From e91fc696c78345199bd839a1468f7d88ffe56c91 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 18 Jan 2023 23:07:29 +0200 Subject: [PATCH 012/508] Support multi-project reference finding --- .../PublicModel/PreprocessingSymbol.cs | 12 +++++++---- .../Portable/Symbols/PreprocessingSymbol.vb | 7 ++++++- ...indReferencesTests.PreprocessingSymbols.vb | 5 +++-- .../Finders/AbstractReferenceFinder.cs | 20 +++++++++++++++++++ .../PreprocessingSymbolReferenceFinder.cs | 17 ++++++---------- .../SymbolKey.PreprocessingSymbolKey.cs | 2 +- 6 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs index a16e438c71a80..ed63e34afcbe0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs @@ -40,15 +40,19 @@ public override bool Equals(object? obj) return true; } - if (ReferenceEquals(obj, null)) + if (obj is not IPreprocessingSymbol other) { return false; } - PreprocessingSymbol? other = obj as PreprocessingSymbol; + if (obj is PreprocessingSymbol csharpPreprocessingSymbol) + { + return _name == csharpPreprocessingSymbol._name; + } - return (object?)other != null && - this._name.Equals(other._name); + // If we do not encounter a C# preprocessing symbol, we still + // compare against the symbol's name directly. + return _name == other.Name; } bool IEquatable.Equals(ISymbol? other) diff --git a/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb index 62838c619ea81..f0d5c373dada8 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb @@ -130,7 +130,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Return False End If - Dim other As PreprocessingSymbol = TryCast(obj, PreprocessingSymbol) + ' If we're comparing against a C# preprocessing symbol, we still refer to the same + ' symbol name. If there exists a different C# preprocessing symbol with a different + ' capitalization variance, we also bind to that one. This is not a concern, as our + ' VB preprocessing symbols only apply within the same project, and we only support + ' this operation for finding all references of the given preprocessing symbol name. + Dim other As IPreprocessingSymbol = TryCast(obj, IPreprocessingSymbol) Return other IsNot Nothing AndAlso IdentifierComparison.Equals(Me.Name, other.Name) diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb index 5a5dc36a6886e..be6b5c9d5b116 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb @@ -180,8 +180,9 @@ class Class #Const [|PREPROCESSING_SYMBOL|] = True -#If DEBUG -#Else If NOT_PREPROCESSING_SYMBOL Or [|$$PREPROCESSING_SYMBOL|] +#If DEBUG Then +' Some code +#ElseIf NOT_PREPROCESSING_SYMBOL = [|$$PREPROCESSING_SYMBOL|] Then #End If diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs index 63a9421604210..7de379f603334 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs @@ -105,6 +105,26 @@ protected static async Task> FindDocumentsAsync( return documents.ToImmutable(); } + protected static async Task> FindAllSolutionDocumentsAsync( + Solution solution, + Func> predicateAsync, + T value, + CancellationToken cancellationToken) + { + using var _ = ArrayBuilder.GetInstance(out var documents); + + foreach (var project in solution.Projects) + { + foreach (var document in await project.GetAllRegularAndSourceGeneratedDocumentsAsync(cancellationToken).ConfigureAwait(false)) + { + if (await predicateAsync(document, value, cancellationToken).ConfigureAwait(false)) + documents.Add(document); + } + } + + return documents.ToImmutable(); + } + /// /// Finds all the documents in the provided project that contain the requested string /// values diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs index 710dabc3955a8..9e2927d361d3b 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs @@ -60,17 +60,12 @@ protected override async Task> DetermineDocumentsToSear FindReferencesSearchOptions options, CancellationToken cancellationToken) { - // NOTE: We intentionally search for multiple documents and consider multiple preprocessing - // symbols with the same name to be the same preprocessing symbol. This is because - // the symbols could either be defined in the compilation options, the project file - // or the document itself that includes the #define directive. In all of the above cases - // the preprocessing symbol is evaluated by name, and thus it's considered to be a global - // symbol that can be used based on whether it's currently defined in the given document - // and line. - // After all, writing any name for a preprocessing symbol, defined or not, is valid and will - // be computed during preprocessing evaluation of the tree - - return await FindDocumentsAsync(project, documents, HasDirectiveProbablyContainsIdentifier, symbol, cancellationToken) + // NOTE: We intentionally search for all documents in the entire solution. This is because + // the symbols are validly bound by their requested name, despite their current definition + // state. Therefore, the same symbol name could be shared across multiple projects and + // configured in the project configuration with the same shared identifier. + + return await FindAllSolutionDocumentsAsync(project.Solution, HasDirectiveProbablyContainsIdentifier, symbol, cancellationToken) .ConfigureAwait(false); static async ValueTask HasDirectiveProbablyContainsIdentifier(Document document, IPreprocessingSymbol symbol, CancellationToken ct) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs index a1603bfe4f442..883d67b14862d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs @@ -17,7 +17,7 @@ public sealed override void Create(IPreprocessingSymbol symbol, SymbolKeyWriter protected sealed override SymbolKeyResolution Resolve(SymbolKeyReader reader, IPreprocessingSymbol? contextualSymbol, out string? failureReason) { - var preprocessingName = reader.ReadString(); + var preprocessingName = reader.ReadRequiredString(); failureReason = null; var preprocessingSymbol = reader.Compilation.CreatePreprocessingSymbol(preprocessingName); return new(preprocessingSymbol); From d5d31e89ffb1d920b0e468bc3412052838873106 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 19 Jan 2023 01:17:32 +0200 Subject: [PATCH 013/508] Fix VB reference and add one more test --- .../Portable/Compilation/SemanticModel.vb | 2 +- ...indReferencesTests.PreprocessingSymbols.vb | 31 ++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb b/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb index 31a7fbfddbae5..d5d006ddce735 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb @@ -2924,7 +2924,7 @@ _Default: Dim parent = DirectCast(token.Parent, VisualBasicSyntaxNode) CheckSyntaxNode(parent) - If parent.Kind() = SyntaxKind.ConstKeyword Then + If parent.Kind() = SyntaxKind.ConstDirectiveTrivia Then Dim symbolInfo As VisualBasicPreprocessingSymbolInfo = token.SyntaxTree.GetPreprocessingSymbolInfo(token) Return RetrieveOrConstructPreprocessingSymbolInfo(symbolInfo, token) diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb index be6b5c9d5b116..93c43b6a90a4f 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb @@ -163,7 +163,36 @@ class Class - Public Async Function TestPreprocessingSymbolMultipleProjects2(kind As TestKind, host As TestHost) As Task + Public Async Function TestPreprocessingSymbolMultipleProjects2HoverCSharp(kind As TestKind, host As TestHost) As Task + Dim input = + + + +#define [|PREPROCESSING_SYMBOL|] + +#if [|PREPROCESSING_SYM$$BOL|] +#undef [|PREPROCESSING_SYMBOL|] +#endif + + + + +#Const [|PREPROCESSING_SYMBOL|] = True + + +#If DEBUG Then +' Some code +#ElseIf NOT_PREPROCESSING_SYMBOL = [|PREPROCESSING_SYMBOL|] Then +#End If + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + + Public Async Function TestPreprocessingSymbolMultipleProjects2HoverVB(kind As TestKind, host As TestHost) As Task Dim input = From bbeb89a9fa4b41818444fdc84189a4ed94261330 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 20 Jan 2023 21:55:11 +0200 Subject: [PATCH 014/508] Support VB preprocessing symbol at location --- .../Services/SemanticFacts/VisualBasicSemanticFacts.vb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb index 881527bfd575a..4eea97342120e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb @@ -85,6 +85,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic If VisualBasicSyntaxFacts.Instance.IsExecutableStatement(ancestor) Then Return Nothing End If + + If VisualBasicSyntaxFacts.Instance.IsIdentifierContainerPreprocessorDirectiveTrivia(ancestor) Then + Return semanticModel.Compilation.CreatePreprocessingSymbol(token.Text) + End If End If Next From cd3e91480f9cb0a952a654b23c5aca89a773aa16 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 20 Jan 2023 23:18:29 +0200 Subject: [PATCH 015/508] Detect pp symbols inside #define and #undef --- .../Compilation/CSharpSemanticModel.cs | 24 ++++++++++++++++ ...indReferencesTests.PreprocessingSymbols.vb | 28 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index ad09222984694..5a2f868ddd3f5 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -505,6 +505,26 @@ internal virtual IOperation GetOperationWorker(CSharpSyntaxNode node, Cancellati return this.GetSymbolInfoWorker(node, SymbolInfoOptions.DefaultOptions, cancellationToken); } + /// + /// Gets the SymbolInfo for the preprocessing symbol defined in a #define directive trivia, if any. + /// + public SymbolInfo GetSymbolInfo(DefineDirectiveTriviaSyntax node, CancellationToken cancellationToken = default(CancellationToken)) + { + CheckSyntaxNode(node); + var preprocessingSymbol = CreatePreprocessingSymbol(node.Name); + return new(preprocessingSymbol); + } + + /// + /// Gets the SymbolInfo for the preprocessing symbol defined in an #undef directive trivia, if any. + /// + public SymbolInfo GetSymbolInfo(UndefDirectiveTriviaSyntax node, CancellationToken cancellationToken = default(CancellationToken)) + { + CheckSyntaxNode(node); + var preprocessingSymbol = CreatePreprocessingSymbol(node.Name); + return new(preprocessingSymbol); + } + /// /// Returns what symbol(s), if any, the given expression syntax bound to in the program. /// @@ -4996,6 +5016,10 @@ private SymbolInfo GetSymbolInfoFromNode(SyntaxNode node, CancellationToken canc return this.GetSymbolInfo(orderingSyntax, cancellationToken); case PositionalPatternClauseSyntax ppcSyntax: return this.GetSymbolInfo(ppcSyntax, cancellationToken); + case DefineDirectiveTriviaSyntax defineSyntax: + return this.GetSymbolInfo(defineSyntax, cancellationToken); + case UndefDirectiveTriviaSyntax undefSyntax: + return this.GetSymbolInfo(undefSyntax, cancellationToken); } return SymbolInfo.None; diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb index 93c43b6a90a4f..30c3bcaa84828 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb @@ -239,6 +239,34 @@ class Class #endif + + Await TestAPIAndFeature(input, kind, host) + End Function + + + Public Async Function TestPreprocessingSymbolHoverDefine(kind As TestKind, host As TestHost) As Task + Dim input = + + + +#define [|PREPROC$$ESSING_SYMBOL|] +#undef [|PREPROCESSING_SYMBOL|] + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + Public Async Function TestPreprocessingSymbolHoverUndef(kind As TestKind, host As TestHost) As Task + Dim input = + + + +#define [|PREPROCESSING_SYMBOL|] +#undef [|PRE$$PROCESSING_SYMBOL|] + + Await TestAPIAndFeature(input, kind, host) End Function From 0801f428f8bb217fab91e0f7e687289c44b86bba Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 21 Jan 2023 11:37:16 +0200 Subject: [PATCH 016/508] Apply commented suggestion --- .../CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs index bdada0c604320..f854642776675 100644 --- a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs @@ -299,8 +299,7 @@ internal override IOperation GetOperationWorker(CSharpSyntaxNode node, Cancellat private static bool IsWithinIdentifierContainerDirectiveTrivia(CSharpSyntaxNode node) { return node is IdentifierNameSyntax - && node.FirstAncestorOrSelf(IsIdentifierContainerDirectiveTrivia) - is not null; + && node.FirstAncestorOrSelf(IsIdentifierContainerDirectiveTrivia) is not null; } private static bool IsIdentifierContainerDirectiveTrivia(CSharpSyntaxNode node) { From aa746c30293936af8136a8bd92121e3859c6d0ae Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 21 Jan 2023 11:53:38 +0200 Subject: [PATCH 017/508] Remove unused CancellationToken parameters --- .../CSharp/Portable/Compilation/CSharpSemanticModel.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index 5a2f868ddd3f5..0a7b0729d10a9 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -508,7 +508,7 @@ internal virtual IOperation GetOperationWorker(CSharpSyntaxNode node, Cancellati /// /// Gets the SymbolInfo for the preprocessing symbol defined in a #define directive trivia, if any. /// - public SymbolInfo GetSymbolInfo(DefineDirectiveTriviaSyntax node, CancellationToken cancellationToken = default(CancellationToken)) + public SymbolInfo GetSymbolInfo(DefineDirectiveTriviaSyntax node) { CheckSyntaxNode(node); var preprocessingSymbol = CreatePreprocessingSymbol(node.Name); @@ -518,7 +518,7 @@ internal virtual IOperation GetOperationWorker(CSharpSyntaxNode node, Cancellati /// /// Gets the SymbolInfo for the preprocessing symbol defined in an #undef directive trivia, if any. /// - public SymbolInfo GetSymbolInfo(UndefDirectiveTriviaSyntax node, CancellationToken cancellationToken = default(CancellationToken)) + public SymbolInfo GetSymbolInfo(UndefDirectiveTriviaSyntax node) { CheckSyntaxNode(node); var preprocessingSymbol = CreatePreprocessingSymbol(node.Name); @@ -5017,9 +5017,9 @@ private SymbolInfo GetSymbolInfoFromNode(SyntaxNode node, CancellationToken canc case PositionalPatternClauseSyntax ppcSyntax: return this.GetSymbolInfo(ppcSyntax, cancellationToken); case DefineDirectiveTriviaSyntax defineSyntax: - return this.GetSymbolInfo(defineSyntax, cancellationToken); + return this.GetSymbolInfo(defineSyntax); case UndefDirectiveTriviaSyntax undefSyntax: - return this.GetSymbolInfo(undefSyntax, cancellationToken); + return this.GetSymbolInfo(undefSyntax); } return SymbolInfo.None; From 000d8fe141b027b42875068ec44b4cd59a9836e4 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 21 Jan 2023 15:33:38 +0200 Subject: [PATCH 018/508] Align pp symbol info retrieval with VB --- .../Compilation/SyntaxTreeSemanticModel.cs | 22 ------------------- .../SemanticFacts/CSharpSemanticFacts.cs | 3 +++ 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs index f854642776675..216e885393fc7 100644 --- a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs @@ -273,18 +273,6 @@ internal override IOperation GetOperationWorker(CSharpSyntaxNode node, Cancellat int adjustedPosition = GetAdjustedNodePosition(crefSyntax); result = GetCrefSymbolInfo(adjustedPosition, crefSyntax, options, HasParameterList(crefSyntax)); } - else if (IsWithinIdentifierContainerDirectiveTrivia(node)) - { - var identifierName = node as IdentifierNameSyntax; - - result = SymbolInfo.None; - - var info = this.GetPreprocessingSymbolInfo(node); - if (info.Symbol is not null) - { - result = new SymbolInfo(info.Symbol); - } - } else { // if expression is not part of a member context then caller may really just have a @@ -296,16 +284,6 @@ internal override IOperation GetOperationWorker(CSharpSyntaxNode node, Cancellat return result; } - private static bool IsWithinIdentifierContainerDirectiveTrivia(CSharpSyntaxNode node) - { - return node is IdentifierNameSyntax - && node.FirstAncestorOrSelf(IsIdentifierContainerDirectiveTrivia) is not null; - } - private static bool IsIdentifierContainerDirectiveTrivia(CSharpSyntaxNode node) - { - return SyntaxFacts.IsIdentifierContainerDirectiveTrivia(node.Kind()); - } - internal override SymbolInfo GetCollectionInitializerSymbolInfoWorker(InitializerExpressionSyntax collectionInitializer, ExpressionSyntax node, CancellationToken cancellationToken = default(CancellationToken)) { var model = this.GetMemberModel(collectionInitializer); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs index 3bc03b112a423..0e6e804f66adc 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs @@ -83,6 +83,9 @@ public bool CanReplaceWithRValue(SemanticModel semanticModel, [NotNullWhen(true) // If we hit an executable statement syntax and didn't find anything yet, we can just stop now -- anything higher would be a member declaration which won't be defined by something inside a statement. if (CSharpSyntaxFacts.Instance.IsExecutableStatement(ancestor)) return null; + + if (CSharpSyntaxFacts.Instance.IsIdentifierContainerPreprocessorDirectiveTrivia(ancestor)) + return semanticModel.Compilation.CreatePreprocessingSymbol(token.ValueText); } return null; From f444dd3cb0bd1cf52b2b198cc527038c1a833228 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 21 Jan 2023 16:08:37 +0200 Subject: [PATCH 019/508] Add more VB tests --- ...indReferencesTests.PreprocessingSymbols.vb | 35 +++++++++++++++++++ .../PreprocessingSymbolReferenceFinder.cs | 21 ++++++++++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb index 30c3bcaa84828..2d19e5ab7b408 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb @@ -267,6 +267,41 @@ class Class #undef [|PRE$$PROCESSING_SYMBOL|] + + Await TestAPIAndFeature(input, kind, host) + End Function + + + Public Async Function TestPreprocessingSymbolHoverConst(kind As TestKind, host As TestHost) As Task + Dim input = + + + +#Const [|PREPROCESS$$ING_SYMBOL|] = True + +#If [|PREPROCESSING_SYMBOL|] Then +' Some code +#End If + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + Public Async Function TestPreprocessingSymbolHoverAssignedConst(kind As TestKind, host As TestHost) As Task + Dim input = + + + +#Const [|PREPROCESSING_SYMBOL|] = True +#Const OTHER = [|PREPROCES$$SING_SYMBOL|] + +#If [|PREPROCESSING_SYMBOL|] Then +' Some code +#End If + + Await TestAPIAndFeature(input, kind, host) End Function diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs index 9e2927d361d3b..aa91d75021c5b 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs @@ -45,7 +45,26 @@ static bool MatchesPreprocessingReference(SyntaxToken token, FindReferencesDocum if (parentKind == syntaxFacts.SyntaxKinds.UndefDirectiveTrivia) return true; - // Only inside an #if or #elif directive are preprocessing symbols used + // In VB, a #Const directive assigns a value that can also + // derive from preprocessing symbols + if (state.Document.Project.Language is LanguageNames.VisualBasic) + { + var extendedTokenParent = tokenParent; + while (true) + { + extendedTokenParent = extendedTokenParent.Parent; + + if (extendedTokenParent is null) + break; + + if (extendedTokenParent.RawKind == syntaxFacts.SyntaxKinds.DefineDirectiveTrivia) + { + return true; + } + } + } + + // Otherwise, only inside an #if or #elif directive are preprocessing symbols used return syntaxFacts.SpansIfOrElseIfPreprocessorDirective(tokenParent); } } From 0f0eb01e4ee93f99181c13ab3d955c90069f6db4 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 21 Jan 2023 23:50:39 +0200 Subject: [PATCH 020/508] Fix parameter name in public API --- src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt | 2 +- src/Compilers/VisualBasic/Portable/Syntax/SyntaxKindFacts.vb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt b/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt index 3120d98931458..a9bc1a8be4987 100644 --- a/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt @@ -1,2 +1,2 @@ Microsoft.CodeAnalysis.VisualBasicExtensions.ContainsDirective(node As Microsoft.CodeAnalysis.SyntaxNode, kind As Microsoft.CodeAnalysis.VisualBasic.SyntaxKind) -> Boolean -Shared Microsoft.CodeAnalysis.VisualBasic.SyntaxFacts.IsIdentifierContainerDirectiveTrivia(token As Microsoft.CodeAnalysis.VisualBasic.SyntaxKind) -> Boolean +Shared Microsoft.CodeAnalysis.VisualBasic.SyntaxFacts.IsIdentifierContainerDirectiveTrivia(kind As Microsoft.CodeAnalysis.VisualBasic.SyntaxKind) -> Boolean diff --git a/src/Compilers/VisualBasic/Portable/Syntax/SyntaxKindFacts.vb b/src/Compilers/VisualBasic/Portable/Syntax/SyntaxKindFacts.vb index 66fd0e6dd8b31..c0d9397229907 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/SyntaxKindFacts.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/SyntaxKindFacts.vb @@ -877,8 +877,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function - Public Shared Function IsIdentifierContainerDirectiveTrivia(token As SyntaxKind) As Boolean - Select Case token + Public Shared Function IsIdentifierContainerDirectiveTrivia(kind As SyntaxKind) As Boolean + Select Case kind Case SyntaxKind.IfDirectiveTrivia, SyntaxKind.ElseIfDirectiveTrivia, From b70ebdf9ab1500e5b2c309357aa13ea3b51d6bbf Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 27 Jan 2023 19:30:41 +0200 Subject: [PATCH 021/508] Redo support for pp symbols in finder Remove new visitors, specially handle the case of preprocessing symbols and add extra display name kinds --- .../PublicModel/PreprocessingSymbol.cs | 20 +++++++---- .../Core/Portable/PublicAPI.Unshipped.txt | 4 +-- .../SymbolDisplay/SymbolDisplayPartKind.cs | 6 ++-- .../Core/Portable/Symbols/SymbolVisitor.cs | 5 --- .../Core/Portable/Symbols/SymbolVisitor`1.cs | 5 --- .../Core/Portable/Symbols/SymbolVisitor`2.cs | 5 --- .../Portable/Symbols/PreprocessingSymbol.vb | 33 +++++++++++++++---- .../Portable/Symbols/SymbolVisitor.vb | 4 --- .../Portable/Symbols/SymbolVisitor`1.vb | 4 --- .../Portable/Symbols/SymbolVisitor`2.vb | 4 --- .../Common/SymbolDisplayPartKindTags.cs | 1 + .../Core/Portable/Common/TaggedText.cs | 3 ++ src/Features/Core/Portable/Common/TextTags.cs | 1 + .../Core/Portable/PublicAPI.Unshipped.txt | 2 +- .../Classification/ClassificationTypeNames.cs | 1 + .../Core/Portable/PublicAPI.Unshipped.txt | 1 + .../SymbolKey/SymbolKey.SymbolKeyWriter.cs | 11 +++---- 17 files changed, 58 insertions(+), 52 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs index ed63e34afcbe0..80739b118c054 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs @@ -75,17 +75,17 @@ bool ISymbol.Equals(ISymbol? other, CodeAnalysis.SymbolEqualityComparer equality void ISymbol.Accept(SymbolVisitor visitor) { - visitor.VisitPreprocessing(this); + throw new NotSupportedException(); } TResult ISymbol.Accept(SymbolVisitor visitor) { - return visitor.VisitPreprocessing(this)!; + throw new NotSupportedException(); } TResult ISymbol.Accept(SymbolVisitor visitor, TArgument argument) { - return visitor.VisitPreprocessing(this, argument); + throw new NotSupportedException(); } string? ISymbol.GetDocumentationCommentId() => null; @@ -94,22 +94,28 @@ TResult ISymbol.Accept(SymbolVisitor vis string ISymbol.ToDisplayString(SymbolDisplayFormat? format) { - return SymbolDisplay.ToDisplayString(this, format); + return _name; } ImmutableArray ISymbol.ToDisplayParts(SymbolDisplayFormat? format) { - return SymbolDisplay.ToDisplayParts(this, format); + return ToDisplayParts(); } string ISymbol.ToMinimalDisplayString(SemanticModel semanticModel, int position, SymbolDisplayFormat? format) { - return SymbolDisplay.ToMinimalDisplayString(this, Symbol.GetCSharpSemanticModel(semanticModel), position, format); + return _name; } ImmutableArray ISymbol.ToMinimalDisplayParts(SemanticModel semanticModel, int position, SymbolDisplayFormat? format) { - return SymbolDisplay.ToMinimalDisplayParts(this, Symbol.GetCSharpSemanticModel(semanticModel), position, format); + return ToDisplayParts(); + } + + private ImmutableArray ToDisplayParts() + { + var part = new SymbolDisplayPart(SymbolDisplayPartKind.PreprocessingName, this, _name); + return ImmutableArray.Create(part); } SymbolKind ISymbol.Kind => SymbolKind.Preprocessing; diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 344c210f21d42..0bc9ffd51145b 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -6,11 +6,9 @@ Microsoft.CodeAnalysis.Operations.IAttributeOperation Microsoft.CodeAnalysis.Operations.IAttributeOperation.Operation.get -> Microsoft.CodeAnalysis.IOperation! Microsoft.CodeAnalysis.SymbolDisplayLocalOptions.IncludeModifiers = 4 -> Microsoft.CodeAnalysis.SymbolDisplayLocalOptions Microsoft.CodeAnalysis.SymbolDisplayParameterOptions.IncludeModifiers = 2 -> Microsoft.CodeAnalysis.SymbolDisplayParameterOptions +Microsoft.CodeAnalysis.SymbolDisplayPartKind.PreprocessingName = 33 -> Microsoft.CodeAnalysis.SymbolDisplayPartKind Microsoft.CodeAnalysis.SyntaxNode.ContainsDirective(int rawKind) -> bool static Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph.Create(Microsoft.CodeAnalysis.Operations.IAttributeOperation! attribute, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! static Microsoft.CodeAnalysis.Location.Create(string! filePath, Microsoft.CodeAnalysis.Text.TextSpan textSpan, Microsoft.CodeAnalysis.Text.LinePositionSpan lineSpan, string! mappedFilePath, Microsoft.CodeAnalysis.Text.LinePositionSpan mappedLineSpan) -> Microsoft.CodeAnalysis.Location! virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitAttribute(Microsoft.CodeAnalysis.Operations.IAttributeOperation! operation) -> void virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitAttribute(Microsoft.CodeAnalysis.Operations.IAttributeOperation! operation, TArgument argument) -> TResult? -virtual Microsoft.CodeAnalysis.SymbolVisitor.VisitPreprocessing(Microsoft.CodeAnalysis.IPreprocessingSymbol! symbol) -> void -virtual Microsoft.CodeAnalysis.SymbolVisitor.VisitPreprocessing(Microsoft.CodeAnalysis.IPreprocessingSymbol! symbol, TArgument argument) -> TResult -virtual Microsoft.CodeAnalysis.SymbolVisitor.VisitPreprocessing(Microsoft.CodeAnalysis.IPreprocessingSymbol! symbol) -> TResult? diff --git a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayPartKind.cs b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayPartKind.cs index d82214664fce4..b64579895a159 100644 --- a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayPartKind.cs +++ b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayPartKind.cs @@ -89,11 +89,13 @@ public enum SymbolDisplayPartKind RecordClassName = 31, /// The name of a record struct. RecordStructName = 32, + /// The name of a preprocessing symbol. + PreprocessingName = 33, } internal static class InternalSymbolDisplayPartKind { - private const SymbolDisplayPartKind @base = SymbolDisplayPartKind.RecordStructName + 1; + private const SymbolDisplayPartKind @base = SymbolDisplayPartKind.PreprocessingName + 1; public const SymbolDisplayPartKind Arity = @base + 0; public const SymbolDisplayPartKind Other = @base + 1; } @@ -102,7 +104,7 @@ internal static partial class EnumBounds { internal static bool IsValid(this SymbolDisplayPartKind value) { - return (value >= SymbolDisplayPartKind.AliasName && value <= SymbolDisplayPartKind.RecordStructName) || + return (value >= SymbolDisplayPartKind.AliasName && value <= SymbolDisplayPartKind.PreprocessingName) || (value >= InternalSymbolDisplayPartKind.Arity && value <= InternalSymbolDisplayPartKind.Other); } } diff --git a/src/Compilers/Core/Portable/Symbols/SymbolVisitor.cs b/src/Compilers/Core/Portable/Symbols/SymbolVisitor.cs index 1783500633a8b..0516496a3a2f0 100644 --- a/src/Compilers/Core/Portable/Symbols/SymbolVisitor.cs +++ b/src/Compilers/Core/Portable/Symbols/SymbolVisitor.cs @@ -109,10 +109,5 @@ public virtual void VisitTypeParameter(ITypeParameterSymbol symbol) { DefaultVisit(symbol); } - - public virtual void VisitPreprocessing(IPreprocessingSymbol symbol) - { - DefaultVisit(symbol); - } } } diff --git a/src/Compilers/Core/Portable/Symbols/SymbolVisitor`1.cs b/src/Compilers/Core/Portable/Symbols/SymbolVisitor`1.cs index e0a2836ac4070..119e8be223cdf 100644 --- a/src/Compilers/Core/Portable/Symbols/SymbolVisitor`1.cs +++ b/src/Compilers/Core/Portable/Symbols/SymbolVisitor`1.cs @@ -112,10 +112,5 @@ public abstract class SymbolVisitor { return DefaultVisit(symbol); } - - public virtual TResult? VisitPreprocessing(IPreprocessingSymbol symbol) - { - return DefaultVisit(symbol); - } } } diff --git a/src/Compilers/Core/Portable/Symbols/SymbolVisitor`2.cs b/src/Compilers/Core/Portable/Symbols/SymbolVisitor`2.cs index 09cde048362d0..ea9d2eba5e8fd 100644 --- a/src/Compilers/Core/Portable/Symbols/SymbolVisitor`2.cs +++ b/src/Compilers/Core/Portable/Symbols/SymbolVisitor`2.cs @@ -117,10 +117,5 @@ public virtual TResult VisitTypeParameter(ITypeParameterSymbol symbol, TArgument { return DefaultVisit(symbol, argument); } - - public virtual TResult VisitPreprocessing(IPreprocessingSymbol symbol, TArgument argument) - { - return DefaultVisit(symbol, argument); - } } } diff --git a/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb index f0d5c373dada8..a6a5f6c967d68 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb @@ -146,27 +146,48 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Function Public Overloads Overrides Sub Accept(visitor As SymbolVisitor) - visitor.VisitPreprocessing(Me) + Throw New NotSupportedException() End Sub Public Overloads Overrides Sub Accept(visitor As VisualBasicSymbolVisitor) - visitor.VisitPreprocessing(Me) + Throw New NotSupportedException() End Sub Public Overloads Overrides Function Accept(Of TResult)(visitor As SymbolVisitor(Of TResult)) As TResult - Return visitor.VisitPreprocessing(Me) + Throw New NotSupportedException() End Function Public Overrides Function Accept(Of TArgument, TResult)(visitor As SymbolVisitor(Of TArgument, TResult), argument As TArgument) As TResult - Return visitor.VisitPreprocessing(Me, argument) + Throw New NotSupportedException() End Function Public Overloads Overrides Function Accept(Of TResult)(visitor As VisualBasicSymbolVisitor(Of TResult)) As TResult - Return visitor.VisitPreprocessing(Me) + Throw New NotSupportedException() End Function Friend Overloads Overrides Function Accept(Of TArgument, TResult)(visitor As VisualBasicSymbolVisitor(Of TArgument, TResult), arg As TArgument) As TResult - Return visitor.VisitPreprocessing(Me, arg) + Throw New NotSupportedException() + End Function + + Private Function ISymbol_ToDisplayString(Optional format As SymbolDisplayFormat = Nothing) As String Implements ISymbol.ToDisplayString + Return Name + End Function + + Private Function ISymbol_ToDisplayParts(Optional format As SymbolDisplayFormat = Nothing) As ImmutableArray(Of SymbolDisplayPart) Implements ISymbol.ToDisplayParts + Return ToDisplayParts() + End Function + + Private Function ISymbol_ToMinimalDisplayString(semanticModel As SemanticModel, position As Integer, Optional format As SymbolDisplayFormat = Nothing) As String Implements ISymbol.ToMinimalDisplayString + Return Name + End Function + + Private Function ISymbol_ToMinimalDisplayParts(semanticModel As SemanticModel, position As Integer, Optional format As SymbolDisplayFormat = Nothing) As ImmutableArray(Of SymbolDisplayPart) Implements ISymbol.ToMinimalDisplayParts + Return ToDisplayParts() + End Function + + Private Shadows Function ToDisplayParts() As ImmutableArray(Of SymbolDisplayPart) + Dim part = New SymbolDisplayPart(SymbolDisplayPartKind.PreprocessingName, Me, Name) + Return ImmutableArray.Create(part) End Function End Class diff --git a/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor.vb b/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor.vb index 9ed9bb5f958f0..6e38667c80374 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor.vb @@ -76,9 +76,5 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Public Overridable Sub VisitTypeParameter(symbol As TypeParameterSymbol) DefaultVisit(symbol) End Sub - - Public Overridable Sub VisitPreprocessing(symbol As PreprocessingSymbol) - DefaultVisit(symbol) - End Sub End Class End Namespace diff --git a/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor`1.vb b/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor`1.vb index 380768ba4f95f..8cf9f2417b560 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor`1.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor`1.vb @@ -75,9 +75,5 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Public Overridable Function VisitTypeParameter(symbol As TypeParameterSymbol) As TResult Return DefaultVisit(symbol) End Function - - Public Overridable Function VisitPreprocessing(symbol As PreprocessingSymbol) As TResult - Return DefaultVisit(symbol) - End Function End Class End Namespace diff --git a/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor`2.vb b/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor`2.vb index 073a5f51920d8..b9e7c75dc800a 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor`2.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor`2.vb @@ -94,9 +94,5 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Public Overridable Function VisitEvent(symbol As EventSymbol, arg As TArgument) As TResult Return DefaultVisit(symbol, arg) End Function - - Public Overridable Function VisitPreprocessing(symbol As PreprocessingSymbol, arg As TArgument) As TResult - Return DefaultVisit(symbol, arg) - End Function End Class End Namespace diff --git a/src/Features/Core/Portable/Common/SymbolDisplayPartKindTags.cs b/src/Features/Core/Portable/Common/SymbolDisplayPartKindTags.cs index 410a0b7fb928f..8f3b7e6b1a06f 100644 --- a/src/Features/Core/Portable/Common/SymbolDisplayPartKindTags.cs +++ b/src/Features/Core/Portable/Common/SymbolDisplayPartKindTags.cs @@ -44,6 +44,7 @@ public static string GetTag(SymbolDisplayPartKind kind) SymbolDisplayPartKind.ConstantName => TextTags.Constant, SymbolDisplayPartKind.RecordClassName => TextTags.Record, SymbolDisplayPartKind.RecordStructName => TextTags.RecordStruct, + SymbolDisplayPartKind.PreprocessingName => TextTags.Preprocessing, _ => string.Empty, }; } diff --git a/src/Features/Core/Portable/Common/TaggedText.cs b/src/Features/Core/Portable/Common/TaggedText.cs index 08ea0a8950396..ea83585d65eb0 100644 --- a/src/Features/Core/Portable/Common/TaggedText.cs +++ b/src/Features/Core/Portable/Common/TaggedText.cs @@ -224,6 +224,9 @@ public static string ToClassificationTypeName(this string taggedTextTag) case TextTags.RecordStruct: return ClassificationTypeNames.RecordStructName; + case TextTags.Preprocessing: + return ClassificationTypeNames.PreprocessingName; + case TextTags.ContainerStart: case TextTags.ContainerEnd: case TextTags.CodeBlockStart: diff --git a/src/Features/Core/Portable/Common/TextTags.cs b/src/Features/Core/Portable/Common/TextTags.cs index ca2f1517116dc..dcb5d6bed1c62 100644 --- a/src/Features/Core/Portable/Common/TextTags.cs +++ b/src/Features/Core/Portable/Common/TextTags.cs @@ -45,6 +45,7 @@ public static class TextTags public const string Constant = nameof(Constant); public const string Record = nameof(Record); public const string RecordStruct = nameof(RecordStruct); + public const string Preprocessing = nameof(Preprocessing); /// /// Indicates the start of a text container. The elements after through (but not diff --git a/src/Features/Core/Portable/PublicAPI.Unshipped.txt b/src/Features/Core/Portable/PublicAPI.Unshipped.txt index 8b137891791fe..2422b21713349 100644 --- a/src/Features/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Features/Core/Portable/PublicAPI.Unshipped.txt @@ -1 +1 @@ - +const Microsoft.CodeAnalysis.TextTags.Preprocessing = "Preprocessing" -> string diff --git a/src/Workspaces/Core/Portable/Classification/ClassificationTypeNames.cs b/src/Workspaces/Core/Portable/Classification/ClassificationTypeNames.cs index 4db2569c172db..4d78f9de2b0ab 100644 --- a/src/Workspaces/Core/Portable/Classification/ClassificationTypeNames.cs +++ b/src/Workspaces/Core/Portable/Classification/ClassificationTypeNames.cs @@ -123,6 +123,7 @@ public static class ClassificationTypeNames public const string ModuleName = "module name"; public const string StructName = "struct name"; public const string RecordStructName = "record struct name"; + public const string PreprocessingName = "preprocessing name"; public const string TypeParameterName = "type parameter name"; public const string FieldName = "field name"; diff --git a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt index 713fd389ad167..06b3807281283 100644 --- a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt @@ -1,4 +1,5 @@ *REMOVED*abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.ParameterDeclaration(string name, Microsoft.CodeAnalysis.SyntaxNode type = null, Microsoft.CodeAnalysis.SyntaxNode initializer = null, Microsoft.CodeAnalysis.RefKind refKind = Microsoft.CodeAnalysis.RefKind.None) -> Microsoft.CodeAnalysis.SyntaxNode +const Microsoft.CodeAnalysis.Classification.ClassificationTypeNames.PreprocessingName = "preprocessing name" -> string Microsoft.CodeAnalysis.CodeFixes.CodeFixContext.CodeFixContext(Microsoft.CodeAnalysis.TextDocument document, Microsoft.CodeAnalysis.Diagnostic diagnostic, System.Action> registerCodeFix, System.Threading.CancellationToken cancellationToken) -> void Microsoft.CodeAnalysis.CodeFixes.CodeFixContext.CodeFixContext(Microsoft.CodeAnalysis.TextDocument document, Microsoft.CodeAnalysis.Text.TextSpan span, System.Collections.Immutable.ImmutableArray diagnostics, System.Action> registerCodeFix, System.Threading.CancellationToken cancellationToken) -> void Microsoft.CodeAnalysis.CodeFixes.CodeFixContext.TextDocument.get -> Microsoft.CodeAnalysis.TextDocument diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs index e0f5301a3efe6..2181ebdecdf1b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs @@ -159,6 +159,11 @@ internal void WriteSymbolKey(ISymbol? symbol) WriteType(SymbolKeyType.BodyLevel); BodyLevelSymbolKey.Create(symbol, this); } + else if (symbol is IPreprocessingSymbol preprocessingSymbol) + { + WriteType(SymbolKeyType.Preprocessing); + PreprocessingSymbolKey.Instance.Create(preprocessingSymbol, this); + } else { symbol.Accept(this); @@ -488,12 +493,6 @@ public override void VisitTypeParameter(ITypeParameterSymbol typeParameterSymbol } } - public override void VisitPreprocessing(IPreprocessingSymbol preprocessingSymbol) - { - WriteType(SymbolKeyType.Preprocessing); - PreprocessingSymbolKey.Instance.Create(preprocessingSymbol, this); - } - public bool ShouldWriteTypeParameterOrdinal(ISymbol symbol, out int methodIndex) { if (symbol.Kind == SymbolKind.TypeParameter) From 887eaac2eb003454fd7327ad3666853e81f7514e Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 27 Jan 2023 22:14:29 +0200 Subject: [PATCH 022/508] Apply code suggestions and revert changes --- .../Compilation/CSharpSemanticModel.cs | 58 ++++++++----------- .../PublicModel/PreprocessingSymbol.cs | 22 +------ .../Portable/Compilation/SemanticModel.cs | 16 ----- .../Core/Portable/PublicAPI.Unshipped.txt | 2 - .../Portable/Compilation/SemanticModel.vb | 19 ++++-- .../FindReferences/FindReferenceCache.cs | 5 -- .../Finders/AbstractReferenceFinder.cs | 4 +- .../PreprocessingSymbolReferenceFinder.cs | 4 +- .../Services/SyntaxFacts/CSharpSyntaxKinds.cs | 2 +- .../SyntaxFacts/ISyntaxFactsExtensions.cs | 2 +- .../Core/Services/SyntaxFacts/ISyntaxKinds.cs | 2 +- .../SymbolKey.PreprocessingSymbolKey.cs | 2 +- .../SymbolKey/SymbolKey.SymbolKeyWriter.cs | 2 +- .../SyntaxFacts/VisualBasicSyntaxKinds.vb | 2 +- 14 files changed, 53 insertions(+), 89 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index 0a7b0729d10a9..f68405de365cd 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -975,13 +975,6 @@ public SymbolInfo GetSpeculativeSymbolInfo(int position, CrefSyntax cref, Symbol { return CSharpTypeInfo.None; } - else if (IsInStructuredTriviaNotContainingIdentifiers(expression, out var decisiveParentSyntaxKind)) - { - if (SyntaxFacts.IsIdentifierContainerDirectiveTrivia(decisiveParentSyntaxKind)) - { - return CSharpTypeInfo.None; - } - } else if (SyntaxFacts.IsDeclarationExpressionType(expression, out DeclarationExpressionSyntax parent)) { switch (parent.Designation.Kind()) @@ -4857,24 +4850,6 @@ private ImmutableArray CreateReducedExtensionMethodIfPossible(BoundDeleg /// The node. public abstract AwaitExpressionInfo GetAwaitExpressionInfo(AwaitExpressionSyntax node); - /// - /// If the given token is within a preprocessing directive, gets the preprocessing symbol info for it. - /// Define and undefine directive trivia parents are supported too. - /// - /// Preprocessing symbol syntax token. - public new PreprocessingSymbolInfo GetPreprocessingSymbolInfo(SyntaxToken token) - { - var parent = token.Parent as CSharpSyntaxNode; - CheckSyntaxNode(parent); - - if (parent.Kind() is SyntaxKind.DefineDirectiveTrivia or SyntaxKind.UndefDirectiveTrivia) - return CreatePreprocessingSymbolInfo(token); - - if (parent is IdentifierNameSyntax identifier) - return GetPreprocessingSymbolInfo(identifier); - - return PreprocessingSymbolInfo.None; - } /// /// If the given node is within a preprocessing directive, gets the preprocessing symbol info for it. /// @@ -4890,6 +4865,24 @@ public PreprocessingSymbolInfo GetPreprocessingSymbolInfo(IdentifierNameSyntax n return PreprocessingSymbolInfo.None; } + /// + /// Gets the preprocessing symbol info for the preprocessing symbol defined in the #define directive. + /// + /// A #define directive trivia node. + public PreprocessingSymbolInfo GetPreprocessingSymbolInfo(DefineDirectiveTriviaSyntax node) + { + CheckSyntaxNode(node); + return CreatePreprocessingSymbolInfo(node.Name); + } + /// + /// Gets the preprocessing symbol info for the preprocessing symbol undefined in the #undef directive. + /// + /// An #undef directive trivia node. + public PreprocessingSymbolInfo GetPreprocessingSymbolInfo(UndefDirectiveTriviaSyntax node) + { + CheckSyntaxNode(node); + return CreatePreprocessingSymbolInfo(node.Name); + } private PreprocessingSymbolInfo CreatePreprocessingSymbolInfo(in SyntaxToken identifier) { @@ -5117,16 +5110,15 @@ protected sealed override IAliasSymbol GetAliasInfoCore(SyntaxNode node, Cancell return node is IdentifierNameSyntax nameSyntax ? GetAliasInfo(nameSyntax, cancellationToken) : null; } - protected sealed override PreprocessingSymbolInfo GetPreprocessingSymbolInfoCore(SyntaxToken token) - { - return GetPreprocessingSymbolInfo(token); - } - protected sealed override PreprocessingSymbolInfo GetPreprocessingSymbolInfoCore(SyntaxNode node) { - return node is IdentifierNameSyntax nameSyntax - ? GetPreprocessingSymbolInfo(nameSyntax) - : PreprocessingSymbolInfo.None; + return node switch + { + IdentifierNameSyntax nameSyntax => GetPreprocessingSymbolInfo(nameSyntax), + DefineDirectiveTriviaSyntax defineSyntax => GetPreprocessingSymbolInfo(defineSyntax), + UndefDirectiveTriviaSyntax undefSyntax => GetPreprocessingSymbolInfo(undefSyntax), + _ => PreprocessingSymbolInfo.None + }; } protected sealed override ISymbol GetDeclaredSymbolCore(SyntaxNode node, CancellationToken cancellationToken) diff --git a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs index 80739b118c054..76e2701cf691f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs @@ -45,13 +45,6 @@ public override bool Equals(object? obj) return false; } - if (obj is PreprocessingSymbol csharpPreprocessingSymbol) - { - return _name == csharpPreprocessingSymbol._name; - } - - // If we do not encounter a C# preprocessing symbol, we still - // compare against the symbol's name directly. return _name == other.Name; } @@ -73,20 +66,11 @@ bool ISymbol.Equals(ISymbol? other, CodeAnalysis.SymbolEqualityComparer equality Accessibility ISymbol.DeclaredAccessibility => Accessibility.NotApplicable; - void ISymbol.Accept(SymbolVisitor visitor) - { - throw new NotSupportedException(); - } + void ISymbol.Accept(SymbolVisitor visitor) => throw new NotSupportedException(); - TResult ISymbol.Accept(SymbolVisitor visitor) - { - throw new NotSupportedException(); - } + TResult ISymbol.Accept(SymbolVisitor visitor) => throw new NotSupportedException(); - TResult ISymbol.Accept(SymbolVisitor visitor, TArgument argument) - { - throw new NotSupportedException(); - } + TResult ISymbol.Accept(SymbolVisitor visitor, TArgument argument) => throw new NotSupportedException(); string? ISymbol.GetDocumentationCommentId() => null; diff --git a/src/Compilers/Core/Portable/Compilation/SemanticModel.cs b/src/Compilers/Core/Portable/Compilation/SemanticModel.cs index d3aa79b9d0939..45ecd74cdac89 100644 --- a/src/Compilers/Core/Portable/Compilation/SemanticModel.cs +++ b/src/Compilers/Core/Portable/Compilation/SemanticModel.cs @@ -841,15 +841,6 @@ public bool IsEventUsableAsField(int position, IEventSymbol eventSymbol) /// protected abstract bool IsEventUsableAsFieldCore(int position, IEventSymbol eventSymbol); - /// - /// If is an identifier token for a preprocessor symbol, return the corresponding - /// to it. - /// - /// The syntax token to get semantic information for. - public PreprocessingSymbolInfo GetPreprocessingSymbolInfo(SyntaxToken token) - { - return GetPreprocessingSymbolInfoCore(token); - } /// /// If is an identifier name syntax node, return the corresponding /// to it. @@ -860,13 +851,6 @@ public PreprocessingSymbolInfo GetPreprocessingSymbolInfo(SyntaxNode nameSyntax) return GetPreprocessingSymbolInfoCore(nameSyntax); } - /// - /// If is an identifier token for a preprocessor symbol, return the corresponding - /// to it. - /// - /// The syntax token to get semantic information for. - protected abstract PreprocessingSymbolInfo GetPreprocessingSymbolInfoCore(SyntaxToken token); - /// /// If is an identifier name syntax node, return the corresponding /// to it. diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 0bc9ffd51145b..0aa146ab6becd 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -1,6 +1,4 @@ -abstract Microsoft.CodeAnalysis.SemanticModel.GetPreprocessingSymbolInfoCore(Microsoft.CodeAnalysis.SyntaxToken token) -> Microsoft.CodeAnalysis.PreprocessingSymbolInfo Microsoft.CodeAnalysis.Compilation.CreatePreprocessingSymbol(string! name) -> Microsoft.CodeAnalysis.IPreprocessingSymbol! -Microsoft.CodeAnalysis.SemanticModel.GetPreprocessingSymbolInfo(Microsoft.CodeAnalysis.SyntaxToken token) -> Microsoft.CodeAnalysis.PreprocessingSymbolInfo Microsoft.CodeAnalysis.OperationKind.Attribute = 125 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.Operations.IAttributeOperation Microsoft.CodeAnalysis.Operations.IAttributeOperation.Operation.get -> Microsoft.CodeAnalysis.IOperation! diff --git a/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb b/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb index d5d006ddce735..d2f4478312860 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb @@ -2954,6 +2954,16 @@ _Default: Return VisualBasicPreprocessingSymbolInfo.None End Function + ''' + ''' Gets the preprocessing symbol info for the preprocessing symbol defined in the #Const directive. + ''' + ''' A #Const directive trivia node. + Public Shadows Function GetPreprocessingSymbolInfo(node As ConstDirectiveTriviaSyntax) As VisualBasicPreprocessingSymbolInfo + CheckSyntaxNode(node) + Dim symbolInfo As VisualBasicPreprocessingSymbolInfo = node.SyntaxTree.GetPreprocessingSymbolInfo(node.Name) + Return RetrieveOrConstructPreprocessingSymbolInfo(symbolInfo, node.Name) + End Function + Private Function RetrieveOrConstructPreprocessingSymbolInfo(symbolInfo As VisualBasicPreprocessingSymbolInfo, token As SyntaxToken) As VisualBasicPreprocessingSymbolInfo If symbolInfo.Symbol IsNot Nothing Then @@ -3225,16 +3235,17 @@ _Default: Return Nothing End Function - Protected NotOverridable Overrides Function GetPreprocessingSymbolInfoCore(token As SyntaxToken) As PreprocessingSymbolInfo - Return GetPreprocessingSymbolInfo(token) - End Function - Protected NotOverridable Overrides Function GetPreprocessingSymbolInfoCore(node As SyntaxNode) As PreprocessingSymbolInfo Dim nameSyntax = TryCast(node, IdentifierNameSyntax) If nameSyntax IsNot Nothing Then Return GetPreprocessingSymbolInfo(nameSyntax) End If + Dim constSyntax = TryCast(node, ConstDirectiveTriviaSyntax) + If constSyntax IsNot Nothing Then + Return GetPreprocessingSymbolInfo(constSyntax) + End If + Return Nothing End Function diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferenceCache.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferenceCache.cs index 9f2954d283bcf..9079eb03214d5 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferenceCache.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferenceCache.cs @@ -50,11 +50,6 @@ public SymbolInfo GetSymbolInfo(SyntaxNode node, CancellationToken cancellationT return _symbolInfoCache.GetOrAdd(node, static (n, arg) => arg._semanticModel.GetSymbolInfo(n, arg.cancellationToken), (_semanticModel, cancellationToken)); } - public PreprocessingSymbolInfo GetPreprocessingSymbolInfo(SyntaxToken token) - { - return _semanticModel.GetPreprocessingSymbolInfo(token); - } - public IAliasSymbol? GetAliasInfo( ISemanticFactsService semanticFacts, SyntaxToken token, CancellationToken cancellationToken) { diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs index 7de379f603334..cba90a6292c79 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs @@ -36,7 +36,7 @@ public abstract Task> DetermineDocumentsToSearchAsync( public abstract ValueTask> FindReferencesInDocumentAsync( ISymbol symbol, FindReferencesDocumentState state, FindReferencesSearchOptions options, CancellationToken cancellationToken); - private static async ValueTask<(bool matched, CandidateReason reason)> SymbolsMatchAsync( + private static ValueTask<(bool matched, CandidateReason reason)> SymbolsMatchAsync( ISymbol symbol, FindReferencesDocumentState state, SyntaxToken token, CancellationToken cancellationToken) { // delegates don't have exposed symbols for their constructors. so when you do `new MyDel()`, that's only a @@ -47,7 +47,7 @@ public abstract ValueTask> FindReferencesInDocume : state.SyntaxFacts.TryGetBindableParent(token); parent ??= token.Parent!; - return await SymbolsMatchAsync(symbol, state, parent, cancellationToken).ConfigureAwait(false); + return SymbolsMatchAsync(symbol, state, parent, cancellationToken); } protected static async ValueTask<(bool matched, CandidateReason reason)> SymbolsMatchAsync( diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs index aa91d75021c5b..5baa8d7d5579f 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs @@ -132,8 +132,8 @@ private static async ValueTask PreprocessingSymbolMatchesAsync(ISymbol sym private static async ValueTask PreprocessingSymbolsMatchAsync( IPreprocessingSymbol searchSymbol, FindReferencesDocumentState state, SyntaxToken token, CancellationToken cancellationToken) { - var symbolInfo = state.Cache.GetPreprocessingSymbolInfo(token); - return await SymbolFinder.OriginalSymbolsMatchAsync(state.Solution, searchSymbol, symbolInfo.Symbol, cancellationToken).ConfigureAwait(false); + var symbol = state.SemanticModel.Compilation.CreatePreprocessingSymbol(token.ValueText); + return await SymbolFinder.OriginalSymbolsMatchAsync(state.Solution, searchSymbol, symbol, cancellationToken).ConfigureAwait(false); } private static FinderLocation CreateFinderLocation(FindReferencesDocumentState state, SyntaxToken token, CancellationToken cancellationToken) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs index b4bbbb66fb2e5..85ef5cad238f6 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs @@ -29,7 +29,7 @@ public TSyntaxKind Convert(int kind) where TSyntaxKind : struct public int? MultiLineDocCommentTrivia => (int)SyntaxKind.MultiLineDocumentationCommentTrivia; public int? ShebangDirectiveTrivia => (int)SyntaxKind.ShebangDirectiveTrivia; public int IfDirectiveTrivia => (int)SyntaxKind.IfDirectiveTrivia; - public int ElseIfDirectiveTrivia => (int)SyntaxKind.ElifDirectiveTrivia; + public int ElifDirectiveTrivia => (int)SyntaxKind.ElifDirectiveTrivia; public int DefineDirectiveTrivia => (int)SyntaxKind.DefineDirectiveTrivia; public int? UndefDirectiveTrivia => (int)SyntaxKind.UndefDirectiveTrivia; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs index 79e82a49ad61b..173f243869494 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs @@ -258,7 +258,7 @@ public static bool SpansIfOrElseIfPreprocessorDirective(this ISyntaxFacts syntax var kinds = syntaxFacts.SyntaxKinds; var ifDirectiveKind = kinds.IfDirectiveTrivia; - var elseIfDirectiveKind = kinds.ElseIfDirectiveTrivia; + var elseIfDirectiveKind = kinds.ElifDirectiveTrivia; while (node is not null) { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs index ac760a0f84c5c..c76ec9c08b964 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs @@ -35,7 +35,7 @@ internal interface ISyntaxKinds int? ShebangDirectiveTrivia { get; } int IfDirectiveTrivia { get; } - int ElseIfDirectiveTrivia { get; } + int ElifDirectiveTrivia { get; } int DefineDirectiveTrivia { get; } int? UndefDirectiveTrivia { get; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs index 883d67b14862d..ed92e68016f6f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs @@ -17,8 +17,8 @@ public sealed override void Create(IPreprocessingSymbol symbol, SymbolKeyWriter protected sealed override SymbolKeyResolution Resolve(SymbolKeyReader reader, IPreprocessingSymbol? contextualSymbol, out string? failureReason) { - var preprocessingName = reader.ReadRequiredString(); failureReason = null; + var preprocessingName = reader.ReadRequiredString(); var preprocessingSymbol = reader.Compilation.CreatePreprocessingSymbol(preprocessingName); return new(preprocessingSymbol); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs index 2181ebdecdf1b..b1da38eb27675 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs @@ -27,6 +27,7 @@ private enum SymbolKeyType Field = 'F', FunctionPointer = 'G', DynamicType = 'I', + Preprocessing = 'J', BuiltinOperator = 'L', Method = 'M', Namespace = 'N', @@ -48,7 +49,6 @@ private enum SymbolKeyType Reference = '#', Null = '!', TypeParameterOrdinal = '@', - Preprocessing = '*', } private class SymbolKeyWriter : SymbolVisitor, IDisposable diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb index 6da98bcc4a914..ac8eee80e42fa 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb @@ -31,7 +31,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Public ReadOnly Property ShebangDirectiveTrivia As Integer? Implements ISyntaxKinds.ShebangDirectiveTrivia Public ReadOnly Property IfDirectiveTrivia As Integer = SyntaxKind.IfDirectiveTrivia Implements ISyntaxKinds.IfDirectiveTrivia - Public ReadOnly Property ElseIfDirectiveTrivia As Integer = SyntaxKind.ElseIfDirectiveTrivia Implements ISyntaxKinds.ElseIfDirectiveTrivia + Public ReadOnly Property ElifDirectiveTrivia As Integer = SyntaxKind.ElseIfDirectiveTrivia Implements ISyntaxKinds.ElifDirectiveTrivia Public ReadOnly Property DefineDirectiveTrivia As Integer = SyntaxKind.ConstDirectiveTrivia Implements ISyntaxKinds.DefineDirectiveTrivia Public ReadOnly Property UndefDirectiveTrivia As Integer? Implements ISyntaxKinds.UndefDirectiveTrivia From 37ebad0f6fe4329466152095f9a9722f176f7446 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 28 Jan 2023 00:16:41 +0200 Subject: [PATCH 023/508] Simplify methods and implementations --- .../Compilation/CSharpSemanticModel.cs | 22 ++++--------------- .../CSharp/Portable/PublicAPI.Unshipped.txt | 1 - .../CSharp/Portable/Syntax/SyntaxKindFacts.cs | 9 -------- .../Portable/PublicAPI.Unshipped.txt | 1 - .../Portable/Syntax/SyntaxKindFacts.vb | 14 ------------ .../PreprocessingSymbolReferenceFinder.cs | 6 ++--- .../SemanticFacts/CSharpSemanticFacts.cs | 10 +++++++-- .../Services/SyntaxFacts/CSharpSyntaxFacts.cs | 3 --- .../Core/Services/SyntaxFacts/ISyntaxFacts.cs | 1 - .../SemanticFacts/VisualBasicSemanticFacts.vb | 9 +++++--- .../SyntaxFacts/VisualBasicSyntaxFacts.vb | 4 ---- 11 files changed, 21 insertions(+), 59 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index f68405de365cd..d18d963fcc424 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -60,7 +60,7 @@ internal static bool CanGetSemanticInfo(CSharpSyntaxNode node, bool allowNamedAr { Debug.Assert(node != null); - if (!isSpeculative && IsInStructuredTriviaNotContainingIdentifiers(node)) + if (!isSpeculative && IsInStructuredTriviaOtherThanCrefOrNameAttribute(node)) { return false; } @@ -1253,29 +1253,15 @@ internal bool IsInTree(SyntaxNode node) return node.SyntaxTree == this.SyntaxTree; } - private static bool IsInStructuredTriviaNotContainingIdentifiers(CSharpSyntaxNode node) + private static bool IsInStructuredTriviaOtherThanCrefOrNameAttribute(CSharpSyntaxNode node) { - return IsInStructuredTriviaNotContainingIdentifiers(node, out _); - } - private static bool IsInStructuredTriviaNotContainingIdentifiers(CSharpSyntaxNode node, out SyntaxKind decisiveParentSyntaxKind) - { - decisiveParentSyntaxKind = default; while (node != null) { - decisiveParentSyntaxKind = node.Kind(); - switch (decisiveParentSyntaxKind) - { - case SyntaxKind.XmlCrefAttribute: - case SyntaxKind.XmlNameAttribute: - return false; - } - - if (SyntaxFacts.IsIdentifierContainerDirectiveTrivia(decisiveParentSyntaxKind)) + if (node.Kind() == SyntaxKind.XmlCrefAttribute || node.Kind() == SyntaxKind.XmlNameAttribute) { return false; } - - if (node.IsStructuredTrivia) + else if (node.IsStructuredTrivia) { return true; } diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index ebec6ff8a28cf..00644bd7fc75b 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -1,2 +1 @@ static Microsoft.CodeAnalysis.CSharpExtensions.ContainsDirective(this Microsoft.CodeAnalysis.SyntaxNode! node, Microsoft.CodeAnalysis.CSharp.SyntaxKind kind) -> bool -static Microsoft.CodeAnalysis.CSharp.SyntaxFacts.IsIdentifierContainerDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.SyntaxKind kind) -> bool diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs index 498ca0631c7e4..d6624d597b04b 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs @@ -1779,14 +1779,5 @@ public static bool IsDocumentationCommentTrivia(SyntaxKind kind) return kind == SyntaxKind.SingleLineDocumentationCommentTrivia || kind == SyntaxKind.MultiLineDocumentationCommentTrivia; } - - public static bool IsIdentifierContainerDirectiveTrivia(SyntaxKind kind) - { - return kind - is SyntaxKind.IfDirectiveTrivia - or SyntaxKind.ElifDirectiveTrivia - or SyntaxKind.DefineDirectiveTrivia - or SyntaxKind.UndefDirectiveTrivia; - } } } diff --git a/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt b/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt index a9bc1a8be4987..f058fa0ebc587 100644 --- a/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt @@ -1,2 +1 @@ Microsoft.CodeAnalysis.VisualBasicExtensions.ContainsDirective(node As Microsoft.CodeAnalysis.SyntaxNode, kind As Microsoft.CodeAnalysis.VisualBasic.SyntaxKind) -> Boolean -Shared Microsoft.CodeAnalysis.VisualBasic.SyntaxFacts.IsIdentifierContainerDirectiveTrivia(kind As Microsoft.CodeAnalysis.VisualBasic.SyntaxKind) -> Boolean diff --git a/src/Compilers/VisualBasic/Portable/Syntax/SyntaxKindFacts.vb b/src/Compilers/VisualBasic/Portable/Syntax/SyntaxKindFacts.vb index c0d9397229907..c0a4c30eae930 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/SyntaxKindFacts.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/SyntaxKindFacts.vb @@ -877,20 +877,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function - Public Shared Function IsIdentifierContainerDirectiveTrivia(kind As SyntaxKind) As Boolean - Select Case kind - - Case SyntaxKind.IfDirectiveTrivia, - SyntaxKind.ElseIfDirectiveTrivia, - SyntaxKind.ConstDirectiveTrivia - Return True - - Case Else - Return False - End Select - - End Function - End Class End Namespace diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs index 5baa8d7d5579f..625828dc13161 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs @@ -87,13 +87,13 @@ protected override async Task> DetermineDocumentsToSear return await FindAllSolutionDocumentsAsync(project.Solution, HasDirectiveProbablyContainsIdentifier, symbol, cancellationToken) .ConfigureAwait(false); - static async ValueTask HasDirectiveProbablyContainsIdentifier(Document document, IPreprocessingSymbol symbol, CancellationToken ct) + static async ValueTask HasDirectiveProbablyContainsIdentifier(Document document, IPreprocessingSymbol symbol, CancellationToken cancellationToken) { - var root = await document.GetSyntaxRootAsync(ct).ConfigureAwait(false); + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); if (root is not { ContainsDirectives: true }) return false; - var syntaxTreeIndex = await document.GetSyntaxTreeIndexAsync(ct).ConfigureAwait(false); + var syntaxTreeIndex = await document.GetSyntaxTreeIndexAsync(cancellationToken).ConfigureAwait(false); return syntaxTreeIndex.ProbablyContainsIdentifier(symbol.Name); } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs index 0e6e804f66adc..74ff7dc5ac952 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs @@ -84,8 +84,14 @@ public bool CanReplaceWithRValue(SemanticModel semanticModel, [NotNullWhen(true) if (CSharpSyntaxFacts.Instance.IsExecutableStatement(ancestor)) return null; - if (CSharpSyntaxFacts.Instance.IsIdentifierContainerPreprocessorDirectiveTrivia(ancestor)) - return semanticModel.Compilation.CreatePreprocessingSymbol(token.ValueText); + switch (ancestor.Kind()) + { + case SyntaxKind.IfDirectiveTrivia: + case SyntaxKind.ElifDirectiveTrivia: + case SyntaxKind.DefineDirectiveTrivia: + case SyntaxKind.UndefDirectiveTrivia: + return semanticModel.Compilation.CreatePreprocessingSymbol(token.ValueText); + } } return null; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs index 5787022699ede..ae114ae74942d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs @@ -115,9 +115,6 @@ public bool IsPreProcessorDirectiveContext(SyntaxTree syntaxTree, int position, => syntaxTree.IsPreProcessorDirectiveContext( position, syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken, includeDirectives: true), cancellationToken); - public bool IsIdentifierContainerPreprocessorDirectiveTrivia(SyntaxNode node) - => SyntaxFacts.IsIdentifierContainerDirectiveTrivia(node.Kind()); - public bool IsEntirelyWithinStringOrCharOrNumericLiteral([NotNullWhen(true)] SyntaxTree? syntaxTree, int position, CancellationToken cancellationToken) { if (syntaxTree == null) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs index c82ab2412bb62..e762f3a608805 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs @@ -161,7 +161,6 @@ internal interface ISyntaxFacts /// bool IsPreprocessorKeyword(SyntaxToken token); bool IsPreProcessorDirectiveContext(SyntaxTree syntaxTree, int position, CancellationToken cancellationToken); - bool IsIdentifierContainerPreprocessorDirectiveTrivia(SyntaxNode node); bool IsLiteral(SyntaxToken token); bool IsStringLiteralOrInterpolatedStringLiteral(SyntaxToken token); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb index 4eea97342120e..e1728ba091a63 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb @@ -86,9 +86,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return Nothing End If - If VisualBasicSyntaxFacts.Instance.IsIdentifierContainerPreprocessorDirectiveTrivia(ancestor) Then - Return semanticModel.Compilation.CreatePreprocessingSymbol(token.Text) - End If + Select Case ancestor.Kind() + Case SyntaxKind.IfDirectiveTrivia, + SyntaxKind.ElseIfDirectiveTrivia, + SyntaxKind.ConstDirectiveTrivia + Return semanticModel.Compilation.CreatePreprocessingSymbol(token.Text) + End Select End If Next diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb index 297345f5108d1..6badbc40e16a2 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb @@ -124,10 +124,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Return syntaxTree.IsInPreprocessorDirectiveContext(position, cancellationToken) End Function - Public Function IsIdentifierContainerPreprocessorDirectiveTrivia(syntaxNode As SyntaxNode) As Boolean Implements ISyntaxFacts.IsIdentifierContainerPreprocessorDirectiveTrivia - Return SyntaxFacts.IsIdentifierContainerDirectiveTrivia(syntaxNode.Kind()) - End Function - Public Function IsEntirelyWithinStringOrCharOrNumericLiteral(syntaxTree As SyntaxTree, position As Integer, cancellationToken As CancellationToken) As Boolean Implements ISyntaxFacts.IsEntirelyWithinStringOrCharOrNumericLiteral If syntaxTree Is Nothing Then Return False From 2b343313ccce40c5fbebd8cdf1af845e32a45999 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 28 Jan 2023 00:41:53 +0200 Subject: [PATCH 024/508] Migrate reference finder and add index bit --- .../Finders/AbstractReferenceFinder.cs | 20 ------------- .../PreprocessingSymbolReferenceFinder.cs | 28 +++++++++++++++---- .../SyntaxTree/SyntaxTreeIndex.ContextInfo.cs | 14 ++++++++-- .../SyntaxTree/SyntaxTreeIndex_Create.cs | 5 +++- .../SyntaxTree/SyntaxTreeIndex_Forwarders.cs | 1 + 5 files changed, 39 insertions(+), 29 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs index cba90a6292c79..3f0e01c2635d1 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs @@ -105,26 +105,6 @@ protected static async Task> FindDocumentsAsync( return documents.ToImmutable(); } - protected static async Task> FindAllSolutionDocumentsAsync( - Solution solution, - Func> predicateAsync, - T value, - CancellationToken cancellationToken) - { - using var _ = ArrayBuilder.GetInstance(out var documents); - - foreach (var project in solution.Projects) - { - foreach (var document in await project.GetAllRegularAndSourceGeneratedDocumentsAsync(cancellationToken).ConfigureAwait(false)) - { - if (await predicateAsync(document, value, cancellationToken).ConfigureAwait(false)) - documents.Add(document); - } - } - - return documents.ToImmutable(); - } - /// /// Finds all the documents in the provided project that contain the requested string /// values diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs index 625828dc13161..76f8d4e7dc07e 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -89,15 +90,32 @@ protected override async Task> DetermineDocumentsToSear static async ValueTask HasDirectiveProbablyContainsIdentifier(Document document, IPreprocessingSymbol symbol, CancellationToken cancellationToken) { - var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - if (root is not { ContainsDirectives: true }) - return false; - var syntaxTreeIndex = await document.GetSyntaxTreeIndexAsync(cancellationToken).ConfigureAwait(false); - return syntaxTreeIndex.ProbablyContainsIdentifier(symbol.Name); + return syntaxTreeIndex.ContainsDirective + && syntaxTreeIndex.ProbablyContainsIdentifier(symbol.Name); } } + private static async Task> FindAllSolutionDocumentsAsync( + Solution solution, + Func> predicateAsync, + T value, + CancellationToken cancellationToken) + { + using var _ = ArrayBuilder.GetInstance(out var documents); + + foreach (var project in solution.Projects) + { + foreach (var document in await project.GetAllRegularAndSourceGeneratedDocumentsAsync(cancellationToken).ConfigureAwait(false)) + { + if (await predicateAsync(document, value, cancellationToken).ConfigureAwait(false)) + documents.Add(document); + } + } + + return documents.ToImmutable(); + } + private static async ValueTask> FindPreprocessingReferencesInTokensAsync( ISymbol symbol, FindReferencesDocumentState state, diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs index 3ecb5731408d8..bc9b35803715a 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs @@ -33,7 +33,8 @@ public ContextInfo( bool containsImplicitObjectCreation, bool containsGlobalSuppressMessageAttribute, bool containsConversion, - bool containsGlobalKeyword) + bool containsGlobalKeyword, + bool containsDirective) : this(predefinedTypes, predefinedOperators, ConvertToContainingNodeFlag( containsForEachStatement, @@ -50,7 +51,8 @@ public ContextInfo( containsImplicitObjectCreation, containsGlobalSuppressMessageAttribute, containsConversion, - containsGlobalKeyword)) + containsGlobalKeyword, + containsDirective)) { } @@ -76,7 +78,8 @@ private static ContainingNodes ConvertToContainingNodeFlag( bool containsImplicitObjectCreation, bool containsGlobalSuppressMessageAttribute, bool containsConversion, - bool containsGlobalKeyword) + bool containsGlobalKeyword, + bool containsDirective) { var containingNodes = ContainingNodes.None; @@ -95,6 +98,7 @@ private static ContainingNodes ConvertToContainingNodeFlag( containingNodes |= containsGlobalSuppressMessageAttribute ? ContainingNodes.ContainsGlobalSuppressMessageAttribute : 0; containingNodes |= containsConversion ? ContainingNodes.ContainsConversion : 0; containingNodes |= containsGlobalKeyword ? ContainingNodes.ContainsGlobalKeyword : 0; + containingNodes |= containsDirective ? ContainingNodes.ContainsDirective : 0; return containingNodes; } @@ -150,6 +154,9 @@ public bool ContainsGlobalSuppressMessageAttribute public bool ContainsConversion => (_containingNodes & ContainingNodes.ContainsConversion) == ContainingNodes.ContainsConversion; + public bool ContainsDirective + => (_containingNodes & ContainingNodes.ContainsDirective) == ContainingNodes.ContainsDirective; + public void WriteTo(ObjectWriter writer) { writer.WriteInt32(_predefinedTypes); @@ -193,6 +200,7 @@ private enum ContainingNodes ContainsGlobalSuppressMessageAttribute = 1 << 12, ContainsConversion = 1 << 13, ContainsGlobalKeyword = 1 << 14, + ContainsDirective = 1 << 15, } } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs index fa683ab0971d2..81d8ca0b73690 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs @@ -70,6 +70,7 @@ private static SyntaxTreeIndex CreateIndex( var containsGlobalSuppressMessageAttribute = false; var containsConversion = false; var containsGlobalKeyword = false; + var containsDirective = false; var predefinedTypes = (int)PredefinedType.None; var predefinedOperators = (int)PredefinedOperator.None; @@ -99,6 +100,7 @@ private static SyntaxTreeIndex CreateIndex( containsImplicitObjectCreation = containsImplicitObjectCreation || syntaxFacts.IsImplicitObjectCreationExpression(node); containsGlobalSuppressMessageAttribute = containsGlobalSuppressMessageAttribute || IsGlobalSuppressMessageAttribute(syntaxFacts, node); containsConversion = containsConversion || syntaxFacts.IsConversionExpression(node); + containsDirective = containsDirective || syntaxFacts.IsDirective(node); TryAddGlobalAliasInfo(syntaxFacts, ref globalAliasInfo, node); } @@ -186,7 +188,8 @@ private static SyntaxTreeIndex CreateIndex( containsImplicitObjectCreation, containsGlobalSuppressMessageAttribute, containsConversion, - containsGlobalKeyword), + containsGlobalKeyword, + containsDirective), globalAliasInfo); } finally diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs index b4fc9449f9337..a831c686849a6 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs @@ -34,6 +34,7 @@ internal sealed partial class SyntaxTreeIndex public bool ContainsThisConstructorInitializer => _contextInfo.ContainsThisConstructorInitializer; public bool ContainsTupleExpressionOrTupleType => _contextInfo.ContainsTupleExpressionOrTupleType; public bool ContainsUsingStatement => _contextInfo.ContainsUsingStatement; + public bool ContainsDirective => _contextInfo.ContainsDirective; /// /// Gets the set of global aliases that point to something with the provided name and arity. From f00f75bb08cd41c77380b2bd1da79e74795b90f4 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 28 Jan 2023 01:14:19 +0200 Subject: [PATCH 025/508] Simplify processing and use syntax root node flag --- .../PreprocessingSymbolReferenceFinder.cs | 37 +++++++------------ .../SyntaxTree/SyntaxTreeIndex_Create.cs | 3 +- 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs index 76f8d4e7dc07e..44a14f1113649 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs @@ -80,40 +80,31 @@ protected override async Task> DetermineDocumentsToSear FindReferencesSearchOptions options, CancellationToken cancellationToken) { + using var _ = ArrayBuilder.GetInstance(out var resultDocuments); + // NOTE: We intentionally search for all documents in the entire solution. This is because // the symbols are validly bound by their requested name, despite their current definition // state. Therefore, the same symbol name could be shared across multiple projects and // configured in the project configuration with the same shared identifier. - return await FindAllSolutionDocumentsAsync(project.Solution, HasDirectiveProbablyContainsIdentifier, symbol, cancellationToken) - .ConfigureAwait(false); - - static async ValueTask HasDirectiveProbablyContainsIdentifier(Document document, IPreprocessingSymbol symbol, CancellationToken cancellationToken) + var solution = project.Solution; + foreach (var solutionProject in solution.Projects) { - var syntaxTreeIndex = await document.GetSyntaxTreeIndexAsync(cancellationToken).ConfigureAwait(false); - return syntaxTreeIndex.ContainsDirective - && syntaxTreeIndex.ProbablyContainsIdentifier(symbol.Name); + foreach (var document in await solutionProject.GetAllRegularAndSourceGeneratedDocumentsAsync(cancellationToken).ConfigureAwait(false)) + { + if (await HasDirectiveProbablyContainsIdentifier(document, cancellationToken).ConfigureAwait(false)) + resultDocuments.Add(document); + } } - } - private static async Task> FindAllSolutionDocumentsAsync( - Solution solution, - Func> predicateAsync, - T value, - CancellationToken cancellationToken) - { - using var _ = ArrayBuilder.GetInstance(out var documents); + return resultDocuments.ToImmutable(); - foreach (var project in solution.Projects) + async ValueTask HasDirectiveProbablyContainsIdentifier(Document document, CancellationToken cancellationToken) { - foreach (var document in await project.GetAllRegularAndSourceGeneratedDocumentsAsync(cancellationToken).ConfigureAwait(false)) - { - if (await predicateAsync(document, value, cancellationToken).ConfigureAwait(false)) - documents.Add(document); - } + var syntaxTreeIndex = await document.GetSyntaxTreeIndexAsync(cancellationToken).ConfigureAwait(false); + return syntaxTreeIndex.ContainsDirective + && syntaxTreeIndex.ProbablyContainsIdentifier(symbol.Name); } - - return documents.ToImmutable(); } private static async ValueTask> FindPreprocessingReferencesInTokensAsync( diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs index 81d8ca0b73690..97d08697ec117 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs @@ -70,7 +70,7 @@ private static SyntaxTreeIndex CreateIndex( var containsGlobalSuppressMessageAttribute = false; var containsConversion = false; var containsGlobalKeyword = false; - var containsDirective = false; + var containsDirective = root.ContainsDirectives; var predefinedTypes = (int)PredefinedType.None; var predefinedOperators = (int)PredefinedOperator.None; @@ -100,7 +100,6 @@ private static SyntaxTreeIndex CreateIndex( containsImplicitObjectCreation = containsImplicitObjectCreation || syntaxFacts.IsImplicitObjectCreationExpression(node); containsGlobalSuppressMessageAttribute = containsGlobalSuppressMessageAttribute || IsGlobalSuppressMessageAttribute(syntaxFacts, node); containsConversion = containsConversion || syntaxFacts.IsConversionExpression(node); - containsDirective = containsDirective || syntaxFacts.IsDirective(node); TryAddGlobalAliasInfo(syntaxFacts, ref globalAliasInfo, node); } From 9a826f4b102d832f013e4a35815920892f1c2c85 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 28 Jan 2023 01:32:23 +0200 Subject: [PATCH 026/508] Inline document filtering + update persistence ver --- .../Finders/PreprocessingSymbolReferenceFinder.cs | 10 ++-------- .../Shared/AbstractSyntaxIndex_Persistence.cs | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs index 44a14f1113649..7edde3c236a44 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs @@ -92,19 +92,13 @@ protected override async Task> DetermineDocumentsToSear { foreach (var document in await solutionProject.GetAllRegularAndSourceGeneratedDocumentsAsync(cancellationToken).ConfigureAwait(false)) { - if (await HasDirectiveProbablyContainsIdentifier(document, cancellationToken).ConfigureAwait(false)) + var syntaxTreeIndex = await document.GetSyntaxTreeIndexAsync(cancellationToken).ConfigureAwait(false); + if (syntaxTreeIndex.ContainsDirective && syntaxTreeIndex.ProbablyContainsIdentifier(symbol.Name)) resultDocuments.Add(document); } } return resultDocuments.ToImmutable(); - - async ValueTask HasDirectiveProbablyContainsIdentifier(Document document, CancellationToken cancellationToken) - { - var syntaxTreeIndex = await document.GetSyntaxTreeIndexAsync(cancellationToken).ConfigureAwait(false); - return syntaxTreeIndex.ContainsDirective - && syntaxTreeIndex.ProbablyContainsIdentifier(symbol.Name); - } } private static async ValueTask> FindPreprocessingReferencesInTokensAsync( diff --git a/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs b/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs index f64de24fa331f..64da3c786df28 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.FindSymbols internal partial class AbstractSyntaxIndex : IObjectWritable { private static readonly string s_persistenceName = typeof(TIndex).Name; - private static readonly Checksum s_serializationFormatChecksum = Checksum.Create("35"); + private static readonly Checksum s_serializationFormatChecksum = Checksum.Create("36"); /// /// Cache of ParseOptions to a checksum for the contained From 04ba3d4286943742e6623ada6ac055a569d52041 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 28 Jan 2023 15:42:05 +0200 Subject: [PATCH 027/508] Migrate preprocessing symbol display logic --- .../Portable/SymbolDisplay/SymbolDisplay.cs | 2 +- .../SymbolDisplayVisitor_Misc.cs | 32 +++++++++++++++++++ .../PublicModel/PreprocessingSymbol.cs | 14 +++----- .../Portable/SymbolDisplay/SymbolDisplay.vb | 2 +- .../SymbolDisplayVisitor_Misc.vb | 26 +++++++++++++++ .../Portable/Symbols/PreprocessingSymbol.vb | 21 ------------ 6 files changed, 64 insertions(+), 33 deletions(-) create mode 100644 src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.cs create mode 100644 src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.vb diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplay.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplay.cs index f454f7ec0fed4..bdb7bf40481bd 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplay.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplay.cs @@ -237,7 +237,7 @@ private static ImmutableArray ToDisplayParts( var builder = ArrayBuilder.GetInstance(); var visitor = new SymbolDisplayVisitor(builder, format, semanticModelOpt, positionOpt); - symbol.Accept(visitor); + visitor.VisitSymbol(symbol); return builder.ToImmutableAndFree(); } diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.cs new file mode 100644 index 0000000000000..0c6c934458b5c --- /dev/null +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable disable + +namespace Microsoft.CodeAnalysis.CSharp +{ + internal partial class SymbolDisplayVisitor + { + private void AddPreprocessingName(IPreprocessingSymbol preprocessing) + { + var part = new SymbolDisplayPart(SymbolDisplayPartKind.PreprocessingName, preprocessing, preprocessing.Name); + builder.Add(part); + } + + /// + /// Visits a symbol, and specifically handles symbol types that do not support visiting. + /// + /// The symbol to visit. + public void VisitSymbol(ISymbol symbol) + { + if (symbol is IPreprocessingSymbol preprocessingSymbol) + { + AddPreprocessingName(preprocessingSymbol); + return; + } + + symbol.Accept(this); + } + } +} diff --git a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs index 76e2701cf691f..0d9ec4b84d04f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs @@ -78,28 +78,22 @@ bool ISymbol.Equals(ISymbol? other, CodeAnalysis.SymbolEqualityComparer equality string ISymbol.ToDisplayString(SymbolDisplayFormat? format) { - return _name; + return SymbolDisplay.ToDisplayString(this, format); } ImmutableArray ISymbol.ToDisplayParts(SymbolDisplayFormat? format) { - return ToDisplayParts(); + return SymbolDisplay.ToDisplayParts(this, format); } string ISymbol.ToMinimalDisplayString(SemanticModel semanticModel, int position, SymbolDisplayFormat? format) { - return _name; + return SymbolDisplay.ToMinimalDisplayString(this, Symbol.GetCSharpSemanticModel(semanticModel), position, format); } ImmutableArray ISymbol.ToMinimalDisplayParts(SemanticModel semanticModel, int position, SymbolDisplayFormat? format) { - return ToDisplayParts(); - } - - private ImmutableArray ToDisplayParts() - { - var part = new SymbolDisplayPart(SymbolDisplayPartKind.PreprocessingName, this, _name); - return ImmutableArray.Create(part); + return SymbolDisplay.ToMinimalDisplayParts(this, Symbol.GetCSharpSemanticModel(semanticModel), position, format); } SymbolKind ISymbol.Kind => SymbolKind.Preprocessing; diff --git a/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplay.vb b/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplay.vb index d171bd0770539..46c0f921bb46d 100644 --- a/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplay.vb +++ b/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplay.vb @@ -105,7 +105,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Dim builder = ArrayBuilder(Of SymbolDisplayPart).GetInstance() Dim visitor = New SymbolDisplayVisitor(builder, format, semanticModelOpt, positionOpt) - symbol.Accept(visitor) + visitor.VisitSymbol(symbol) Return builder.ToImmutableAndFree() End Function diff --git a/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.vb b/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.vb new file mode 100644 index 0000000000000..a03d0f91b91bd --- /dev/null +++ b/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.vb @@ -0,0 +1,26 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Namespace Microsoft.CodeAnalysis.VisualBasic + Partial Friend Class SymbolDisplayVisitor + Private Sub AddPreprocessingName(preprocessing As IPreprocessingSymbol) + Dim part = New SymbolDisplayPart(SymbolDisplayPartKind.PreprocessingName, preprocessing, preprocessing.Name) + builder.Add(part) + End Sub + + ''' + ''' Visits a symbol, and specifically handles symbol types that do not support visiting. + ''' + ''' The symbol to visit. + Public Sub VisitSymbol(symbol As ISymbol) + Dim preprocessingSymbol = TryCast(symbol, IPreprocessingSymbol) + If preprocessingSymbol IsNot Nothing Then + AddPreprocessingName(preprocessingSymbol) + Return + End If + + symbol.Accept(Me) + End Sub + End Class +End Namespace diff --git a/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb index a6a5f6c967d68..c288bda69783a 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb @@ -168,27 +168,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Friend Overloads Overrides Function Accept(Of TArgument, TResult)(visitor As VisualBasicSymbolVisitor(Of TArgument, TResult), arg As TArgument) As TResult Throw New NotSupportedException() End Function - - Private Function ISymbol_ToDisplayString(Optional format As SymbolDisplayFormat = Nothing) As String Implements ISymbol.ToDisplayString - Return Name - End Function - - Private Function ISymbol_ToDisplayParts(Optional format As SymbolDisplayFormat = Nothing) As ImmutableArray(Of SymbolDisplayPart) Implements ISymbol.ToDisplayParts - Return ToDisplayParts() - End Function - - Private Function ISymbol_ToMinimalDisplayString(semanticModel As SemanticModel, position As Integer, Optional format As SymbolDisplayFormat = Nothing) As String Implements ISymbol.ToMinimalDisplayString - Return Name - End Function - - Private Function ISymbol_ToMinimalDisplayParts(semanticModel As SemanticModel, position As Integer, Optional format As SymbolDisplayFormat = Nothing) As ImmutableArray(Of SymbolDisplayPart) Implements ISymbol.ToMinimalDisplayParts - Return ToDisplayParts() - End Function - - Private Shadows Function ToDisplayParts() As ImmutableArray(Of SymbolDisplayPart) - Dim part = New SymbolDisplayPart(SymbolDisplayPartKind.PreprocessingName, Me, Name) - Return ImmutableArray.Create(part) - End Function End Class End Namespace From a83c40d40ab4d3feff18233da4d00cb0a526a5c2 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Sat, 4 Mar 2023 01:29:45 +0200 Subject: [PATCH 028/508] Comply with classification type tests --- .../Core/Portable/Classification/ClassificationTypeNames.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/Classification/ClassificationTypeNames.cs b/src/Workspaces/Core/Portable/Classification/ClassificationTypeNames.cs index 4d78f9de2b0ab..b2570b347a029 100644 --- a/src/Workspaces/Core/Portable/Classification/ClassificationTypeNames.cs +++ b/src/Workspaces/Core/Portable/Classification/ClassificationTypeNames.cs @@ -41,6 +41,7 @@ public static class ClassificationTypeNames StructName, RecordStructName, TypeParameterName, + PreprocessingName, FieldName, EnumMemberName, ConstantName, @@ -123,8 +124,9 @@ public static class ClassificationTypeNames public const string ModuleName = "module name"; public const string StructName = "struct name"; public const string RecordStructName = "record struct name"; - public const string PreprocessingName = "preprocessing name"; public const string TypeParameterName = "type parameter name"; + // NOTE: This is internal until the classification type is formally supported + internal const string PreprocessingName = "preprocessing name"; public const string FieldName = "field name"; public const string EnumMemberName = "enum member name"; From dfe7a138d79528985723912f9669695bee1096b3 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Sat, 4 Mar 2023 16:21:38 +0200 Subject: [PATCH 029/508] Remove internal API from unshipped public APIs --- src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt index fca7ace06c013..7eb1cd93de9f2 100644 --- a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt @@ -6,7 +6,6 @@ *REMOVED*abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.DelegateDeclaration(string name, System.Collections.Generic.IEnumerable parameters = null, System.Collections.Generic.IEnumerable typeParameters = null, Microsoft.CodeAnalysis.SyntaxNode returnType = null, Microsoft.CodeAnalysis.Accessibility accessibility = Microsoft.CodeAnalysis.Accessibility.NotApplicable, Microsoft.CodeAnalysis.Editing.DeclarationModifiers modifiers = default(Microsoft.CodeAnalysis.Editing.DeclarationModifiers)) -> Microsoft.CodeAnalysis.SyntaxNode *REMOVED*abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.WithTypeParameters(Microsoft.CodeAnalysis.SyntaxNode declaration, System.Collections.Generic.IEnumerable typeParameters) -> Microsoft.CodeAnalysis.SyntaxNode *REMOVED*abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.ParameterDeclaration(string name, Microsoft.CodeAnalysis.SyntaxNode type = null, Microsoft.CodeAnalysis.SyntaxNode initializer = null, Microsoft.CodeAnalysis.RefKind refKind = Microsoft.CodeAnalysis.RefKind.None) -> Microsoft.CodeAnalysis.SyntaxNode -const Microsoft.CodeAnalysis.Classification.ClassificationTypeNames.PreprocessingName = "preprocessing name" -> string Microsoft.CodeAnalysis.CodeFixes.CodeFixContext.CodeFixContext(Microsoft.CodeAnalysis.TextDocument document, Microsoft.CodeAnalysis.Diagnostic diagnostic, System.Action> registerCodeFix, System.Threading.CancellationToken cancellationToken) -> void Microsoft.CodeAnalysis.CodeFixes.CodeFixContext.CodeFixContext(Microsoft.CodeAnalysis.TextDocument document, Microsoft.CodeAnalysis.Text.TextSpan span, System.Collections.Immutable.ImmutableArray diagnostics, System.Action> registerCodeFix, System.Threading.CancellationToken cancellationToken) -> void Microsoft.CodeAnalysis.CodeFixes.CodeFixContext.TextDocument.get -> Microsoft.CodeAnalysis.TextDocument From 85e3e952238de0cc62dce88d398fea52777cd5e3 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Sat, 22 Jul 2023 20:07:26 +0300 Subject: [PATCH 030/508] Add #pragma warning test --- ...indReferencesTests.PreprocessingSymbols.vb | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb index 2d19e5ab7b408..9bb34b1462580 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb @@ -85,6 +85,31 @@ class Class #endregion PREPROCESSING_SYMBOL } +#if NOT_PREPROCESSING_SYMBOL +#elif PREPROCESSING_SYMBOL +#endif + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + + Public Async Function TestPreprocessingSymbolPragmaWarning(kind As TestKind, host As TestHost) As Task + Dim input = + + + +#define PREPROCESSING_SYMBOL + +class Class +{ + #pragma warning disable PREPROCESSING_SYMBOL + public void Method() { } + #pragma warning restore PREPROC$$ESSING_SYMBOL +} + #if NOT_PREPROCESSING_SYMBOL #elif PREPROCESSING_SYMBOL #endif From a86ebc8b4d0cccb5dc83c08b660c0278828473e3 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Sat, 22 Jul 2023 20:50:11 +0300 Subject: [PATCH 031/508] Fix handler test for the new API --- .../References/FindAllReferencesHandlerTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs index 80069cd9895d0..15f7fb320cd4f 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs @@ -292,8 +292,8 @@ public async Task TestFindAllReferencesAsync_StaticClassification(bool mutatingL Assert.Equal(9, textRuns.Count()); } - [Fact] - public async Task TestFindAllReferencesAsync_PreprocessingSymbol() + [Theory, CombinatorialData] + public async Task TestFindAllReferencesAsync_PreprocessingSymbol(bool mutatingLspWorkspace) { var markup = @"#define {|reference:PREPROCESSING_SYMBOL|} @@ -314,7 +314,7 @@ class PREPROCESSING_SYMBOL { } "; - await using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, CapabilitiesWithVSExtensions); var results = await RunFindAllReferencesAsync(testLspServer, testLspServer.GetLocations("caret").First()); From 07d1192ab70289e06b2b1b1d9489bb4fc729a34b Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Sat, 22 Jul 2023 22:22:42 +0300 Subject: [PATCH 032/508] Remove syntactical filtering for FAR --- .../PreprocessingSymbolReferenceFinder.cs | 42 +------------------ .../SyntaxFacts/ISyntaxFactsExtensions.cs | 25 ----------- 2 files changed, 2 insertions(+), 65 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs index 7edde3c236a44..20635dc925a47 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs @@ -26,48 +26,10 @@ protected sealed override async ValueTask> FindRe var normalReferences = await FindPreprocessingReferencesInTokensAsync( symbol, state, - tokens.WhereAsArray(MatchesPreprocessingReference, state), + tokens, cancellationToken).ConfigureAwait(false); return normalReferences; - - static bool MatchesPreprocessingReference(SyntaxToken token, FindReferencesDocumentState state) - { - var syntaxFacts = state.SyntaxFacts; - - var tokenParent = token.Parent; - Debug.Assert(tokenParent is not null); - - // Quickly evaluate the common case that the parent is a #define or #undef directive - var parentKind = tokenParent.RawKind; - if (parentKind == syntaxFacts.SyntaxKinds.DefineDirectiveTrivia) - return true; - - if (parentKind == syntaxFacts.SyntaxKinds.UndefDirectiveTrivia) - return true; - - // In VB, a #Const directive assigns a value that can also - // derive from preprocessing symbols - if (state.Document.Project.Language is LanguageNames.VisualBasic) - { - var extendedTokenParent = tokenParent; - while (true) - { - extendedTokenParent = extendedTokenParent.Parent; - - if (extendedTokenParent is null) - break; - - if (extendedTokenParent.RawKind == syntaxFacts.SyntaxKinds.DefineDirectiveTrivia) - { - return true; - } - } - } - - // Otherwise, only inside an #if or #elif directive are preprocessing symbols used - return syntaxFacts.SpansIfOrElseIfPreprocessorDirective(tokenParent); - } } protected override bool CanFind(IPreprocessingSymbol symbol) => true; @@ -135,7 +97,7 @@ private static async ValueTask PreprocessingSymbolMatchesAsync(ISymbol sym private static async ValueTask PreprocessingSymbolsMatchAsync( IPreprocessingSymbol searchSymbol, FindReferencesDocumentState state, SyntaxToken token, CancellationToken cancellationToken) { - var symbol = state.SemanticModel.Compilation.CreatePreprocessingSymbol(token.ValueText); + var symbol = state.SemanticModel.GetPreprocessingSymbolInfo(token.Parent!).Symbol; return await SymbolFinder.OriginalSymbolsMatchAsync(state.Solution, searchSymbol, symbol, cancellationToken).ConfigureAwait(false); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs index 649698c2434c0..d15bb23e25040 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs @@ -248,31 +248,6 @@ public static bool SpansPreprocessorDirective(this ISyntaxFacts syntaxFacts, IEn private static bool SpansPreprocessorDirective(this ISyntaxFacts syntaxFacts, SyntaxTriviaList list) => list.Any(syntaxFacts.IsPreprocessorDirective); - public static bool SpansIfOrElseIfPreprocessorDirective(this ISyntaxFacts syntaxFacts, SyntaxToken token) - { - var parent = token.Parent; - return syntaxFacts.SpansIfOrElseIfPreprocessorDirective(parent); - } - public static bool SpansIfOrElseIfPreprocessorDirective(this ISyntaxFacts syntaxFacts, SyntaxNode? node) - { - var kinds = syntaxFacts.SyntaxKinds; - - var ifDirectiveKind = kinds.IfDirectiveTrivia; - var elseIfDirectiveKind = kinds.ElifDirectiveTrivia; - - while (node is not null) - { - if (node.RawKind == ifDirectiveKind) - return true; - if (node.RawKind == elseIfDirectiveKind) - return true; - - node = node.Parent; - } - - return false; - } - public static bool IsLegalIdentifier(this ISyntaxFacts syntaxFacts, string name) { if (name.Length == 0) From 4a64e6c2ca901f2c706342028c95ab2caac31ce6 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Sun, 23 Jul 2023 01:46:34 +0300 Subject: [PATCH 033/508] Remove missing members from unshipped public API --- src/Compilers/Core/Portable/PublicAPI.Unshipped.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 2702465f22c5d..9321d420359c8 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -49,6 +49,4 @@ virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.V virtual Microsoft.CodeAnalysis.SyntaxContextReceiverCreator.Invoke() -> Microsoft.CodeAnalysis.ISyntaxContextReceiver? virtual Microsoft.CodeAnalysis.SyntaxReceiverCreator.Invoke() -> Microsoft.CodeAnalysis.ISyntaxReceiver! Microsoft.CodeAnalysis.SymbolDisplayPartKind.PreprocessingName = 33 -> Microsoft.CodeAnalysis.SymbolDisplayPartKind -Microsoft.CodeAnalysis.SemanticModel.GetPreprocessingSymbolInfo(Microsoft.CodeAnalysis.SyntaxToken token) -> Microsoft.CodeAnalysis.PreprocessingSymbolInfo -Microsoft.CodeAnalysis.Compilation.CreatePreprocessingSymbol(string! name) -> Microsoft.CodeAnalysis.IPreprocessingSymbol! -abstract Microsoft.CodeAnalysis.SemanticModel.GetPreprocessingSymbolInfoCore(Microsoft.CodeAnalysis.SyntaxToken token) -> Microsoft.CodeAnalysis.PreprocessingSymbolInfo \ No newline at end of file +Microsoft.CodeAnalysis.Compilation.CreatePreprocessingSymbol(string! name) -> Microsoft.CodeAnalysis.IPreprocessingSymbol! \ No newline at end of file From d567a955ead4112232f46d163397c10d3b691f20 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Sun, 23 Jul 2023 09:25:06 +0300 Subject: [PATCH 034/508] Remove GetSymbolInfo for directive syntaxes --- .../Compilation/CSharpSemanticModel.cs | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index c129e25b7943d..5d6bbf7adadb5 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -505,26 +505,6 @@ internal virtual IOperation GetOperationWorker(CSharpSyntaxNode node, Cancellati return this.GetSymbolInfoWorker(node, SymbolInfoOptions.DefaultOptions, cancellationToken); } - /// - /// Gets the SymbolInfo for the preprocessing symbol defined in a #define directive trivia, if any. - /// - public SymbolInfo GetSymbolInfo(DefineDirectiveTriviaSyntax node) - { - CheckSyntaxNode(node); - var preprocessingSymbol = CreatePreprocessingSymbol(node.Name); - return new(preprocessingSymbol); - } - - /// - /// Gets the SymbolInfo for the preprocessing symbol defined in an #undef directive trivia, if any. - /// - public SymbolInfo GetSymbolInfo(UndefDirectiveTriviaSyntax node) - { - CheckSyntaxNode(node); - var preprocessingSymbol = CreatePreprocessingSymbol(node.Name); - return new(preprocessingSymbol); - } - /// /// Returns what symbol(s), if any, the given expression syntax bound to in the program. /// @@ -5020,10 +5000,6 @@ private SymbolInfo GetSymbolInfoFromNode(SyntaxNode node, CancellationToken canc return this.GetSymbolInfo(orderingSyntax, cancellationToken); case PositionalPatternClauseSyntax ppcSyntax: return this.GetSymbolInfo(ppcSyntax, cancellationToken); - case DefineDirectiveTriviaSyntax defineSyntax: - return this.GetSymbolInfo(defineSyntax); - case UndefDirectiveTriviaSyntax undefSyntax: - return this.GetSymbolInfo(undefSyntax); } return SymbolInfo.None; From 1268b43a9a6e873dec8720c29aa10da3cf637bcd Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Sun, 23 Jul 2023 09:28:06 +0300 Subject: [PATCH 035/508] Update attributes on tests --- ...indReferencesTests.PreprocessingSymbols.vb | 21 ++++++++++++------- .../CSharp/Test/F1Help/F1HelpTests.cs | 3 ++- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb index 9bb34b1462580..576eea1b70e97 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb @@ -9,7 +9,7 @@ Imports Microsoft.CodeAnalysis.Remote.Testing Namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences Partial Public Class FindReferencesTests - + Public Async Function TestPreprocessingSymbolBasic(kind As TestKind, host As TestHost) As Task Dim input = @@ -39,7 +39,7 @@ class PREPROCESSING_SYMBOL Await TestAPIAndFeature(input, kind, host) End Function - + Public Async Function TestNotPreprocessingSymbol(kind As TestKind, host As TestHost) As Task Dim input = @@ -69,7 +69,7 @@ class {|Definition:PREPROCES$$SING_SYMBOL|} Await TestAPIAndFeature(input, kind, host) End Function - + Public Async Function TestPreprocessingSymbolRegion(kind As TestKind, host As TestHost) As Task Dim input = @@ -94,7 +94,7 @@ class Class Await TestAPIAndFeature(input, kind, host) End Function - + Public Async Function TestPreprocessingSymbolPragmaWarning(kind As TestKind, host As TestHost) As Task Dim input = @@ -119,7 +119,7 @@ class Class Await TestAPIAndFeature(input, kind, host) End Function - + Public Async Function TestPreprocessingSymbolMultipleDocuments(kind As TestKind, host As TestHost) As Task Dim input = @@ -154,7 +154,7 @@ class Class Await TestAPIAndFeature(input, kind, host) End Function - + Public Async Function TestPreprocessingSymbolMultipleProjects1(kind As TestKind, host As TestHost) As Task Dim input = @@ -186,7 +186,7 @@ class Class Await TestAPIAndFeature(input, kind, host) End Function - + Public Async Function TestPreprocessingSymbolMultipleProjects2HoverCSharp(kind As TestKind, host As TestHost) As Task Dim input = @@ -215,7 +215,7 @@ class Class Await TestAPIAndFeature(input, kind, host) End Function - + Public Async Function TestPreprocessingSymbolMultipleProjects2HoverVB(kind As TestKind, host As TestHost) As Task Dim input = @@ -244,6 +244,7 @@ class Class Await TestAPIAndFeature(input, kind, host) End Function + Public Async Function TestPreprocessingSymbolUsedInSourceGeneratedDocument(kind As TestKind, host As TestHost) As Task Dim input = @@ -268,6 +269,7 @@ class Class Await TestAPIAndFeature(input, kind, host) End Function + Public Async Function TestPreprocessingSymbolHoverDefine(kind As TestKind, host As TestHost) As Task Dim input = @@ -282,6 +284,7 @@ class Class Await TestAPIAndFeature(input, kind, host) End Function + Public Async Function TestPreprocessingSymbolHoverUndef(kind As TestKind, host As TestHost) As Task Dim input = @@ -296,6 +299,7 @@ class Class Await TestAPIAndFeature(input, kind, host) End Function + Public Async Function TestPreprocessingSymbolHoverConst(kind As TestKind, host As TestHost) As Task Dim input = @@ -313,6 +317,7 @@ class Class Await TestAPIAndFeature(input, kind, host) End Function + Public Async Function TestPreprocessingSymbolHoverAssignedConst(kind As TestKind, host As TestHost) As Task Dim input = diff --git a/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs b/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs index 9f312d34fa637..4eb9ef74fdf97 100644 --- a/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs +++ b/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs @@ -1586,8 +1586,9 @@ void M() }", "discard"); } + [WorkItem("https://github.com/dotnet/roslyn/issues/66009")] [Fact] - public async Task TestNotFound() + public async Task TestNotFound_PreprocessingSymbol() { await TestAsync( @" From 9f06de24ac1946deb08750efb4e8f87283eb2e25 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Sun, 23 Jul 2023 10:02:49 +0300 Subject: [PATCH 036/508] Fix nits --- .../Portable/Compilation/CSharpSemanticModel.cs | 13 ++++++++----- .../SymbolDisplay/SymbolDisplayVisitor_Misc.cs | 9 ++------- .../SymbolDisplay/SymbolDisplayVisitor_Misc.vb | 8 ++------ .../Finders/PreprocessingSymbolReferenceFinder.cs | 12 +++++------- 4 files changed, 17 insertions(+), 25 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index 5d6bbf7adadb5..50a1525ddb471 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -4856,20 +4856,22 @@ public PreprocessingSymbolInfo GetPreprocessingSymbolInfo(IdentifierNameSyntax n return PreprocessingSymbolInfo.None; } + /// /// Gets the preprocessing symbol info for the preprocessing symbol defined in the #define directive. /// /// A #define directive trivia node. - public PreprocessingSymbolInfo GetPreprocessingSymbolInfo(DefineDirectiveTriviaSyntax node) + private PreprocessingSymbolInfo GetPreprocessingSymbolInfo(DefineDirectiveTriviaSyntax node) { CheckSyntaxNode(node); return CreatePreprocessingSymbolInfo(node.Name); } + /// /// Gets the preprocessing symbol info for the preprocessing symbol undefined in the #undef directive. /// /// An #undef directive trivia node. - public PreprocessingSymbolInfo GetPreprocessingSymbolInfo(UndefDirectiveTriviaSyntax node) + private PreprocessingSymbolInfo GetPreprocessingSymbolInfo(UndefDirectiveTriviaSyntax node) { CheckSyntaxNode(node); return CreatePreprocessingSymbolInfo(node.Name); @@ -4881,12 +4883,13 @@ private PreprocessingSymbolInfo CreatePreprocessingSymbolInfo(in SyntaxToken ide var preprocessingSymbol = CreatePreprocessingSymbol(identifier); return new(preprocessingSymbol, isDefined); } + private Symbols.PublicModel.PreprocessingSymbol CreatePreprocessingSymbol(in SyntaxToken identifier) { - return new( + return new Symbols.PublicModel.PreprocessingSymbol( identifier.ValueText, - Compilation.Assembly.ISymbol as IAssemblySymbol, - Compilation.SourceModule.ISymbol as IModuleSymbol); + Compilation.Assembly.GetPublicSymbol(), + Compilation.SourceModule.GetPublicSymbol()); } /// diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.cs index 0c6c934458b5c..f6e38a0ea39bb 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.cs @@ -8,12 +8,6 @@ namespace Microsoft.CodeAnalysis.CSharp { internal partial class SymbolDisplayVisitor { - private void AddPreprocessingName(IPreprocessingSymbol preprocessing) - { - var part = new SymbolDisplayPart(SymbolDisplayPartKind.PreprocessingName, preprocessing, preprocessing.Name); - builder.Add(part); - } - /// /// Visits a symbol, and specifically handles symbol types that do not support visiting. /// @@ -22,7 +16,8 @@ public void VisitSymbol(ISymbol symbol) { if (symbol is IPreprocessingSymbol preprocessingSymbol) { - AddPreprocessingName(preprocessingSymbol); + var part = new SymbolDisplayPart(SymbolDisplayPartKind.PreprocessingName, preprocessingSymbol, preprocessingSymbol.Name); + builder.Add(part); return; } diff --git a/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.vb b/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.vb index a03d0f91b91bd..2daf9865cd7e2 100644 --- a/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.vb +++ b/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.vb @@ -4,11 +4,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Partial Friend Class SymbolDisplayVisitor - Private Sub AddPreprocessingName(preprocessing As IPreprocessingSymbol) - Dim part = New SymbolDisplayPart(SymbolDisplayPartKind.PreprocessingName, preprocessing, preprocessing.Name) - builder.Add(part) - End Sub - ''' ''' Visits a symbol, and specifically handles symbol types that do not support visiting. ''' @@ -16,7 +11,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Public Sub VisitSymbol(symbol As ISymbol) Dim preprocessingSymbol = TryCast(symbol, IPreprocessingSymbol) If preprocessingSymbol IsNot Nothing Then - AddPreprocessingName(preprocessingSymbol) + Dim part = New SymbolDisplayPart(SymbolDisplayPartKind.PreprocessingName, preprocessingSymbol, preprocessingSymbol.Name) + builder.Add(part) Return End If diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs index 20635dc925a47..a69457a7a597f 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs @@ -16,6 +16,8 @@ namespace Microsoft.CodeAnalysis.FindSymbols.Finders; internal class PreprocessingSymbolReferenceFinder : AbstractReferenceFinder { + protected override bool CanFind(IPreprocessingSymbol symbol) => true; + protected sealed override async ValueTask> FindReferencesInDocumentAsync( IPreprocessingSymbol symbol, FindReferencesDocumentState state, @@ -32,8 +34,6 @@ protected sealed override async ValueTask> FindRe return normalReferences; } - protected override bool CanFind(IPreprocessingSymbol symbol) => true; - protected override async Task> DetermineDocumentsToSearchAsync( IPreprocessingSymbol symbol, HashSet? globalAliases, @@ -64,7 +64,7 @@ protected override async Task> DetermineDocumentsToSear } private static async ValueTask> FindPreprocessingReferencesInTokensAsync( - ISymbol symbol, + IPreprocessingSymbol symbol, FindReferencesDocumentState state, ImmutableArray tokens, CancellationToken cancellationToken) @@ -78,7 +78,7 @@ private static async ValueTask> FindPreprocessing symbol, state, token, cancellationToken).ConfigureAwait(false); if (matched) { - var finderLocation = CreateFinderLocation(state, token, cancellationToken); + var finderLocation = CreateFinderLocation(state, token, CandidateReason.None, cancellationToken); locations.Add(finderLocation); } @@ -94,13 +94,11 @@ private static async ValueTask PreprocessingSymbolMatchesAsync(ISymbol sym return await PreprocessingSymbolsMatchAsync(preprocessingSearchSymbol, state, token, cancellationToken).ConfigureAwait(false); } + private static async ValueTask PreprocessingSymbolsMatchAsync( IPreprocessingSymbol searchSymbol, FindReferencesDocumentState state, SyntaxToken token, CancellationToken cancellationToken) { var symbol = state.SemanticModel.GetPreprocessingSymbolInfo(token.Parent!).Symbol; return await SymbolFinder.OriginalSymbolsMatchAsync(state.Solution, searchSymbol, symbol, cancellationToken).ConfigureAwait(false); } - - private static FinderLocation CreateFinderLocation(FindReferencesDocumentState state, SyntaxToken token, CancellationToken cancellationToken) - => CreateFinderLocation(state, token, CandidateReason.None, cancellationToken); } From d3731c44553cc8dddc07453d725e9ab42e868e53 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Sun, 23 Jul 2023 13:03:45 +0300 Subject: [PATCH 037/508] Support preprocessing name in classification --- .../TestUtilities/Classification/FormattedClassifications.cs | 4 ++++ .../Protocol/Handler/SemanticTokens/SemanticTokensSchema.cs | 1 + src/Workspaces/Core/Portable/Classification/Classifier.cs | 1 + 3 files changed, 6 insertions(+) diff --git a/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.cs b/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.cs index 127aafb3fa507..bc7c2e11ffea4 100644 --- a/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.cs +++ b/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.cs @@ -46,6 +46,10 @@ public static FormattedClassification Delegate(string text) public static FormattedClassification TypeParameter(string text) => New(text, ClassificationTypeNames.TypeParameterName); + [DebuggerStepThrough] + public static FormattedClassification Preprocessing(string text) + => New(text, ClassificationTypeNames.PreprocessingName); + [DebuggerStepThrough] public static FormattedClassification Namespace(string text) => New(text, ClassificationTypeNames.NamespaceName); diff --git a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensSchema.cs b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensSchema.cs index 4265be9758086..3743aa5c8b532 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensSchema.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensSchema.cs @@ -52,6 +52,7 @@ internal readonly struct SemanticTokensSchema [ClassificationTypeNames.PreprocessorKeyword] = SemanticTokenTypes.Macro, // in https://code.visualstudio.com/api/language-extensions/semantic-highlight-guide#standard-token-types-and-modifiers [ClassificationTypeNames.LabelName] = "label", + [ClassificationTypeNames.PreprocessingName] = "label", }).ToImmutableDictionary(); /// diff --git a/src/Workspaces/Core/Portable/Classification/Classifier.cs b/src/Workspaces/Core/Portable/Classification/Classifier.cs index 4d1a36c96fd5f..62b79b1e04782 100644 --- a/src/Workspaces/Core/Portable/Classification/Classifier.cs +++ b/src/Workspaces/Core/Portable/Classification/Classifier.cs @@ -168,6 +168,7 @@ private static IEnumerable Space(int count = 1) ClassificationTypeNames.DelegateName => SymbolDisplayPartKind.DelegateName, ClassificationTypeNames.EnumName => SymbolDisplayPartKind.EnumName, ClassificationTypeNames.TypeParameterName => SymbolDisplayPartKind.TypeParameterName, + ClassificationTypeNames.PreprocessingName => SymbolDisplayPartKind.PreprocessingName, ClassificationTypeNames.ModuleName => SymbolDisplayPartKind.ModuleName, ClassificationTypeNames.VerbatimStringLiteral => SymbolDisplayPartKind.StringLiteral, ClassificationTypeNames.FieldName => SymbolDisplayPartKind.FieldName, From 3082e94308bffc5acb91c6d1462ebeb4438d235a Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Wed, 26 Jul 2023 20:50:37 +0300 Subject: [PATCH 038/508] Adjust preprocessing symbol glyph --- .../Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs b/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs index 5e536de119077..cc0c3c6c1143a 100644 --- a/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs +++ b/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs @@ -153,9 +153,8 @@ MethodKind.Conversion or case SymbolKind.TypeParameter: return Glyph.TypeParameter; - // Temporary fix case SymbolKind.Preprocessing: - return Glyph.Label; + return Glyph.Keyword; default: throw new ArgumentException(FeaturesResources.The_symbol_does_not_have_an_icon, nameof(symbol)); From 6ef552c6cf3c751e4ad7f05d56c1018107ef43ea Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Wed, 26 Jul 2023 21:02:24 +0300 Subject: [PATCH 039/508] Better handle preprocessing symbol visits --- .../CSharp/Portable/SymbolDisplay/SymbolDisplay.cs | 2 +- .../Portable/Symbols/PublicModel/PreprocessingSymbol.cs | 6 +++--- src/Compilers/Core/Portable/Symbols/SymbolVisitor.cs | 5 +++++ src/Compilers/Core/Portable/Symbols/SymbolVisitor`1.cs | 5 +++++ src/Compilers/Core/Portable/Symbols/SymbolVisitor`2.cs | 5 +++++ .../VisualBasic/Portable/SymbolDisplay/SymbolDisplay.vb | 2 +- .../VisualBasic/Portable/Symbols/PreprocessingSymbol.vb | 6 +++--- 7 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplay.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplay.cs index bdb7bf40481bd..f454f7ec0fed4 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplay.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplay.cs @@ -237,7 +237,7 @@ private static ImmutableArray ToDisplayParts( var builder = ArrayBuilder.GetInstance(); var visitor = new SymbolDisplayVisitor(builder, format, semanticModelOpt, positionOpt); - visitor.VisitSymbol(symbol); + symbol.Accept(visitor); return builder.ToImmutableAndFree(); } diff --git a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs index 0d9ec4b84d04f..d892d07120779 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs @@ -66,11 +66,11 @@ bool ISymbol.Equals(ISymbol? other, CodeAnalysis.SymbolEqualityComparer equality Accessibility ISymbol.DeclaredAccessibility => Accessibility.NotApplicable; - void ISymbol.Accept(SymbolVisitor visitor) => throw new NotSupportedException(); + void ISymbol.Accept(SymbolVisitor visitor) => visitor.VisitPreprocessing(this); - TResult ISymbol.Accept(SymbolVisitor visitor) => throw new NotSupportedException(); + TResult ISymbol.Accept(SymbolVisitor visitor) => visitor.VisitPreprocessing(this)!; - TResult ISymbol.Accept(SymbolVisitor visitor, TArgument argument) => throw new NotSupportedException(); + TResult ISymbol.Accept(SymbolVisitor visitor, TArgument argument) => visitor.VisitPreprocessing(this, argument); string? ISymbol.GetDocumentationCommentId() => null; diff --git a/src/Compilers/Core/Portable/Symbols/SymbolVisitor.cs b/src/Compilers/Core/Portable/Symbols/SymbolVisitor.cs index 0516496a3a2f0..d32a133d8c310 100644 --- a/src/Compilers/Core/Portable/Symbols/SymbolVisitor.cs +++ b/src/Compilers/Core/Portable/Symbols/SymbolVisitor.cs @@ -109,5 +109,10 @@ public virtual void VisitTypeParameter(ITypeParameterSymbol symbol) { DefaultVisit(symbol); } + + internal virtual void VisitPreprocessing(IPreprocessingSymbol symbol) + { + DefaultVisit(symbol); + } } } diff --git a/src/Compilers/Core/Portable/Symbols/SymbolVisitor`1.cs b/src/Compilers/Core/Portable/Symbols/SymbolVisitor`1.cs index 119e8be223cdf..212da5801cfe0 100644 --- a/src/Compilers/Core/Portable/Symbols/SymbolVisitor`1.cs +++ b/src/Compilers/Core/Portable/Symbols/SymbolVisitor`1.cs @@ -112,5 +112,10 @@ public abstract class SymbolVisitor { return DefaultVisit(symbol); } + + internal virtual TResult? VisitPreprocessing(IPreprocessingSymbol symbol) + { + return DefaultVisit(symbol); + } } } diff --git a/src/Compilers/Core/Portable/Symbols/SymbolVisitor`2.cs b/src/Compilers/Core/Portable/Symbols/SymbolVisitor`2.cs index ea9d2eba5e8fd..46681a1e8ca3c 100644 --- a/src/Compilers/Core/Portable/Symbols/SymbolVisitor`2.cs +++ b/src/Compilers/Core/Portable/Symbols/SymbolVisitor`2.cs @@ -117,5 +117,10 @@ public virtual TResult VisitTypeParameter(ITypeParameterSymbol symbol, TArgument { return DefaultVisit(symbol, argument); } + + internal virtual TResult VisitPreprocessing(IPreprocessingSymbol symbol, TArgument argument) + { + return DefaultVisit(symbol, argument); + } } } diff --git a/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplay.vb b/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplay.vb index 46c0f921bb46d..d171bd0770539 100644 --- a/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplay.vb +++ b/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplay.vb @@ -105,7 +105,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Dim builder = ArrayBuilder(Of SymbolDisplayPart).GetInstance() Dim visitor = New SymbolDisplayVisitor(builder, format, semanticModelOpt, positionOpt) - visitor.VisitSymbol(symbol) + symbol.Accept(visitor) Return builder.ToImmutableAndFree() End Function diff --git a/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb index c288bda69783a..8bb0639df539e 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb @@ -146,7 +146,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Function Public Overloads Overrides Sub Accept(visitor As SymbolVisitor) - Throw New NotSupportedException() + visitor.VisitPreprocessing(Me) End Sub Public Overloads Overrides Sub Accept(visitor As VisualBasicSymbolVisitor) @@ -154,11 +154,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Sub Public Overloads Overrides Function Accept(Of TResult)(visitor As SymbolVisitor(Of TResult)) As TResult - Throw New NotSupportedException() + Return visitor.VisitPreprocessing(Me) End Function Public Overrides Function Accept(Of TArgument, TResult)(visitor As SymbolVisitor(Of TArgument, TResult), argument As TArgument) As TResult - Throw New NotSupportedException() + Return visitor.VisitPreprocessing(Me, argument) End Function Public Overloads Overrides Function Accept(Of TResult)(visitor As VisualBasicSymbolVisitor(Of TResult)) As TResult From 0fe7680ba0efe04e320d79d8ae482a6d7f937928 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Wed, 26 Jul 2023 21:19:39 +0300 Subject: [PATCH 040/508] Improve LSP support on preprocessing symbols --- .../Handler/SemanticTokens/CustomLspSemanticTokenNames.cs | 2 ++ .../Protocol/Handler/SemanticTokens/SemanticTokensSchema.cs | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/CustomLspSemanticTokenNames.cs b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/CustomLspSemanticTokenNames.cs index e0b62e91f7974..8f9810ffd646a 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/CustomLspSemanticTokenNames.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/CustomLspSemanticTokenNames.cs @@ -37,6 +37,7 @@ internal class CustomLspSemanticTokenNames public const string FieldName = "field"; public const string ConstantName = "constant"; public const string ExtensionMethodName = "extensionMethod"; + public const string PreprocessingName = "preprocessingName"; public const string XmlDocCommentAttributeName = "xmlDocCommentAttributeName"; public const string XmlDocCommentAttributeQuotes = "xmlDocCommentAttributeQuotes"; @@ -101,6 +102,7 @@ internal class CustomLspSemanticTokenNames [ClassificationTypeNames.FieldName] = FieldName, [ClassificationTypeNames.ConstantName] = ConstantName, [ClassificationTypeNames.ExtensionMethodName] = ExtensionMethodName, + [ClassificationTypeNames.PreprocessingName] = PreprocessingName, [ClassificationTypeNames.XmlDocCommentAttributeName] = XmlDocCommentAttributeName, [ClassificationTypeNames.XmlDocCommentAttributeQuotes] = XmlDocCommentAttributeQuotes, diff --git a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensSchema.cs b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensSchema.cs index 3743aa5c8b532..4265be9758086 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensSchema.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensSchema.cs @@ -52,7 +52,6 @@ internal readonly struct SemanticTokensSchema [ClassificationTypeNames.PreprocessorKeyword] = SemanticTokenTypes.Macro, // in https://code.visualstudio.com/api/language-extensions/semantic-highlight-guide#standard-token-types-and-modifiers [ClassificationTypeNames.LabelName] = "label", - [ClassificationTypeNames.PreprocessingName] = "label", }).ToImmutableDictionary(); /// From d9a4dc0ecb79d74a8c34a158e7dcfaa458f8c47b Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Wed, 26 Jul 2023 21:41:26 +0300 Subject: [PATCH 041/508] Use the new VisitPreprocessing methods --- .../SymbolDisplay/SymbolDisplayVisitor_Misc.cs | 16 +++------------- .../SymbolDisplay/SymbolDisplayVisitor_Misc.vb | 16 +++------------- 2 files changed, 6 insertions(+), 26 deletions(-) diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.cs index f6e38a0ea39bb..471f629455686 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.cs @@ -8,20 +8,10 @@ namespace Microsoft.CodeAnalysis.CSharp { internal partial class SymbolDisplayVisitor { - /// - /// Visits a symbol, and specifically handles symbol types that do not support visiting. - /// - /// The symbol to visit. - public void VisitSymbol(ISymbol symbol) + internal override void VisitPreprocessing(IPreprocessingSymbol symbol) { - if (symbol is IPreprocessingSymbol preprocessingSymbol) - { - var part = new SymbolDisplayPart(SymbolDisplayPartKind.PreprocessingName, preprocessingSymbol, preprocessingSymbol.Name); - builder.Add(part); - return; - } - - symbol.Accept(this); + var part = new SymbolDisplayPart(SymbolDisplayPartKind.PreprocessingName, symbol, symbol.Name); + builder.Add(part); } } } diff --git a/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.vb b/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.vb index 2daf9865cd7e2..2c8dfc4cc1ee1 100644 --- a/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.vb +++ b/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.vb @@ -4,19 +4,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Partial Friend Class SymbolDisplayVisitor - ''' - ''' Visits a symbol, and specifically handles symbol types that do not support visiting. - ''' - ''' The symbol to visit. - Public Sub VisitSymbol(symbol As ISymbol) - Dim preprocessingSymbol = TryCast(symbol, IPreprocessingSymbol) - If preprocessingSymbol IsNot Nothing Then - Dim part = New SymbolDisplayPart(SymbolDisplayPartKind.PreprocessingName, preprocessingSymbol, preprocessingSymbol.Name) - builder.Add(part) - Return - End If - - symbol.Accept(Me) + Friend Overrides Sub VisitPreprocessing(symbol As IPreprocessingSymbol) + Dim part = New SymbolDisplayPart(SymbolDisplayPartKind.PreprocessingName, symbol, symbol.Name) + builder.Add(part) End Sub End Class End Namespace From 0b5d451fe49e3e4a5f6bf93a081b7664e96d24af Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Thu, 27 Jul 2023 02:36:44 +0300 Subject: [PATCH 042/508] Fix JSON output format test cases --- src/Features/Lsif/GeneratorTest/OutputFormatTests.vb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Features/Lsif/GeneratorTest/OutputFormatTests.vb b/src/Features/Lsif/GeneratorTest/OutputFormatTests.vb index 3b8b862348b28..0ad383801594b 100644 --- a/src/Features/Lsif/GeneratorTest/OutputFormatTests.vb +++ b/src/Features/Lsif/GeneratorTest/OutputFormatTests.vb @@ -25,7 +25,7 @@ Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests , openDocuments:=False, composition:=TestLsifOutput.TestComposition), jsonWriter) AssertEx.EqualOrDiff( -"{""hoverProvider"":true,""declarationProvider"":false,""definitionProvider"":true,""referencesProvider"":true,""typeDefinitionProvider"":false,""documentSymbolProvider"":false,""foldingRangeProvider"":true,""diagnosticProvider"":false,""semanticTokensProvider"":{""tokenTypes"":[""namespace"",""type"",""class"",""enum"",""interface"",""struct"",""typeParameter"",""parameter"",""variable"",""property"",""enumMember"",""event"",""function"",""method"",""macro"",""keyword"",""modifier"",""comment"",""string"",""number"",""regexp"",""operator"",""class name"",""constant name"",""delegate name"",""enum member name"",""enum name"",""event name"",""excluded code"",""extension method name"",""field name"",""interface name"",""json - array"",""json - comment"",""json - constructor name"",""json - keyword"",""json - number"",""json - object"",""json - operator"",""json - property name"",""json - punctuation"",""json - string"",""json - text"",""keyword - control"",""label name"",""local name"",""method name"",""module name"",""namespace name"",""operator - overloaded"",""parameter name"",""preprocessor keyword"",""preprocessor text"",""property name"",""punctuation"",""record class name"",""record struct name"",""regex - alternation"",""regex - anchor"",""regex - character class"",""regex - comment"",""regex - grouping"",""regex - other escape"",""regex - quantifier"",""regex - self escaped character"",""regex - text"",""string - escape character"",""string - verbatim"",""struct name"",""text"",""type parameter name"",""whitespace"",""xml doc comment - attribute name"",""xml doc comment - attribute quotes"",""xml doc comment - attribute value"",""xml doc comment - cdata section"",""xml doc comment - comment"",""xml doc comment - delimiter"",""xml doc comment - entity reference"",""xml doc comment - name"",""xml doc comment - processing instruction"",""xml doc comment - text"",""xml literal - attribute name"",""xml literal - attribute quotes"",""xml literal - attribute value"",""xml literal - cdata section"",""xml literal - comment"",""xml literal - delimiter"",""xml literal - embedded expression"",""xml literal - entity reference"",""xml literal - name"",""xml literal - processing instruction"",""xml literal - text""],""tokenModifiers"":[""static""]},""id"":1,""type"":""vertex"",""label"":""capabilities""} +"{""hoverProvider"":true,""declarationProvider"":false,""definitionProvider"":true,""referencesProvider"":true,""typeDefinitionProvider"":false,""documentSymbolProvider"":false,""foldingRangeProvider"":true,""diagnosticProvider"":false,""semanticTokensProvider"":{""tokenTypes"":[""namespace"",""type"",""class"",""enum"",""interface"",""struct"",""typeParameter"",""parameter"",""variable"",""property"",""enumMember"",""event"",""function"",""method"",""macro"",""keyword"",""modifier"",""comment"",""string"",""number"",""regexp"",""operator"",""class name"",""constant name"",""delegate name"",""enum member name"",""enum name"",""event name"",""excluded code"",""extension method name"",""field name"",""interface name"",""json - array"",""json - comment"",""json - constructor name"",""json - keyword"",""json - number"",""json - object"",""json - operator"",""json - property name"",""json - punctuation"",""json - string"",""json - text"",""keyword - control"",""label name"",""local name"",""method name"",""module name"",""namespace name"",""operator - overloaded"",""parameter name"",""preprocessing name"",""preprocessor keyword"",""preprocessor text"",""property name"",""punctuation"",""record class name"",""record struct name"",""regex - alternation"",""regex - anchor"",""regex - character class"",""regex - comment"",""regex - grouping"",""regex - other escape"",""regex - quantifier"",""regex - self escaped character"",""regex - text"",""string - escape character"",""string - verbatim"",""struct name"",""text"",""type parameter name"",""whitespace"",""xml doc comment - attribute name"",""xml doc comment - attribute quotes"",""xml doc comment - attribute value"",""xml doc comment - cdata section"",""xml doc comment - comment"",""xml doc comment - delimiter"",""xml doc comment - entity reference"",""xml doc comment - name"",""xml doc comment - processing instruction"",""xml doc comment - text"",""xml literal - attribute name"",""xml literal - attribute quotes"",""xml literal - attribute value"",""xml literal - cdata section"",""xml literal - comment"",""xml literal - delimiter"",""xml literal - embedded expression"",""xml literal - entity reference"",""xml literal - name"",""xml literal - processing instruction"",""xml literal - text""],""tokenModifiers"":[""static""]},""id"":1,""type"":""vertex"",""label"":""capabilities""} {""kind"":""csharp"",""resource"":""file:///Z:/TestProject.csproj"",""name"":""TestProject"",""id"":2,""type"":""vertex"",""label"":""project""} {""kind"":""begin"",""scope"":""project"",""data"":2,""id"":3,""type"":""vertex"",""label"":""$event""} {""uri"":""file:///Z:/A.cs"",""languageId"":""csharp"",""id"":4,""type"":""vertex"",""label"":""document""} @@ -119,6 +119,7 @@ Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests ""namespace name"", ""operator - overloaded"", ""parameter name"", + ""preprocessing name"", ""preprocessor keyword"", ""preprocessor text"", ""property name"", From c93befc49e2d93025978e4699297b0bebf0fcb58 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Thu, 27 Jul 2023 02:47:34 +0300 Subject: [PATCH 043/508] Fix SemanticTokensData --- src/Features/Lsif/GeneratorTest/SemanticTokensTests.vb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Features/Lsif/GeneratorTest/SemanticTokensTests.vb b/src/Features/Lsif/GeneratorTest/SemanticTokensTests.vb index 53b288e84fee7..26a8676d9b25c 100644 --- a/src/Features/Lsif/GeneratorTest/SemanticTokensTests.vb +++ b/src/Features/Lsif/GeneratorTest/SemanticTokensTests.vb @@ -16,9 +16,9 @@ Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests - - - + + + Public Async Function TestSemanticTokensData(code As String, expectedTokens As String) As Task ' This test performs LSIF specific validation of the semantic tokens output. As of this writing ' this feature is based on the same code path used to generate semantic tokens information in LSP From 5f480dd3832eaaee527571c7a11edde4d39ea154 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Thu, 27 Jul 2023 21:53:09 +0300 Subject: [PATCH 044/508] Nits --- .../Compilation/CSharpSemanticModel.cs | 28 +++------- .../SymbolDisplay/SymbolDisplayVisitor.cs | 6 +++ .../SymbolDisplayVisitor_Misc.cs | 17 ------ .../Portable/Compilation/SemanticModel.vb | 4 +- .../SymbolDisplay/SymbolDisplayVisitor.vb | 5 ++ .../SymbolDisplayVisitor_Misc.vb | 12 ----- .../Portable/Syntax/VisualBasicSyntaxTree.vb | 10 ++-- .../Portable/VisualBasicExtensions.vb | 10 +--- .../CSharp/Test/F1Help/F1HelpTests.cs | 3 +- .../PreprocessingSymbolReferenceFinder.cs | 52 ++++++++++--------- 10 files changed, 52 insertions(+), 95 deletions(-) delete mode 100644 src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.cs delete mode 100644 src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.vb diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index 50a1525ddb471..05c0096f5f340 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -4857,34 +4857,20 @@ public PreprocessingSymbolInfo GetPreprocessingSymbolInfo(IdentifierNameSyntax n return PreprocessingSymbolInfo.None; } - /// - /// Gets the preprocessing symbol info for the preprocessing symbol defined in the #define directive. - /// - /// A #define directive trivia node. - private PreprocessingSymbolInfo GetPreprocessingSymbolInfo(DefineDirectiveTriviaSyntax node) - { - CheckSyntaxNode(node); - return CreatePreprocessingSymbolInfo(node.Name); - } - - /// - /// Gets the preprocessing symbol info for the preprocessing symbol undefined in the #undef directive. - /// - /// An #undef directive trivia node. - private PreprocessingSymbolInfo GetPreprocessingSymbolInfo(UndefDirectiveTriviaSyntax node) + private PreprocessingSymbolInfo GetPreprocessingSymbolInfo(DirectiveTriviaSyntax node, SyntaxToken name) { CheckSyntaxNode(node); - return CreatePreprocessingSymbolInfo(node.Name); + return CreatePreprocessingSymbolInfo(name); } - private PreprocessingSymbolInfo CreatePreprocessingSymbolInfo(in SyntaxToken identifier) + private PreprocessingSymbolInfo CreatePreprocessingSymbolInfo(SyntaxToken identifier) { bool isDefined = SyntaxTree.IsPreprocessorSymbolDefined(identifier.ValueText, identifier.SpanStart); var preprocessingSymbol = CreatePreprocessingSymbol(identifier); - return new(preprocessingSymbol, isDefined); + return new PreprocessingSymbolInfo(preprocessingSymbol, isDefined); } - private Symbols.PublicModel.PreprocessingSymbol CreatePreprocessingSymbol(in SyntaxToken identifier) + private Symbols.PublicModel.PreprocessingSymbol CreatePreprocessingSymbol(SyntaxToken identifier) { return new Symbols.PublicModel.PreprocessingSymbol( identifier.ValueText, @@ -5105,8 +5091,8 @@ protected sealed override PreprocessingSymbolInfo GetPreprocessingSymbolInfoCore return node switch { IdentifierNameSyntax nameSyntax => GetPreprocessingSymbolInfo(nameSyntax), - DefineDirectiveTriviaSyntax defineSyntax => GetPreprocessingSymbolInfo(defineSyntax), - UndefDirectiveTriviaSyntax undefSyntax => GetPreprocessingSymbolInfo(undefSyntax), + DefineDirectiveTriviaSyntax defineSyntax => GetPreprocessingSymbolInfo(defineSyntax, defineSyntax.Name), + UndefDirectiveTriviaSyntax undefSyntax => GetPreprocessingSymbolInfo(undefSyntax, undefSyntax.Name), _ => PreprocessingSymbolInfo.None }; } diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs index 823daf6d2f694..c5ff59dbde6ca 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs @@ -290,6 +290,12 @@ public override void VisitAlias(IAliasSymbol symbol) } } + internal override void VisitPreprocessing(IPreprocessingSymbol symbol) + { + var part = new SymbolDisplayPart(SymbolDisplayPartKind.PreprocessingName, symbol, symbol.Name); + builder.Add(part); + } + protected override void AddSpace() { builder.Add(CreatePart(SymbolDisplayPartKind.Space, null, " ")); diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.cs deleted file mode 100644 index 471f629455686..0000000000000 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable disable - -namespace Microsoft.CodeAnalysis.CSharp -{ - internal partial class SymbolDisplayVisitor - { - internal override void VisitPreprocessing(IPreprocessingSymbol symbol) - { - var part = new SymbolDisplayPart(SymbolDisplayPartKind.PreprocessingName, symbol, symbol.Name); - builder.Add(part); - } - } -} diff --git a/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb b/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb index d1a5676cfbd26..333b589941b37 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb @@ -2920,7 +2920,6 @@ _Default: ''' ''' Preprocessing symbol syntax token. Public Shadows Function GetPreprocessingSymbolInfo(token As SyntaxToken) As VisualBasicPreprocessingSymbolInfo - Dim parent = DirectCast(token.Parent, VisualBasicSyntaxNode) CheckSyntaxNode(parent) @@ -2946,8 +2945,7 @@ _Default: CheckSyntaxNode(node) If SyntaxFacts.IsWithinPreprocessorConditionalExpression(node) Then - Dim symbolInfo As VisualBasicPreprocessingSymbolInfo = node.SyntaxTree.GetPreprocessingSymbolInfo(node) - + Dim symbolInfo As VisualBasicPreprocessingSymbolInfo = node.SyntaxTree.GetPreprocessingSymbolInfo(node.Identifier) Return RetrieveOrConstructPreprocessingSymbolInfo(symbolInfo, node.Identifier) End If diff --git a/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.vb b/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.vb index 8b0791c7c5be7..45519f9421d4a 100644 --- a/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.vb +++ b/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.vb @@ -289,6 +289,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End If End Sub + Friend Overrides Sub VisitPreprocessing(symbol As IPreprocessingSymbol) + Dim part = New SymbolDisplayPart(SymbolDisplayPartKind.PreprocessingName, symbol, symbol.Name) + builder.Add(part) + End Sub + Protected Overrides Sub AddSpace() builder.Add(CreatePart(SymbolDisplayPartKind.Space, Nothing, " ", False)) End Sub diff --git a/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.vb b/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.vb deleted file mode 100644 index 2c8dfc4cc1ee1..0000000000000 --- a/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor_Misc.vb +++ /dev/null @@ -1,12 +0,0 @@ -' Licensed to the .NET Foundation under one or more agreements. -' The .NET Foundation licenses this file to you under the MIT license. -' See the LICENSE file in the project root for more information. - -Namespace Microsoft.CodeAnalysis.VisualBasic - Partial Friend Class SymbolDisplayVisitor - Friend Overrides Sub VisitPreprocessing(symbol As IPreprocessingSymbol) - Dim part = New SymbolDisplayPart(SymbolDisplayPartKind.PreprocessingName, symbol, symbol.Name) - builder.Add(part) - End Sub - End Class -End Namespace diff --git a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.vb b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.vb index 96b9970df3dcc..5b1c4fa94d5a5 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.vb @@ -597,15 +597,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return False End Function - Friend Function GetPreprocessingSymbolInfo(identifierNode As IdentifierNameSyntax) As VisualBasicPreprocessingSymbolInfo - Return GetPreprocessingSymbolInfo(identifierNode.Identifier) - End Function - - Friend Function GetPreprocessingSymbolInfo(identifierNode As SyntaxToken) As VisualBasicPreprocessingSymbolInfo - Dim conditionalSymbolName As String = identifierNode.ValueText + Friend Function GetPreprocessingSymbolInfo(token As SyntaxToken) As VisualBasicPreprocessingSymbolInfo + Dim conditionalSymbolName As String = token.ValueText Dim conditionalSymbols As ConditionalSymbolsMap = Me.ConditionalSymbols - Return If(conditionalSymbols Is Nothing, VisualBasicPreprocessingSymbolInfo.None, conditionalSymbols.GetPreprocessingSymbolInfo(conditionalSymbolName, identifierNode)) + Return If(conditionalSymbols Is Nothing, VisualBasicPreprocessingSymbolInfo.None, conditionalSymbols.GetPreprocessingSymbolInfo(conditionalSymbolName, token)) End Function #End Region diff --git a/src/Compilers/VisualBasic/Portable/VisualBasicExtensions.vb b/src/Compilers/VisualBasic/Portable/VisualBasicExtensions.vb index 194196ec8c056..3a01c14107e49 100644 --- a/src/Compilers/VisualBasic/Portable/VisualBasicExtensions.vb +++ b/src/Compilers/VisualBasic/Portable/VisualBasicExtensions.vb @@ -261,15 +261,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function - Friend Function GetPreprocessingSymbolInfo(syntaxTree As SyntaxTree, identifierNode As IdentifierNameSyntax) As VisualBasicPreprocessingSymbolInfo + Friend Function GetPreprocessingSymbolInfo(syntaxTree As SyntaxTree, token As SyntaxToken) As VisualBasicPreprocessingSymbolInfo Dim vbTree = DirectCast(syntaxTree, VisualBasicSyntaxTree) - Return vbTree.GetPreprocessingSymbolInfo(identifierNode) - End Function - - - Friend Function GetPreprocessingSymbolInfo(syntaxTree As SyntaxTree, identifierNode As SyntaxToken) As VisualBasicPreprocessingSymbolInfo - Dim vbTree = DirectCast(syntaxTree, VisualBasicSyntaxTree) - Return vbTree.GetPreprocessingSymbolInfo(identifierNode) + Return vbTree.GetPreprocessingSymbolInfo(token) End Function diff --git a/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs b/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs index 4eb9ef74fdf97..79a7e136ad0c9 100644 --- a/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs +++ b/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs @@ -1586,8 +1586,7 @@ void M() }", "discard"); } - [WorkItem("https://github.com/dotnet/roslyn/issues/66009")] - [Fact] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] public async Task TestNotFound_PreprocessingSymbol() { await TestAsync( diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs index a69457a7a597f..e6d5a522c101e 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs @@ -2,19 +2,17 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; +using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.FindSymbols.Finders; -internal class PreprocessingSymbolReferenceFinder : AbstractReferenceFinder +internal sealed class PreprocessingSymbolReferenceFinder : AbstractReferenceFinder { protected override bool CanFind(IPreprocessingSymbol symbol) => true; @@ -50,19 +48,32 @@ protected override async Task> DetermineDocumentsToSear // configured in the project configuration with the same shared identifier. var solution = project.Solution; - foreach (var solutionProject in solution.Projects) + var sourceDocuments = (IEnumerable?)documents + ?? await GetAllSolutionDocumentsAsync(solution, cancellationToken).ConfigureAwait(false); + + foreach (var document in sourceDocuments) { - foreach (var document in await solutionProject.GetAllRegularAndSourceGeneratedDocumentsAsync(cancellationToken).ConfigureAwait(false)) - { - var syntaxTreeIndex = await document.GetSyntaxTreeIndexAsync(cancellationToken).ConfigureAwait(false); - if (syntaxTreeIndex.ContainsDirective && syntaxTreeIndex.ProbablyContainsIdentifier(symbol.Name)) - resultDocuments.Add(document); - } + var syntaxTreeIndex = await document.GetSyntaxTreeIndexAsync(cancellationToken).ConfigureAwait(false); + if (syntaxTreeIndex.ContainsDirective && syntaxTreeIndex.ProbablyContainsIdentifier(symbol.Name)) + resultDocuments.Add(document); } return resultDocuments.ToImmutable(); } + private async ValueTask> GetAllSolutionDocumentsAsync(Solution solution, CancellationToken cancellationToken) + { + var documents = Enumerable.Empty(); + + foreach (var solutionProject in solution.Projects) + { + var projectDocuments = await solutionProject.GetAllRegularAndSourceGeneratedDocumentsAsync(cancellationToken).ConfigureAwait(false); + documents = documents.Concat(projectDocuments); + } + + return documents; + } + private static async ValueTask> FindPreprocessingReferencesInTokensAsync( IPreprocessingSymbol symbol, FindReferencesDocumentState state, @@ -74,31 +85,22 @@ private static async ValueTask> FindPreprocessing { cancellationToken.ThrowIfCancellationRequested(); - var matched = await PreprocessingSymbolMatchesAsync( - symbol, state, token, cancellationToken).ConfigureAwait(false); + var matched = await PreprocessingSymbolsMatchAsync(symbol, state, token, cancellationToken) + .ConfigureAwait(false); + if (matched) { - var finderLocation = CreateFinderLocation(state, token, CandidateReason.None, cancellationToken); - - locations.Add(finderLocation); + locations.Add(CreateFinderLocation(state, token, CandidateReason.None, cancellationToken)); } } return locations.ToImmutable(); } - private static async ValueTask PreprocessingSymbolMatchesAsync(ISymbol symbol, FindReferencesDocumentState state, SyntaxToken token, CancellationToken cancellationToken) - { - var preprocessingSearchSymbol = symbol as IPreprocessingSymbol; - Debug.Assert(preprocessingSearchSymbol is not null); - - return await PreprocessingSymbolsMatchAsync(preprocessingSearchSymbol, state, token, cancellationToken).ConfigureAwait(false); - } - private static async ValueTask PreprocessingSymbolsMatchAsync( IPreprocessingSymbol searchSymbol, FindReferencesDocumentState state, SyntaxToken token, CancellationToken cancellationToken) { - var symbol = state.SemanticModel.GetPreprocessingSymbolInfo(token.Parent!).Symbol; + var symbol = state.SemanticModel.GetPreprocessingSymbolInfo(token.GetRequiredParent()).Symbol; return await SymbolFinder.OriginalSymbolsMatchAsync(state.Solution, searchSymbol, symbol, cancellationToken).ConfigureAwait(false); } } From b13573ed4e7787b8a8c4f6103e8a3ee8ae8847b3 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Thu, 27 Jul 2023 22:08:51 +0300 Subject: [PATCH 045/508] Make static --- .../Finders/PreprocessingSymbolReferenceFinder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs index e6d5a522c101e..3a112997cf120 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs @@ -61,7 +61,7 @@ protected override async Task> DetermineDocumentsToSear return resultDocuments.ToImmutable(); } - private async ValueTask> GetAllSolutionDocumentsAsync(Solution solution, CancellationToken cancellationToken) + private static async ValueTask> GetAllSolutionDocumentsAsync(Solution solution, CancellationToken cancellationToken) { var documents = Enumerable.Empty(); From c5704f349d6d9f406f72b66cf13b941fa9e585f5 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Sun, 10 Sep 2023 18:27:17 +0300 Subject: [PATCH 046/508] Adjust implementation around document filtering --- .../Portable/Compilation/CSharpCompilation.cs | 2 +- .../Compilation/CSharpSemanticModel.cs | 10 +----- .../PublicModel/PreprocessingSymbol.cs | 10 ++---- .../Portable/Compilation/SemanticModel.vb | 2 +- .../Compilation/VisualBasicCompilation.vb | 2 +- .../Portable/Symbols/PreprocessingSymbol.vb | 33 ------------------- .../FindReferences/DependentProjectsFinder.cs | 5 +-- .../PreprocessingSymbolReferenceFinder.cs | 25 ++------------ 8 files changed, 13 insertions(+), 76 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index 53e86fe5bd317..371d53de370fe 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -325,7 +325,7 @@ protected override INamespaceSymbol CommonCreateErrorNamespaceSymbol(INamespaceS protected override IPreprocessingSymbol CommonCreatePreprocessingSymbol(string name) { - return new Symbols.PublicModel.PreprocessingSymbol(name, CommonAssembly, CommonSourceModule); + return new Symbols.PublicModel.PreprocessingSymbol(name); } #region Constructors and Factories diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index 05c0096f5f340..cde4217efe3be 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -4866,18 +4866,10 @@ private PreprocessingSymbolInfo GetPreprocessingSymbolInfo(DirectiveTriviaSyntax private PreprocessingSymbolInfo CreatePreprocessingSymbolInfo(SyntaxToken identifier) { bool isDefined = SyntaxTree.IsPreprocessorSymbolDefined(identifier.ValueText, identifier.SpanStart); - var preprocessingSymbol = CreatePreprocessingSymbol(identifier); + var preprocessingSymbol = new Symbols.PublicModel.PreprocessingSymbol(identifier.ValueText); return new PreprocessingSymbolInfo(preprocessingSymbol, isDefined); } - private Symbols.PublicModel.PreprocessingSymbol CreatePreprocessingSymbol(SyntaxToken identifier) - { - return new Symbols.PublicModel.PreprocessingSymbol( - identifier.ValueText, - Compilation.Assembly.GetPublicSymbol(), - Compilation.SourceModule.GetPublicSymbol()); - } - /// /// Options to control the internal working of GetSymbolInfoWorker. Not currently exposed /// to public clients, but could be if desired. diff --git a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs index d892d07120779..33c9d3cc31ddc 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs @@ -12,14 +12,10 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols.PublicModel internal sealed class PreprocessingSymbol : IPreprocessingSymbol { private readonly string _name; - private readonly IAssemblySymbol _assembly; - private readonly IModuleSymbol _module; - internal PreprocessingSymbol(string name, IAssemblySymbol assembly, IModuleSymbol module) + internal PreprocessingSymbol(string name) { _name = name; - _assembly = assembly; - _module = module; } ISymbol ISymbol.OriginalDefinition => this; @@ -106,9 +102,9 @@ ImmutableArray ISymbol.ToMinimalDisplayParts(SemanticModel se int ISymbol.MetadataToken => 0; - IAssemblySymbol? ISymbol.ContainingAssembly => _assembly; + IAssemblySymbol? ISymbol.ContainingAssembly => null; - IModuleSymbol? ISymbol.ContainingModule => _module; + IModuleSymbol? ISymbol.ContainingModule => null; INamespaceSymbol? ISymbol.ContainingNamespace => null; diff --git a/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb b/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb index 333b589941b37..3c53f26292705 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb @@ -2969,7 +2969,7 @@ _Default: Return symbolInfo End If - Return New VisualBasicPreprocessingSymbolInfo(New PreprocessingSymbol(token.ValueText, Compilation.Assembly, Compilation.SourceModule), constantValueOpt:=Nothing, isDefined:=False) + Return New VisualBasicPreprocessingSymbolInfo(New PreprocessingSymbol(token.ValueText), constantValueOpt:=Nothing, isDefined:=False) End Function diff --git a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb index d7574a1e6636e..6ca25a409aa31 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb @@ -2837,7 +2837,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function Protected Overrides Function CommonCreatePreprocessingSymbol(name As String) As IPreprocessingSymbol - Return New PreprocessingSymbol(name, CommonAssembly, CommonSourceModule) + Return New PreprocessingSymbol(name) End Function Protected Overrides Function CommonCreateArrayTypeSymbol(elementType As ITypeSymbol, rank As Integer, elementNullableAnnotation As NullableAnnotation) As IArrayTypeSymbol diff --git a/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb index 8bb0639df539e..dd56401af993f 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb @@ -13,18 +13,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Implements IPreprocessingSymbol Private ReadOnly _name As String - Private ReadOnly _assembly As IAssemblySymbol - Private ReadOnly _module As IModuleSymbol Friend Sub New(name As String) - Me.New(name, Nothing, Nothing) - End Sub - - Friend Sub New(name As String, assembly As IAssemblySymbol, moduleSymbol As IModuleSymbol) - MyBase.New() _name = name - _assembly = assembly - _module = moduleSymbol End Sub Public Overrides ReadOnly Property Name As String @@ -99,30 +90,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property - Public Overrides ReadOnly Property ContainingAssembly As AssemblySymbol - Get - Return TryCast(ISymbolContainingAssembly, AssemblySymbol) - End Get - End Property - - Friend Overloads ReadOnly Property ISymbolContainingAssembly As IAssemblySymbol Implements ISymbol.ContainingAssembly - Get - Return _assembly - End Get - End Property - - Public Overrides ReadOnly Property ContainingModule As ModuleSymbol - Get - Return TryCast(ISymbolContainingModule, ModuleSymbol) - End Get - End Property - - Friend Overloads ReadOnly Property ISymbolContainingModule As IModuleSymbol Implements ISymbol.ContainingModule - Get - Return _module - End Get - End Property - Public Overrides Function Equals(obj As Object) As Boolean If obj Is Me Then Return True diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs index 90bbb41b2005c..6d539d6c5987c 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs @@ -33,8 +33,9 @@ internal static partial class DependentProjectsFinder public static async Task> GetDependentProjectsAsync( Solution solution, ImmutableArray symbols, IImmutableSet projects, CancellationToken cancellationToken) { - // namespaces are visible in all projects. - if (symbols.Any(static s => s.Kind == SymbolKind.Namespace)) + // Namespaces are visible in all projects + // Preprocessing symbols are arbitrary identifiers that are not bound to specific projects + if (symbols.Any(static s => s.Kind is SymbolKind.Namespace or SymbolKind.Preprocessing)) return projects.ToImmutableArray(); var dependentProjects = await GetDependentProjectsWorkerAsync(solution, symbols, cancellationToken).ConfigureAwait(false); diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs index 3a112997cf120..ad3001fa4f9cd 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs @@ -42,16 +42,10 @@ protected override async Task> DetermineDocumentsToSear { using var _ = ArrayBuilder.GetInstance(out var resultDocuments); - // NOTE: We intentionally search for all documents in the entire solution. This is because - // the symbols are validly bound by their requested name, despite their current definition - // state. Therefore, the same symbol name could be shared across multiple projects and - // configured in the project configuration with the same shared identifier. + var projectDocuments = (IEnumerable?)documents + ?? await project.GetAllRegularAndSourceGeneratedDocumentsAsync(cancellationToken).ConfigureAwait(false); - var solution = project.Solution; - var sourceDocuments = (IEnumerable?)documents - ?? await GetAllSolutionDocumentsAsync(solution, cancellationToken).ConfigureAwait(false); - - foreach (var document in sourceDocuments) + foreach (var document in projectDocuments) { var syntaxTreeIndex = await document.GetSyntaxTreeIndexAsync(cancellationToken).ConfigureAwait(false); if (syntaxTreeIndex.ContainsDirective && syntaxTreeIndex.ProbablyContainsIdentifier(symbol.Name)) @@ -61,19 +55,6 @@ protected override async Task> DetermineDocumentsToSear return resultDocuments.ToImmutable(); } - private static async ValueTask> GetAllSolutionDocumentsAsync(Solution solution, CancellationToken cancellationToken) - { - var documents = Enumerable.Empty(); - - foreach (var solutionProject in solution.Projects) - { - var projectDocuments = await solutionProject.GetAllRegularAndSourceGeneratedDocumentsAsync(cancellationToken).ConfigureAwait(false); - documents = documents.Concat(projectDocuments); - } - - return documents; - } - private static async ValueTask> FindPreprocessingReferencesInTokensAsync( IPreprocessingSymbol symbol, FindReferencesDocumentState state, From 2c39677d24e35edd501897b4ba8527d7996aaab9 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Sun, 10 Sep 2023 18:35:34 +0300 Subject: [PATCH 047/508] Remove invalid comment --- .../Syntax/VisualBasicSyntaxTree.ConditionalSymbolsMap.vb | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.ConditionalSymbolsMap.vb b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.ConditionalSymbolsMap.vb index 00fbd1d991770..5a2d6a9677c28 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.ConditionalSymbolsMap.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.ConditionalSymbolsMap.vb @@ -157,8 +157,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ' Get symbol name at preprocessor definition, i.e. #Const directive. ' NOTE: symbolName and conditionalSymbolName might have different case, we want the definition name. Dim symbolName = _conditionalsMap.Keys.First(Function(key) IdentifierComparison.Equals(key, conditionalSymbolName)) - ' NOTE: Due to not having access to a semantic model when buidling this preprocessing symbol's info, - ' we allow only simply defining the symbol name, and ignoring its source module and assembly Return New VisualBasicPreprocessingSymbolInfo(New PreprocessingSymbol(name:=symbolName), constantValueOpt:=constValue.ValueAsObject, isDefined:=True) End Function From 95b556ac0c8d6c5588260fcb92c43ef6296235f2 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Mon, 11 Sep 2023 23:23:43 +0300 Subject: [PATCH 048/508] Remove new classification type --- .../CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs | 2 +- src/Compilers/Core/Portable/PublicAPI.Unshipped.txt | 1 - .../Core/Portable/SymbolDisplay/SymbolDisplayPartKind.cs | 6 ++---- .../Portable/SymbolDisplay/SymbolDisplayVisitor.vb | 2 +- .../Classification/FormattedClassifications.cs | 4 ---- .../Core/Portable/Common/SymbolDisplayPartKindTags.cs | 1 - src/Features/Core/Portable/Common/TaggedText.cs | 3 --- src/Features/Core/Portable/Common/TextTags.cs | 1 - src/Features/Core/Portable/PublicAPI.Unshipped.txt | 1 - .../Handler/SemanticTokens/CustomLspSemanticTokenNames.cs | 2 -- .../Core/Portable/Classification/ClassificationTypeNames.cs | 3 --- src/Workspaces/Core/Portable/Classification/Classifier.cs | 1 - 12 files changed, 4 insertions(+), 23 deletions(-) diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs index c5ff59dbde6ca..52b5e424f69c5 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs @@ -292,7 +292,7 @@ public override void VisitAlias(IAliasSymbol symbol) internal override void VisitPreprocessing(IPreprocessingSymbol symbol) { - var part = new SymbolDisplayPart(SymbolDisplayPartKind.PreprocessingName, symbol, symbol.Name); + var part = new SymbolDisplayPart(SymbolDisplayPartKind.Text, symbol, symbol.Name); builder.Add(part); } diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 9d240f0f5d119..0905478a21668 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -57,5 +57,4 @@ virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitInlineArrayAcces virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitInlineArrayAccess(Microsoft.CodeAnalysis.Operations.IInlineArrayAccessOperation! operation, TArgument argument) -> TResult? virtual Microsoft.CodeAnalysis.SyntaxContextReceiverCreator.Invoke() -> Microsoft.CodeAnalysis.ISyntaxContextReceiver? virtual Microsoft.CodeAnalysis.SyntaxReceiverCreator.Invoke() -> Microsoft.CodeAnalysis.ISyntaxReceiver! -Microsoft.CodeAnalysis.SymbolDisplayPartKind.PreprocessingName = 33 -> Microsoft.CodeAnalysis.SymbolDisplayPartKind Microsoft.CodeAnalysis.Compilation.CreatePreprocessingSymbol(string! name) -> Microsoft.CodeAnalysis.IPreprocessingSymbol! \ No newline at end of file diff --git a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayPartKind.cs b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayPartKind.cs index b64579895a159..d82214664fce4 100644 --- a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayPartKind.cs +++ b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayPartKind.cs @@ -89,13 +89,11 @@ public enum SymbolDisplayPartKind RecordClassName = 31, /// The name of a record struct. RecordStructName = 32, - /// The name of a preprocessing symbol. - PreprocessingName = 33, } internal static class InternalSymbolDisplayPartKind { - private const SymbolDisplayPartKind @base = SymbolDisplayPartKind.PreprocessingName + 1; + private const SymbolDisplayPartKind @base = SymbolDisplayPartKind.RecordStructName + 1; public const SymbolDisplayPartKind Arity = @base + 0; public const SymbolDisplayPartKind Other = @base + 1; } @@ -104,7 +102,7 @@ internal static partial class EnumBounds { internal static bool IsValid(this SymbolDisplayPartKind value) { - return (value >= SymbolDisplayPartKind.AliasName && value <= SymbolDisplayPartKind.PreprocessingName) || + return (value >= SymbolDisplayPartKind.AliasName && value <= SymbolDisplayPartKind.RecordStructName) || (value >= InternalSymbolDisplayPartKind.Arity && value <= InternalSymbolDisplayPartKind.Other); } } diff --git a/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.vb b/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.vb index 45519f9421d4a..8da048297204b 100644 --- a/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.vb +++ b/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.vb @@ -290,7 +290,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Sub Friend Overrides Sub VisitPreprocessing(symbol As IPreprocessingSymbol) - Dim part = New SymbolDisplayPart(SymbolDisplayPartKind.PreprocessingName, symbol, symbol.Name) + Dim part = New SymbolDisplayPart(SymbolDisplayPartKind.Text, symbol, symbol.Name) builder.Add(part) End Sub diff --git a/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.cs b/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.cs index 8aa5a364428a5..b962707454438 100644 --- a/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.cs +++ b/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.cs @@ -46,10 +46,6 @@ public static FormattedClassification Delegate(string text) public static FormattedClassification TypeParameter(string text) => New(text, ClassificationTypeNames.TypeParameterName); - [DebuggerStepThrough] - public static FormattedClassification Preprocessing(string text) - => New(text, ClassificationTypeNames.PreprocessingName); - [DebuggerStepThrough] public static FormattedClassification Namespace(string text) => New(text, ClassificationTypeNames.NamespaceName); diff --git a/src/Features/Core/Portable/Common/SymbolDisplayPartKindTags.cs b/src/Features/Core/Portable/Common/SymbolDisplayPartKindTags.cs index 3adf9576df0ba..58eaf80d1db8d 100644 --- a/src/Features/Core/Portable/Common/SymbolDisplayPartKindTags.cs +++ b/src/Features/Core/Portable/Common/SymbolDisplayPartKindTags.cs @@ -70,7 +70,6 @@ public static string GetTag(SymbolDisplayPartKind kind) SymbolDisplayPartKind.ConstantName => TextTags.Constant, SymbolDisplayPartKind.RecordClassName => TextTags.Record, SymbolDisplayPartKind.RecordStructName => TextTags.RecordStruct, - SymbolDisplayPartKind.PreprocessingName => TextTags.Preprocessing, _ => string.Empty, }; } diff --git a/src/Features/Core/Portable/Common/TaggedText.cs b/src/Features/Core/Portable/Common/TaggedText.cs index 8f82ddafa0dd6..408c67f0e8cbc 100644 --- a/src/Features/Core/Portable/Common/TaggedText.cs +++ b/src/Features/Core/Portable/Common/TaggedText.cs @@ -239,9 +239,6 @@ public static string ToClassificationTypeName(this string taggedTextTag) case TextTags.RecordStruct: return ClassificationTypeNames.RecordStructName; - case TextTags.Preprocessing: - return ClassificationTypeNames.PreprocessingName; - case TextTags.ContainerStart: case TextTags.ContainerEnd: case TextTags.CodeBlockStart: diff --git a/src/Features/Core/Portable/Common/TextTags.cs b/src/Features/Core/Portable/Common/TextTags.cs index dcb5d6bed1c62..ca2f1517116dc 100644 --- a/src/Features/Core/Portable/Common/TextTags.cs +++ b/src/Features/Core/Portable/Common/TextTags.cs @@ -45,7 +45,6 @@ public static class TextTags public const string Constant = nameof(Constant); public const string Record = nameof(Record); public const string RecordStruct = nameof(RecordStruct); - public const string Preprocessing = nameof(Preprocessing); /// /// Indicates the start of a text container. The elements after through (but not diff --git a/src/Features/Core/Portable/PublicAPI.Unshipped.txt b/src/Features/Core/Portable/PublicAPI.Unshipped.txt index 78b13a2e57142..5177098e215f3 100644 --- a/src/Features/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Features/Core/Portable/PublicAPI.Unshipped.txt @@ -1,4 +1,3 @@ -const Microsoft.CodeAnalysis.TextTags.Preprocessing = "Preprocessing" -> string Microsoft.CodeAnalysis.TaggedText.Equals(Microsoft.CodeAnalysis.TaggedText other) -> bool override Microsoft.CodeAnalysis.TaggedText.Equals(object obj) -> bool override Microsoft.CodeAnalysis.TaggedText.GetHashCode() -> int diff --git a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/CustomLspSemanticTokenNames.cs b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/CustomLspSemanticTokenNames.cs index 67c3ef21c20ad..f83adf181168e 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/CustomLspSemanticTokenNames.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/CustomLspSemanticTokenNames.cs @@ -33,7 +33,6 @@ internal class CustomLspSemanticTokenNames public const string FieldName = "field"; public const string ConstantName = "constant"; public const string ExtensionMethodName = "extensionMethod"; - public const string PreprocessingName = "preprocessingName"; public const string XmlDocCommentAttributeName = "xmlDocCommentAttributeName"; public const string XmlDocCommentAttributeQuotes = "xmlDocCommentAttributeQuotes"; @@ -100,7 +99,6 @@ internal class CustomLspSemanticTokenNames [ClassificationTypeNames.FieldName] = FieldName, [ClassificationTypeNames.ConstantName] = ConstantName, [ClassificationTypeNames.ExtensionMethodName] = ExtensionMethodName, - [ClassificationTypeNames.PreprocessingName] = PreprocessingName, [ClassificationTypeNames.XmlDocCommentAttributeName] = XmlDocCommentAttributeName, [ClassificationTypeNames.XmlDocCommentAttributeQuotes] = XmlDocCommentAttributeQuotes, diff --git a/src/Workspaces/Core/Portable/Classification/ClassificationTypeNames.cs b/src/Workspaces/Core/Portable/Classification/ClassificationTypeNames.cs index 540f4ac485ad4..a4c602e689a18 100644 --- a/src/Workspaces/Core/Portable/Classification/ClassificationTypeNames.cs +++ b/src/Workspaces/Core/Portable/Classification/ClassificationTypeNames.cs @@ -41,7 +41,6 @@ public static class ClassificationTypeNames StructName, RecordStructName, TypeParameterName, - PreprocessingName, FieldName, EnumMemberName, ConstantName, @@ -127,8 +126,6 @@ public static class ClassificationTypeNames public const string StructName = "struct name"; public const string RecordStructName = "record struct name"; public const string TypeParameterName = "type parameter name"; - // NOTE: This is internal until the classification type is formally supported - internal const string PreprocessingName = "preprocessing name"; internal const string TestCode = "roslyn test code"; internal const string TestCodeMarkdown = "roslyn test code markdown"; diff --git a/src/Workspaces/Core/Portable/Classification/Classifier.cs b/src/Workspaces/Core/Portable/Classification/Classifier.cs index 1b49f7c49d7d6..cf146ac0ac186 100644 --- a/src/Workspaces/Core/Portable/Classification/Classifier.cs +++ b/src/Workspaces/Core/Portable/Classification/Classifier.cs @@ -183,7 +183,6 @@ private static IEnumerable Space(int count = 1) ClassificationTypeNames.DelegateName => SymbolDisplayPartKind.DelegateName, ClassificationTypeNames.EnumName => SymbolDisplayPartKind.EnumName, ClassificationTypeNames.TypeParameterName => SymbolDisplayPartKind.TypeParameterName, - ClassificationTypeNames.PreprocessingName => SymbolDisplayPartKind.PreprocessingName, ClassificationTypeNames.ModuleName => SymbolDisplayPartKind.ModuleName, ClassificationTypeNames.VerbatimStringLiteral => SymbolDisplayPartKind.StringLiteral, ClassificationTypeNames.FieldName => SymbolDisplayPartKind.FieldName, From ed92ac38c76d5c465e7db45f06647cc5adcfdd43 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Tue, 12 Sep 2023 21:09:52 +0300 Subject: [PATCH 049/508] Adjust finding the preprocessing symbol --- .../Compilation/CSharpSemanticModel.cs | 2 +- .../CSharp/Portable/Syntax/SyntaxKindFacts.cs | 14 ++++++++++ .../Core/Portable/FindSymbols/SymbolFinder.cs | 28 ++++++++++++++++++- .../SemanticFacts/CSharpSemanticFacts.cs | 9 ------ .../SymbolKey.PreprocessingSymbolKey.cs | 4 +-- .../SemanticFacts/VisualBasicSemanticFacts.vb | 7 ----- 6 files changed, 44 insertions(+), 20 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index 06005a7001941..7d519b2e712b9 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -4854,7 +4854,7 @@ public PreprocessingSymbolInfo GetPreprocessingSymbolInfo(IdentifierNameSyntax n { CheckSyntaxNode(node); - if (node.Ancestors().Any(n => SyntaxFacts.IsPreprocessorDirective(n.Kind()))) + if (node.Ancestors().Any(n => SyntaxFacts.IsPreprocessorDirectiveAcceptingPreprocessingSymbols(n.Kind()))) { return CreatePreprocessingSymbolInfo(node.Identifier); } diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs index 652092a89b94f..5ea7f005554e8 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs @@ -249,6 +249,20 @@ public static bool IsTrivia(SyntaxKind kind) } } + internal static bool IsPreprocessorDirectiveAcceptingPreprocessingSymbols(SyntaxKind kind) + { + switch (kind) + { + case SyntaxKind.IfDirectiveTrivia: + case SyntaxKind.ElifDirectiveTrivia: + case SyntaxKind.DefineDirectiveTrivia: + case SyntaxKind.UndefDirectiveTrivia: + return true; + default: + return false; + } + } + public static bool IsPreprocessorDirective(SyntaxKind kind) { switch (kind) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.cs index d7165ae6d80a2..b1177dab41e0f 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.cs @@ -70,7 +70,16 @@ internal static async Task FindSymbolAtPositionAsync( var semanticInfo = await GetSemanticInfoAtPositionAsync( semanticModel, position, services, cancellationToken).ConfigureAwait(false); - return semanticInfo.GetAnySymbol(includeType: false); + var symbol = semanticInfo.GetAnySymbol(includeType: false); + if (symbol is not null) + return symbol; + + var preprocessingInfo = await GetPreprocessingSymbolInfoAtPositionAsync( + semanticModel, position, services, cancellationToken).ConfigureAwait(false); + if (preprocessingInfo.Symbol is not null) + return preprocessingInfo.Symbol; + + return null; } internal static async Task GetSemanticInfoAtPositionAsync( @@ -90,6 +99,23 @@ internal static async Task GetSemanticInfoAtPositionAsync( return TokenSemanticInfo.Empty; } + internal static async Task GetPreprocessingSymbolInfoAtPositionAsync( + SemanticModel semanticModel, + int position, + SolutionServices services, + CancellationToken cancellationToken) + { + var token = await GetTokenAtPositionAsync(semanticModel, position, services, cancellationToken).ConfigureAwait(false); + + if (token != default && + token.Span.IntersectsWith(position)) + { + return semanticModel.GetPreprocessingSymbolInfo(token.Parent); + } + + return default; + } + private static Task GetTokenAtPositionAsync( SemanticModel semanticModel, int position, diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs index 49c33944fc23a..bd7f3d78bd169 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs @@ -83,15 +83,6 @@ public bool CanReplaceWithRValue(SemanticModel semanticModel, [NotNullWhen(true) // If we hit an executable statement syntax and didn't find anything yet, we can just stop now -- anything higher would be a member declaration which won't be defined by something inside a statement. if (CSharpSyntaxFacts.Instance.IsExecutableStatement(ancestor)) return null; - - switch (ancestor.Kind()) - { - case SyntaxKind.IfDirectiveTrivia: - case SyntaxKind.ElifDirectiveTrivia: - case SyntaxKind.DefineDirectiveTrivia: - case SyntaxKind.UndefDirectiveTrivia: - return semanticModel.Compilation.CreatePreprocessingSymbol(token.ValueText); - } } return null; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs index ed92e68016f6f..8973a9ba01f24 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs @@ -8,7 +8,7 @@ internal partial struct SymbolKey { private class PreprocessingSymbolKey : AbstractSymbolKey { - public static readonly PreprocessingSymbolKey Instance = new(); + public static readonly PreprocessingSymbolKey Instance = new PreprocessingSymbolKey(); public sealed override void Create(IPreprocessingSymbol symbol, SymbolKeyWriter visitor) { @@ -20,7 +20,7 @@ protected sealed override SymbolKeyResolution Resolve(SymbolKeyReader reader, IP failureReason = null; var preprocessingName = reader.ReadRequiredString(); var preprocessingSymbol = reader.Compilation.CreatePreprocessingSymbol(preprocessingName); - return new(preprocessingSymbol); + return new SymbolKeyResolution(preprocessingSymbol); } } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb index 50b46773c7381..244ad45538637 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb @@ -85,13 +85,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic If VisualBasicSyntaxFacts.Instance.IsExecutableStatement(ancestor) Then Return Nothing End If - - Select Case ancestor.Kind() - Case SyntaxKind.IfDirectiveTrivia, - SyntaxKind.ElseIfDirectiveTrivia, - SyntaxKind.ConstDirectiveTrivia - Return semanticModel.Compilation.CreatePreprocessingSymbol(token.Text) - End Select End If Next From 38837932fd1af7fa5fb9898109276bec7840609b Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Tue, 12 Sep 2023 21:23:01 +0300 Subject: [PATCH 050/508] Revert changes to the LSP tests for FAR --- src/Features/Lsif/GeneratorTest/OutputFormatTests.vb | 3 +-- src/Features/Lsif/GeneratorTest/SemanticTokensTests.vb | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Features/Lsif/GeneratorTest/OutputFormatTests.vb b/src/Features/Lsif/GeneratorTest/OutputFormatTests.vb index 71ca7751f3c59..10c0ec494f677 100644 --- a/src/Features/Lsif/GeneratorTest/OutputFormatTests.vb +++ b/src/Features/Lsif/GeneratorTest/OutputFormatTests.vb @@ -27,7 +27,7 @@ Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests , openDocuments:=False, composition:=TestLsifOutput.TestComposition), jsonWriter) AssertEx.EqualOrDiff( -"{""hoverProvider"":true,""declarationProvider"":false,""definitionProvider"":true,""referencesProvider"":true,""typeDefinitionProvider"":false,""documentSymbolProvider"":false,""foldingRangeProvider"":true,""diagnosticProvider"":false,""semanticTokensProvider"":{""tokenTypes"":[""namespace"",""type"",""class"",""enum"",""interface"",""struct"",""typeParameter"",""parameter"",""variable"",""property"",""enumMember"",""event"",""function"",""method"",""macro"",""keyword"",""modifier"",""comment"",""string"",""number"",""regexp"",""operator"",""class name"",""constant name"",""delegate name"",""enum member name"",""enum name"",""event name"",""excluded code"",""extension method name"",""field name"",""interface name"",""json - array"",""json - comment"",""json - constructor name"",""json - keyword"",""json - number"",""json - object"",""json - operator"",""json - property name"",""json - punctuation"",""json - string"",""json - text"",""keyword - control"",""label name"",""local name"",""method name"",""module name"",""namespace name"",""operator - overloaded"",""parameter name"",""preprocessing name"",""preprocessor keyword"",""preprocessor text"",""property name"",""punctuation"",""record class name"",""record struct name"",""regex - alternation"",""regex - anchor"",""regex - character class"",""regex - comment"",""regex - grouping"",""regex - other escape"",""regex - quantifier"",""regex - self escaped character"",""regex - text"",""roslyn test code markdown"",""string - escape character"",""string - verbatim"",""struct name"",""text"",""type parameter name"",""whitespace"",""xml doc comment - attribute name"",""xml doc comment - attribute quotes"",""xml doc comment - attribute value"",""xml doc comment - cdata section"",""xml doc comment - comment"",""xml doc comment - delimiter"",""xml doc comment - entity reference"",""xml doc comment - name"",""xml doc comment - processing instruction"",""xml doc comment - text"",""xml literal - attribute name"",""xml literal - attribute quotes"",""xml literal - attribute value"",""xml literal - cdata section"",""xml literal - comment"",""xml literal - delimiter"",""xml literal - embedded expression"",""xml literal - entity reference"",""xml literal - name"",""xml literal - processing instruction"",""xml literal - text""],""tokenModifiers"":[""static""]},""id"":1,""type"":""vertex"",""label"":""capabilities""} +"{""hoverProvider"":true,""declarationProvider"":false,""definitionProvider"":true,""referencesProvider"":true,""typeDefinitionProvider"":false,""documentSymbolProvider"":false,""foldingRangeProvider"":true,""diagnosticProvider"":false,""semanticTokensProvider"":{""tokenTypes"":[""namespace"",""type"",""class"",""enum"",""interface"",""struct"",""typeParameter"",""parameter"",""variable"",""property"",""enumMember"",""event"",""function"",""method"",""macro"",""keyword"",""modifier"",""comment"",""string"",""number"",""regexp"",""operator"",""class name"",""constant name"",""delegate name"",""enum member name"",""enum name"",""event name"",""excluded code"",""extension method name"",""field name"",""interface name"",""json - array"",""json - comment"",""json - constructor name"",""json - keyword"",""json - number"",""json - object"",""json - operator"",""json - property name"",""json - punctuation"",""json - string"",""json - text"",""keyword - control"",""label name"",""local name"",""method name"",""module name"",""namespace name"",""operator - overloaded"",""parameter name"",""preprocessor keyword"",""preprocessor text"",""property name"",""punctuation"",""record class name"",""record struct name"",""regex - alternation"",""regex - anchor"",""regex - character class"",""regex - comment"",""regex - grouping"",""regex - other escape"",""regex - quantifier"",""regex - self escaped character"",""regex - text"",""roslyn test code markdown"",""string - escape character"",""string - verbatim"",""struct name"",""text"",""type parameter name"",""whitespace"",""xml doc comment - attribute name"",""xml doc comment - attribute quotes"",""xml doc comment - attribute value"",""xml doc comment - cdata section"",""xml doc comment - comment"",""xml doc comment - delimiter"",""xml doc comment - entity reference"",""xml doc comment - name"",""xml doc comment - processing instruction"",""xml doc comment - text"",""xml literal - attribute name"",""xml literal - attribute quotes"",""xml literal - attribute value"",""xml literal - cdata section"",""xml literal - comment"",""xml literal - delimiter"",""xml literal - embedded expression"",""xml literal - entity reference"",""xml literal - name"",""xml literal - processing instruction"",""xml literal - text""],""tokenModifiers"":[""static""]},""id"":1,""type"":""vertex"",""label"":""capabilities""} {""kind"":""csharp"",""resource"":""file:///Z:/%EE%89%9B/TestProject.csproj"",""name"":""TestProject"",""id"":2,""type"":""vertex"",""label"":""project""} {""kind"":""begin"",""scope"":""project"",""data"":2,""id"":3,""type"":""vertex"",""label"":""$event""} {""uri"":""file:///Z:/%EE%89%9B/a.cs"",""languageId"":""csharp"",""id"":4,""type"":""vertex"",""label"":""document""} @@ -121,7 +121,6 @@ Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests ""namespace name"", ""operator - overloaded"", ""parameter name"", - ""preprocessing name"", ""preprocessor keyword"", ""preprocessor text"", ""property name"", diff --git a/src/Features/Lsif/GeneratorTest/SemanticTokensTests.vb b/src/Features/Lsif/GeneratorTest/SemanticTokensTests.vb index 26a8676d9b25c..53b288e84fee7 100644 --- a/src/Features/Lsif/GeneratorTest/SemanticTokensTests.vb +++ b/src/Features/Lsif/GeneratorTest/SemanticTokensTests.vb @@ -16,9 +16,9 @@ Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests - - - + + + Public Async Function TestSemanticTokensData(code As String, expectedTokens As String) As Task ' This test performs LSIF specific validation of the semantic tokens output. As of this writing ' this feature is based on the same code path used to generate semantic tokens information in LSP From 65c79887e3880e9c94e28cd0898ecc09c1addd97 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Tue, 12 Sep 2023 21:25:32 +0300 Subject: [PATCH 051/508] Fix nullability issues --- src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.cs index b1177dab41e0f..233817ee1712e 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.cs @@ -79,7 +79,7 @@ internal static async Task FindSymbolAtPositionAsync( if (preprocessingInfo.Symbol is not null) return preprocessingInfo.Symbol; - return null; + return null!; } internal static async Task GetSemanticInfoAtPositionAsync( @@ -110,7 +110,8 @@ internal static async Task GetPreprocessingSymbolInfoAt if (token != default && token.Span.IntersectsWith(position)) { - return semanticModel.GetPreprocessingSymbolInfo(token.Parent); + Debug.Assert(token.Parent is not null); + return semanticModel.GetPreprocessingSymbolInfo(token.Parent!); } return default; From 7476a680f989c52297e8b7e5b4a7a7ae94e26ae9 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Tue, 12 Sep 2023 23:16:34 +0300 Subject: [PATCH 052/508] Make PreprocessingSymbolKey sealed --- .../Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs index 8973a9ba01f24..2044c004fa3df 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs @@ -6,7 +6,7 @@ namespace Microsoft.CodeAnalysis; internal partial struct SymbolKey { - private class PreprocessingSymbolKey : AbstractSymbolKey + private sealed class PreprocessingSymbolKey : AbstractSymbolKey { public static readonly PreprocessingSymbolKey Instance = new PreprocessingSymbolKey(); From 6b2dc864aa10c0419a82467840e82ff0200bd00e Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Thu, 14 Sep 2023 22:33:14 +0300 Subject: [PATCH 053/508] Review comments Co-Authored-By: Cyrus Najmabadi <4564579+CyrusNajmabadi@users.noreply.github.com> --- .../Portable/Compilation/CSharpSemanticModel.cs | 16 +++++++++++++++- .../Symbols/PublicModel/PreprocessingSymbol.cs | 4 ++-- .../CSharp/Portable/Syntax/SyntaxKindFacts.cs | 14 -------------- .../Core/Portable/Compilation/Compilation.cs | 9 +-------- .../Portable/Symbols/PreprocessingSymbol.vb | 7 +------ ...isualBasicSyntaxTree.ConditionalSymbolsMap.vb | 2 +- .../Portable/Syntax/VisualBasicSyntaxTree.vb | 2 +- .../FindReferences/DependentProjectsFinder.cs | 4 ++-- .../PreprocessingSymbolReferenceFinder.cs | 12 ++---------- 9 files changed, 25 insertions(+), 45 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index 7d519b2e712b9..892f82d5be5ca 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -4854,7 +4854,7 @@ public PreprocessingSymbolInfo GetPreprocessingSymbolInfo(IdentifierNameSyntax n { CheckSyntaxNode(node); - if (node.Ancestors().Any(n => SyntaxFacts.IsPreprocessorDirectiveAcceptingPreprocessingSymbols(n.Kind()))) + if (node.Ancestors().Any(n => IsPreprocessorDirectiveAcceptingPreprocessingSymbols(n.Kind()))) { return CreatePreprocessingSymbolInfo(node.Identifier); } @@ -4875,6 +4875,20 @@ private PreprocessingSymbolInfo CreatePreprocessingSymbolInfo(SyntaxToken identi return new PreprocessingSymbolInfo(preprocessingSymbol, isDefined); } + private static bool IsPreprocessorDirectiveAcceptingPreprocessingSymbols(SyntaxKind kind) + { + switch (kind) + { + case SyntaxKind.IfDirectiveTrivia: + case SyntaxKind.ElifDirectiveTrivia: + case SyntaxKind.DefineDirectiveTrivia: + case SyntaxKind.UndefDirectiveTrivia: + return true; + default: + return false; + } + } + /// /// Options to control the internal working of GetSymbolInfoWorker. Not currently exposed /// to public clients, but could be if desired. diff --git a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs index 33c9d3cc31ddc..647c467602c48 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs @@ -36,12 +36,12 @@ public override bool Equals(object? obj) return true; } - if (obj is not IPreprocessingSymbol other) + if (obj is not PreprocessingSymbol other) { return false; } - return _name == other.Name; + return _name == other._name; } bool IEquatable.Equals(ISymbol? other) diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs index 5ea7f005554e8..652092a89b94f 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs @@ -249,20 +249,6 @@ public static bool IsTrivia(SyntaxKind kind) } } - internal static bool IsPreprocessorDirectiveAcceptingPreprocessingSymbols(SyntaxKind kind) - { - switch (kind) - { - case SyntaxKind.IfDirectiveTrivia: - case SyntaxKind.ElifDirectiveTrivia: - case SyntaxKind.DefineDirectiveTrivia: - case SyntaxKind.UndefDirectiveTrivia: - return true; - default: - return false; - } - } - public static bool IsPreprocessorDirective(SyntaxKind kind) { switch (kind) diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs index a8e2b133343b7..b9ce3c1654b75 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -372,14 +372,7 @@ public INamespaceSymbol CreateErrorNamespaceSymbol(INamespaceSymbol container, s /// Returns a new IPreprocessingSymbol representing a preprocessing symbol with the given name. /// public IPreprocessingSymbol CreatePreprocessingSymbol(string name) - { - if (name is null) - { - throw new ArgumentNullException(nameof(name)); - } - - return CommonCreatePreprocessingSymbol(name); - } + => CommonCreatePreprocessingSymbol(name ?? throw new ArgumentNullException(nameof(name))); protected abstract IPreprocessingSymbol CommonCreatePreprocessingSymbol(string name); diff --git a/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb index dd56401af993f..b0d9a1248d6a2 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb @@ -97,12 +97,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Return False End If - ' If we're comparing against a C# preprocessing symbol, we still refer to the same - ' symbol name. If there exists a different C# preprocessing symbol with a different - ' capitalization variance, we also bind to that one. This is not a concern, as our - ' VB preprocessing symbols only apply within the same project, and we only support - ' this operation for finding all references of the given preprocessing symbol name. - Dim other As IPreprocessingSymbol = TryCast(obj, IPreprocessingSymbol) + Dim other As PreprocessingSymbol = TryCast(obj, PreprocessingSymbol) Return other IsNot Nothing AndAlso IdentifierComparison.Equals(Me.Name, other.Name) diff --git a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.ConditionalSymbolsMap.vb b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.ConditionalSymbolsMap.vb index 5a2d6a9677c28..d70462d8a934f 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.ConditionalSymbolsMap.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.ConditionalSymbolsMap.vb @@ -140,7 +140,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic #End Region Friend Function GetPreprocessingSymbolInfo(conditionalSymbolName As String, node As SyntaxToken) As VisualBasicPreprocessingSymbolInfo - Dim constValue As InternalSyntax.CConst = GetPreprocessorSymbolValue(conditionalSymbolName, node) + Dim constValue = GetPreprocessorSymbolValue(conditionalSymbolName, node) Return GetPreprocessingSymbolInfo(conditionalSymbolName, constValue) End Function diff --git a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.vb b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.vb index 5b1c4fa94d5a5..360125edf55f5 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.vb @@ -598,7 +598,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function Friend Function GetPreprocessingSymbolInfo(token As SyntaxToken) As VisualBasicPreprocessingSymbolInfo - Dim conditionalSymbolName As String = token.ValueText + Dim conditionalSymbolName = token.ValueText Dim conditionalSymbols As ConditionalSymbolsMap = Me.ConditionalSymbols Return If(conditionalSymbols Is Nothing, VisualBasicPreprocessingSymbolInfo.None, conditionalSymbols.GetPreprocessingSymbolInfo(conditionalSymbolName, token)) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs index 1c5cc0dec38ea..cb64ef344cb29 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs @@ -33,8 +33,8 @@ internal static partial class DependentProjectsFinder public static async Task> GetDependentProjectsAsync( Solution solution, ImmutableArray symbols, IImmutableSet projects, CancellationToken cancellationToken) { - // Namespaces are visible in all projects - // Preprocessing symbols are arbitrary identifiers that are not bound to specific projects + // Namespaces are visible in all projects. + // Preprocessing symbols are arbitrary identifiers that are not bound to specific projects. if (symbols.Any(static s => s.Kind is SymbolKind.Namespace or SymbolKind.Preprocessing)) return projects.ToImmutableArray(); diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs index ad3001fa4f9cd..856cce2976f86 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.PooledObjects; @@ -66,8 +65,8 @@ private static async ValueTask> FindPreprocessing { cancellationToken.ThrowIfCancellationRequested(); - var matched = await PreprocessingSymbolsMatchAsync(symbol, state, token, cancellationToken) - .ConfigureAwait(false); + var targetSymbol = state.SemanticModel.GetPreprocessingSymbolInfo(token.GetRequiredParent()).Symbol; + var matched = await SymbolFinder.OriginalSymbolsMatchAsync(state.Solution, symbol, targetSymbol, cancellationToken).ConfigureAwait(false); if (matched) { @@ -77,11 +76,4 @@ private static async ValueTask> FindPreprocessing return locations.ToImmutable(); } - - private static async ValueTask PreprocessingSymbolsMatchAsync( - IPreprocessingSymbol searchSymbol, FindReferencesDocumentState state, SyntaxToken token, CancellationToken cancellationToken) - { - var symbol = state.SemanticModel.GetPreprocessingSymbolInfo(token.GetRequiredParent()).Symbol; - return await SymbolFinder.OriginalSymbolsMatchAsync(state.Solution, searchSymbol, symbol, cancellationToken).ConfigureAwait(false); - } } From fa46873c9682259d283af3b0a25b35dc4dc3558d Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Fri, 15 Sep 2023 01:19:28 +0300 Subject: [PATCH 054/508] GetSemanticInfo now returns pp symbols --- .../Core/Portable/FindSymbols/SymbolFinder.cs | 29 +------------------ .../Extensions/SemanticModelExtensions.cs | 6 ++++ .../Services/SyntaxFacts/CSharpSyntaxFacts.cs | 22 ++++++++++++++ .../Core/Services/SyntaxFacts/ISyntaxFacts.cs | 1 + .../SyntaxFacts/VisualBasicSyntaxFacts.vb | 18 ++++++++++++ 5 files changed, 48 insertions(+), 28 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.cs index 233817ee1712e..d7165ae6d80a2 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.cs @@ -70,16 +70,7 @@ internal static async Task FindSymbolAtPositionAsync( var semanticInfo = await GetSemanticInfoAtPositionAsync( semanticModel, position, services, cancellationToken).ConfigureAwait(false); - var symbol = semanticInfo.GetAnySymbol(includeType: false); - if (symbol is not null) - return symbol; - - var preprocessingInfo = await GetPreprocessingSymbolInfoAtPositionAsync( - semanticModel, position, services, cancellationToken).ConfigureAwait(false); - if (preprocessingInfo.Symbol is not null) - return preprocessingInfo.Symbol; - - return null!; + return semanticInfo.GetAnySymbol(includeType: false); } internal static async Task GetSemanticInfoAtPositionAsync( @@ -99,24 +90,6 @@ internal static async Task GetSemanticInfoAtPositionAsync( return TokenSemanticInfo.Empty; } - internal static async Task GetPreprocessingSymbolInfoAtPositionAsync( - SemanticModel semanticModel, - int position, - SolutionServices services, - CancellationToken cancellationToken) - { - var token = await GetTokenAtPositionAsync(semanticModel, position, services, cancellationToken).ConfigureAwait(false); - - if (token != default && - token.Span.IntersectsWith(position)) - { - Debug.Assert(token.Parent is not null); - return semanticModel.GetPreprocessingSymbolInfo(token.Parent!); - } - - return default; - } - private static Task GetTokenAtPositionAsync( SemanticModel semanticModel, int position, diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs index 0eca638b1a20a..e77209d1e89b6 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs @@ -104,6 +104,12 @@ public static TokenSemanticInfo GetSemanticInfo( allSymbols = overriddenSymbol is null ? ImmutableArray.Empty : ImmutableArray.Create(overriddenSymbol); } + else if (token.RawKind == syntaxKinds.IdentifierToken && syntaxFacts.IsInContextAcceptingPreprocessingNames(token)) + { + var tokenParent = token.GetRequiredParent(); + var preprocessingSymbol = semanticModel.GetPreprocessingSymbolInfo(tokenParent).Symbol; + allSymbols = preprocessingSymbol is null ? ImmutableArray.Empty : ImmutableArray.Create(preprocessingSymbol); + } else { aliasSymbol = semanticModel.GetAliasInfo(token.Parent!, cancellationToken); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs index 62aabbdd8d05f..db317c5c6924f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs @@ -116,6 +116,28 @@ public bool IsPreProcessorDirectiveContext(SyntaxTree syntaxTree, int position, => syntaxTree.IsPreProcessorDirectiveContext( position, syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken, includeDirectives: true), cancellationToken); + public bool IsInContextAcceptingPreprocessingNames(SyntaxToken token) + { + var directive = token.GetAncestor(); + if (directive is null) + { + return false; + } + + switch (directive.Kind()) + { + case SyntaxKind.IfDirectiveTrivia: + case SyntaxKind.ElifDirectiveTrivia: + case SyntaxKind.DefineDirectiveTrivia: + case SyntaxKind.UndefDirectiveTrivia: + break; + default: + return false; + } + + return directive.FullSpan.Contains(token.Span); + } + public bool IsEntirelyWithinStringOrCharOrNumericLiteral([NotNullWhen(true)] SyntaxTree? syntaxTree, int position, CancellationToken cancellationToken) { if (syntaxTree == null) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs index c71f1225a6df9..d6cab29473d97 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs @@ -161,6 +161,7 @@ internal interface ISyntaxFacts /// bool IsPreprocessorKeyword(SyntaxToken token); bool IsPreProcessorDirectiveContext(SyntaxTree syntaxTree, int position, CancellationToken cancellationToken); + bool IsInContextAcceptingPreprocessingNames(SyntaxToken token); bool IsLiteral(SyntaxToken token); bool IsStringLiteralOrInterpolatedStringLiteral(SyntaxToken token); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb index e39797f051f14..11390576ac28c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb @@ -128,6 +128,24 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Return syntaxTree.IsInPreprocessorDirectiveContext(position, cancellationToken) End Function + Public Function IsInContextAcceptingPreprocessingNames(token As SyntaxToken) As Boolean Implements ISyntaxFacts.IsInContextAcceptingPreprocessingNames + Dim directive = token.GetAncestor(Of DirectiveTriviaSyntax)() + If directive Is Nothing Then + Return False + End If + + Select Case directive.Kind() + Case SyntaxKind.IfDirectiveTrivia, + SyntaxKind.ElseIfDirectiveTrivia, + SyntaxKind.ConstDirectiveTrivia + Exit Select + Case Else + Return False + End Select + + Return directive.FullSpan.Contains(token.Span) + End Function + Public Function IsEntirelyWithinStringOrCharOrNumericLiteral(syntaxTree As SyntaxTree, position As Integer, cancellationToken As CancellationToken) As Boolean Implements ISyntaxFacts.IsEntirelyWithinStringOrCharOrNumericLiteral If syntaxTree Is Nothing Then Return False From f6912dd4338d859e4fd9e1b8673ee8ebc96d280f Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Fri, 15 Sep 2023 09:49:11 +0300 Subject: [PATCH 055/508] Remove syntax fact --- .../Extensions/SemanticModelExtensions.cs | 45 ++++++++++--------- .../Services/SyntaxFacts/CSharpSyntaxFacts.cs | 22 --------- .../Core/Services/SyntaxFacts/ISyntaxFacts.cs | 1 - .../SyntaxFacts/VisualBasicSyntaxFacts.vb | 18 -------- 4 files changed, 25 insertions(+), 61 deletions(-) diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs index e77209d1e89b6..99ae776a57ab3 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs @@ -104,28 +104,33 @@ public static TokenSemanticInfo GetSemanticInfo( allSymbols = overriddenSymbol is null ? ImmutableArray.Empty : ImmutableArray.Create(overriddenSymbol); } - else if (token.RawKind == syntaxKinds.IdentifierToken && syntaxFacts.IsInContextAcceptingPreprocessingNames(token)) - { - var tokenParent = token.GetRequiredParent(); - var preprocessingSymbol = semanticModel.GetPreprocessingSymbolInfo(tokenParent).Symbol; - allSymbols = preprocessingSymbol is null ? ImmutableArray.Empty : ImmutableArray.Create(preprocessingSymbol); - } else { - aliasSymbol = semanticModel.GetAliasInfo(token.Parent!, cancellationToken); - var bindableParent = syntaxFacts.TryGetBindableParent(token); - var typeInfo = bindableParent != null ? semanticModel.GetTypeInfo(bindableParent, cancellationToken) : default; - type = typeInfo.Type; - convertedType = typeInfo.ConvertedType; - declaredSymbol = MapSymbol(semanticFacts.GetDeclaredSymbol(semanticModel, token, cancellationToken), type); - - var skipSymbolInfoLookup = declaredSymbol.IsKind(SymbolKind.RangeVariable); - allSymbols = skipSymbolInfoLookup - ? ImmutableArray.Empty - : semanticFacts - .GetBestOrAllSymbols(semanticModel, bindableParent, token, cancellationToken) - .WhereAsArray(s => !s.Equals(declaredSymbol)) - .SelectAsArray(s => MapSymbol(s, type)); + IPreprocessingSymbol? preprocessingSymbol = null; + if (token.RawKind == syntaxKinds.IdentifierToken) + { + var tokenParent = token.GetRequiredParent(); + preprocessingSymbol = semanticModel.GetPreprocessingSymbolInfo(tokenParent).Symbol; + allSymbols = preprocessingSymbol is null ? ImmutableArray.Empty : ImmutableArray.Create(preprocessingSymbol); + } + + if (preprocessingSymbol is null) + { + aliasSymbol = semanticModel.GetAliasInfo(token.Parent!, cancellationToken); + var bindableParent = syntaxFacts.TryGetBindableParent(token); + var typeInfo = bindableParent != null ? semanticModel.GetTypeInfo(bindableParent, cancellationToken) : default; + type = typeInfo.Type; + convertedType = typeInfo.ConvertedType; + declaredSymbol = MapSymbol(semanticFacts.GetDeclaredSymbol(semanticModel, token, cancellationToken), type); + + var skipSymbolInfoLookup = declaredSymbol.IsKind(SymbolKind.RangeVariable); + allSymbols = skipSymbolInfoLookup + ? ImmutableArray.Empty + : semanticFacts + .GetBestOrAllSymbols(semanticModel, bindableParent, token, cancellationToken) + .WhereAsArray(s => !s.Equals(declaredSymbol)) + .SelectAsArray(s => MapSymbol(s, type)); + } } // NOTE(cyrusn): This is a workaround to how the semantic model binds and returns diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs index db317c5c6924f..62aabbdd8d05f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs @@ -116,28 +116,6 @@ public bool IsPreProcessorDirectiveContext(SyntaxTree syntaxTree, int position, => syntaxTree.IsPreProcessorDirectiveContext( position, syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken, includeDirectives: true), cancellationToken); - public bool IsInContextAcceptingPreprocessingNames(SyntaxToken token) - { - var directive = token.GetAncestor(); - if (directive is null) - { - return false; - } - - switch (directive.Kind()) - { - case SyntaxKind.IfDirectiveTrivia: - case SyntaxKind.ElifDirectiveTrivia: - case SyntaxKind.DefineDirectiveTrivia: - case SyntaxKind.UndefDirectiveTrivia: - break; - default: - return false; - } - - return directive.FullSpan.Contains(token.Span); - } - public bool IsEntirelyWithinStringOrCharOrNumericLiteral([NotNullWhen(true)] SyntaxTree? syntaxTree, int position, CancellationToken cancellationToken) { if (syntaxTree == null) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs index d6cab29473d97..c71f1225a6df9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs @@ -161,7 +161,6 @@ internal interface ISyntaxFacts /// bool IsPreprocessorKeyword(SyntaxToken token); bool IsPreProcessorDirectiveContext(SyntaxTree syntaxTree, int position, CancellationToken cancellationToken); - bool IsInContextAcceptingPreprocessingNames(SyntaxToken token); bool IsLiteral(SyntaxToken token); bool IsStringLiteralOrInterpolatedStringLiteral(SyntaxToken token); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb index 11390576ac28c..e39797f051f14 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb @@ -128,24 +128,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Return syntaxTree.IsInPreprocessorDirectiveContext(position, cancellationToken) End Function - Public Function IsInContextAcceptingPreprocessingNames(token As SyntaxToken) As Boolean Implements ISyntaxFacts.IsInContextAcceptingPreprocessingNames - Dim directive = token.GetAncestor(Of DirectiveTriviaSyntax)() - If directive Is Nothing Then - Return False - End If - - Select Case directive.Kind() - Case SyntaxKind.IfDirectiveTrivia, - SyntaxKind.ElseIfDirectiveTrivia, - SyntaxKind.ConstDirectiveTrivia - Exit Select - Case Else - Return False - End Select - - Return directive.FullSpan.Contains(token.Span) - End Function - Public Function IsEntirelyWithinStringOrCharOrNumericLiteral(syntaxTree As SyntaxTree, position As Integer, cancellationToken As CancellationToken) As Boolean Implements ISyntaxFacts.IsEntirelyWithinStringOrCharOrNumericLiteral If syntaxTree Is Nothing Then Return False From 4b84b73d0892bb20bd83da002cb25df4c5c95788 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 15 Sep 2023 11:10:04 -0700 Subject: [PATCH 056/508] Update src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs --- .../CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs index 52b5e424f69c5..0663b0950b02a 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs @@ -292,6 +292,7 @@ public override void VisitAlias(IAliasSymbol symbol) internal override void VisitPreprocessing(IPreprocessingSymbol symbol) { + // Currently using 'Text' part kind as there is no kind specific to preprocessing symbols. var part = new SymbolDisplayPart(SymbolDisplayPartKind.Text, symbol, symbol.Name); builder.Add(part); } From 9ef892394501e05e68e65312625b722618d3eac2 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Fri, 15 Sep 2023 21:17:33 +0300 Subject: [PATCH 057/508] Make local function --- .../Compilation/CSharpSemanticModel.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index 2463290d747ba..1a56d9bef5101 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -4859,12 +4859,26 @@ public PreprocessingSymbolInfo GetPreprocessingSymbolInfo(IdentifierNameSyntax n { CheckSyntaxNode(node); - if (node.Ancestors().Any(n => IsPreprocessorDirectiveAcceptingPreprocessingSymbols(n.Kind()))) + if (node.Ancestors().Any(n => isPreprocessorDirectiveAcceptingPreprocessingSymbols(n.Kind()))) { return CreatePreprocessingSymbolInfo(node.Identifier); } return PreprocessingSymbolInfo.None; + + static bool isPreprocessorDirectiveAcceptingPreprocessingSymbols(SyntaxKind kind) + { + switch (kind) + { + case SyntaxKind.IfDirectiveTrivia: + case SyntaxKind.ElifDirectiveTrivia: + case SyntaxKind.DefineDirectiveTrivia: + case SyntaxKind.UndefDirectiveTrivia: + return true; + default: + return false; + } + } } private PreprocessingSymbolInfo GetPreprocessingSymbolInfo(DirectiveTriviaSyntax node, SyntaxToken name) @@ -4880,20 +4894,6 @@ private PreprocessingSymbolInfo CreatePreprocessingSymbolInfo(SyntaxToken identi return new PreprocessingSymbolInfo(preprocessingSymbol, isDefined); } - private static bool IsPreprocessorDirectiveAcceptingPreprocessingSymbols(SyntaxKind kind) - { - switch (kind) - { - case SyntaxKind.IfDirectiveTrivia: - case SyntaxKind.ElifDirectiveTrivia: - case SyntaxKind.DefineDirectiveTrivia: - case SyntaxKind.UndefDirectiveTrivia: - return true; - default: - return false; - } - } - /// /// Options to control the internal working of GetSymbolInfoWorker. Not currently exposed /// to public clients, but could be if desired. From 8e5f80963f141ee19d9c47a8e258f4df16cea960 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 15 Sep 2023 11:36:31 -0700 Subject: [PATCH 058/508] Simplify --- .../Portable/Symbols/PublicModel/PreprocessingSymbol.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs index 647c467602c48..46e8f473d6164 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs @@ -36,12 +36,8 @@ public override bool Equals(object? obj) return true; } - if (obj is not PreprocessingSymbol other) - { - return false; - } - - return _name == other._name; + return obj is PreprocessingSymbol other && + _name == other._name; } bool IEquatable.Equals(ISymbol? other) From 76d756217b2edc46bc2202e8c27b3cbac73e04cf Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 15 Sep 2023 11:37:43 -0700 Subject: [PATCH 059/508] Formatting --- .../Finders/PreprocessingSymbolReferenceFinder.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs index 856cce2976f86..19bd5aeea5bf8 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs @@ -13,7 +13,8 @@ namespace Microsoft.CodeAnalysis.FindSymbols.Finders; internal sealed class PreprocessingSymbolReferenceFinder : AbstractReferenceFinder { - protected override bool CanFind(IPreprocessingSymbol symbol) => true; + protected override bool CanFind(IPreprocessingSymbol symbol) + => true; protected sealed override async ValueTask> FindReferencesInDocumentAsync( IPreprocessingSymbol symbol, From bb11866a1e4d719a90ef32d44ab901299fa074db Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 15 Sep 2023 11:38:24 -0700 Subject: [PATCH 060/508] inline --- .../PreprocessingSymbolReferenceFinder.cs | 40 ++++++------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs index 19bd5aeea5bf8..374eefc27ea6f 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs @@ -24,12 +24,19 @@ protected sealed override async ValueTask> FindRe { var tokens = await FindMatchingIdentifierTokensAsync(state, symbol.Name, cancellationToken).ConfigureAwait(false); - var normalReferences = await FindPreprocessingReferencesInTokensAsync( - symbol, state, - tokens, - cancellationToken).ConfigureAwait(false); + using var _ = ArrayBuilder.GetInstance(out var locations); + foreach (var token in tokens) + { + cancellationToken.ThrowIfCancellationRequested(); + + var targetSymbol = state.SemanticModel.GetPreprocessingSymbolInfo(token.GetRequiredParent()).Symbol; + var matched = await SymbolFinder.OriginalSymbolsMatchAsync(state.Solution, symbol, targetSymbol, cancellationToken).ConfigureAwait(false); + + if (matched) + locations.Add(CreateFinderLocation(state, token, CandidateReason.None, cancellationToken)); + } - return normalReferences; + return locations.ToImmutable(); } protected override async Task> DetermineDocumentsToSearchAsync( @@ -54,27 +61,4 @@ protected override async Task> DetermineDocumentsToSear return resultDocuments.ToImmutable(); } - - private static async ValueTask> FindPreprocessingReferencesInTokensAsync( - IPreprocessingSymbol symbol, - FindReferencesDocumentState state, - ImmutableArray tokens, - CancellationToken cancellationToken) - { - using var _ = ArrayBuilder.GetInstance(out var locations); - foreach (var token in tokens) - { - cancellationToken.ThrowIfCancellationRequested(); - - var targetSymbol = state.SemanticModel.GetPreprocessingSymbolInfo(token.GetRequiredParent()).Symbol; - var matched = await SymbolFinder.OriginalSymbolsMatchAsync(state.Solution, symbol, targetSymbol, cancellationToken).ConfigureAwait(false); - - if (matched) - { - locations.Add(CreateFinderLocation(state, token, CandidateReason.None, cancellationToken)); - } - } - - return locations.ToImmutable(); - } } From 21aa0c9b37647055f3c77aaa43c50b8e8dd94d18 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 15 Sep 2023 11:50:51 -0700 Subject: [PATCH 061/508] Update persistence version --- .../FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs b/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs index 5d23b20ea675f..0accb36bf2a50 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.FindSymbols internal partial class AbstractSyntaxIndex : IObjectWritable { private static readonly string s_persistenceName = typeof(TIndex).Name; - private static readonly Checksum s_serializationFormatChecksum = Checksum.Create("38"); + private static readonly Checksum s_serializationFormatChecksum = Checksum.Create("39"); /// /// Cache of ParseOptions to a checksum for the contained From ba8ab3c6be7d334e8d3a2181f0533c507f0bb5d9 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 15 Sep 2023 12:06:39 -0700 Subject: [PATCH 062/508] Put in correct location --- ...indReferencesTests.PreprocessingSymbols.vb | 26 ++++++------- .../Extensions/SemanticModelExtensions.cs | 39 +++++++------------ .../SemanticFacts/CSharpSemanticFacts.cs | 8 ++++ .../SemanticFacts/VisualBasicSemanticFacts.vb | 16 ++++++-- 4 files changed, 48 insertions(+), 41 deletions(-) diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb index 576eea1b70e97..60f7e7b246fb0 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb @@ -9,8 +9,8 @@ Imports Microsoft.CodeAnalysis.Remote.Testing Namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences Partial Public Class FindReferencesTests - + Public Async Function TestPreprocessingSymbolBasic(kind As TestKind, host As TestHost) As Task Dim input = @@ -39,8 +39,8 @@ class PREPROCESSING_SYMBOL Await TestAPIAndFeature(input, kind, host) End Function - + Public Async Function TestNotPreprocessingSymbol(kind As TestKind, host As TestHost) As Task Dim input = @@ -69,8 +69,8 @@ class {|Definition:PREPROCES$$SING_SYMBOL|} Await TestAPIAndFeature(input, kind, host) End Function - + Public Async Function TestPreprocessingSymbolRegion(kind As TestKind, host As TestHost) As Task Dim input = @@ -94,8 +94,8 @@ class Class Await TestAPIAndFeature(input, kind, host) End Function - + Public Async Function TestPreprocessingSymbolPragmaWarning(kind As TestKind, host As TestHost) As Task Dim input = @@ -119,8 +119,8 @@ class Class Await TestAPIAndFeature(input, kind, host) End Function - + Public Async Function TestPreprocessingSymbolMultipleDocuments(kind As TestKind, host As TestHost) As Task Dim input = @@ -154,8 +154,8 @@ class Class Await TestAPIAndFeature(input, kind, host) End Function - + Public Async Function TestPreprocessingSymbolMultipleProjects1(kind As TestKind, host As TestHost) As Task Dim input = @@ -186,8 +186,8 @@ class Class Await TestAPIAndFeature(input, kind, host) End Function - + Public Async Function TestPreprocessingSymbolMultipleProjects2HoverCSharp(kind As TestKind, host As TestHost) As Task Dim input = @@ -215,8 +215,8 @@ class Class Await TestAPIAndFeature(input, kind, host) End Function - + Public Async Function TestPreprocessingSymbolMultipleProjects2HoverVB(kind As TestKind, host As TestHost) As Task Dim input = @@ -244,8 +244,8 @@ class Class Await TestAPIAndFeature(input, kind, host) End Function - + Public Async Function TestPreprocessingSymbolUsedInSourceGeneratedDocument(kind As TestKind, host As TestHost) As Task Dim input = @@ -269,8 +269,8 @@ class Class Await TestAPIAndFeature(input, kind, host) End Function - + Public Async Function TestPreprocessingSymbolHoverDefine(kind As TestKind, host As TestHost) As Task Dim input = @@ -284,8 +284,8 @@ class Class Await TestAPIAndFeature(input, kind, host) End Function - + Public Async Function TestPreprocessingSymbolHoverUndef(kind As TestKind, host As TestHost) As Task Dim input = @@ -299,8 +299,8 @@ class Class Await TestAPIAndFeature(input, kind, host) End Function - + Public Async Function TestPreprocessingSymbolHoverConst(kind As TestKind, host As TestHost) As Task Dim input = @@ -317,8 +317,8 @@ class Class Await TestAPIAndFeature(input, kind, host) End Function - + Public Async Function TestPreprocessingSymbolHoverAssignedConst(kind As TestKind, host As TestHost) As Task Dim input = diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs index 99ae776a57ab3..0eca638b1a20a 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs @@ -106,31 +106,20 @@ public static TokenSemanticInfo GetSemanticInfo( } else { - IPreprocessingSymbol? preprocessingSymbol = null; - if (token.RawKind == syntaxKinds.IdentifierToken) - { - var tokenParent = token.GetRequiredParent(); - preprocessingSymbol = semanticModel.GetPreprocessingSymbolInfo(tokenParent).Symbol; - allSymbols = preprocessingSymbol is null ? ImmutableArray.Empty : ImmutableArray.Create(preprocessingSymbol); - } - - if (preprocessingSymbol is null) - { - aliasSymbol = semanticModel.GetAliasInfo(token.Parent!, cancellationToken); - var bindableParent = syntaxFacts.TryGetBindableParent(token); - var typeInfo = bindableParent != null ? semanticModel.GetTypeInfo(bindableParent, cancellationToken) : default; - type = typeInfo.Type; - convertedType = typeInfo.ConvertedType; - declaredSymbol = MapSymbol(semanticFacts.GetDeclaredSymbol(semanticModel, token, cancellationToken), type); - - var skipSymbolInfoLookup = declaredSymbol.IsKind(SymbolKind.RangeVariable); - allSymbols = skipSymbolInfoLookup - ? ImmutableArray.Empty - : semanticFacts - .GetBestOrAllSymbols(semanticModel, bindableParent, token, cancellationToken) - .WhereAsArray(s => !s.Equals(declaredSymbol)) - .SelectAsArray(s => MapSymbol(s, type)); - } + aliasSymbol = semanticModel.GetAliasInfo(token.Parent!, cancellationToken); + var bindableParent = syntaxFacts.TryGetBindableParent(token); + var typeInfo = bindableParent != null ? semanticModel.GetTypeInfo(bindableParent, cancellationToken) : default; + type = typeInfo.Type; + convertedType = typeInfo.ConvertedType; + declaredSymbol = MapSymbol(semanticFacts.GetDeclaredSymbol(semanticModel, token, cancellationToken), type); + + var skipSymbolInfoLookup = declaredSymbol.IsKind(SymbolKind.RangeVariable); + allSymbols = skipSymbolInfoLookup + ? ImmutableArray.Empty + : semanticFacts + .GetBestOrAllSymbols(semanticModel, bindableParent, token, cancellationToken) + .WhereAsArray(s => !s.Equals(declaredSymbol)) + .SelectAsArray(s => MapSymbol(s, type)); } // NOTE(cyrusn): This is a workaround to how the semantic model binds and returns diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs index bd7f3d78bd169..9cb298f38bce7 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs @@ -359,6 +359,14 @@ private static ImmutableArray GetSymbolInfo(SemanticModel semanticModel } } + if (token.IsKind(SyntaxKind.IdentifierToken)) + { + var tokenParent = token.GetRequiredParent(); + var preprocessingSymbol = semanticModel.GetPreprocessingSymbolInfo(tokenParent).Symbol; + if (preprocessingSymbol != null) + return ImmutableArray.Create(preprocessingSymbol); + } + return semanticModel.GetSymbolInfo(node, cancellationToken).GetBestOrAllSymbols(); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb index 244ad45538637..0a4f3b2a6172c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb @@ -258,9 +258,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function Public Function GetBestOrAllSymbols(semanticModel As SemanticModel, node As SyntaxNode, token As SyntaxToken, cancellationToken As CancellationToken) As ImmutableArray(Of ISymbol) Implements ISemanticFacts.GetBestOrAllSymbols - Return If(node Is Nothing, - ImmutableArray(Of ISymbol).Empty, - semanticModel.GetSymbolInfo(node, cancellationToken).GetBestOrAllSymbols()) + If node Is Nothing Then + Return ImmutableArray(Of ISymbol).Empty + End If + + If token.IsKind(SyntaxKind.IdentifierToken) Then + Dim tokenParent = token.GetRequiredParent() + Dim preprocessingSymbol = semanticModel.GetPreprocessingSymbolInfo(tokenParent).Symbol + If preprocessingSymbol IsNot Nothing Then + Return ImmutableArray.Create(Of ISymbol)(preprocessingSymbol) + End If + End If + + Return semanticModel.GetSymbolInfo(node, cancellationToken).GetBestOrAllSymbols() End Function Public Function IsInsideNameOfExpression(semanticModel As SemanticModel, node As SyntaxNode, cancellationToken As CancellationToken) As Boolean Implements ISemanticFacts.IsInsideNameOfExpression From 352c77b200c83f787a0d98d146f08854262cff83 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 15 Sep 2023 12:07:41 -0700 Subject: [PATCH 063/508] Simpler --- .../Services/SemanticFacts/CSharpSemanticFacts.cs | 13 ++++--------- .../SemanticFacts/VisualBasicSemanticFacts.vb | 13 ++++--------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs index 9cb298f38bce7..c9bd1d53a0ba0 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs @@ -359,15 +359,10 @@ private static ImmutableArray GetSymbolInfo(SemanticModel semanticModel } } - if (token.IsKind(SyntaxKind.IdentifierToken)) - { - var tokenParent = token.GetRequiredParent(); - var preprocessingSymbol = semanticModel.GetPreprocessingSymbolInfo(tokenParent).Symbol; - if (preprocessingSymbol != null) - return ImmutableArray.Create(preprocessingSymbol); - } - - return semanticModel.GetSymbolInfo(node, cancellationToken).GetBestOrAllSymbols(); + var preprocessingSymbol = semanticModel.GetPreprocessingSymbolInfo(node).Symbol; + return preprocessingSymbol != null + ? ImmutableArray.Create(preprocessingSymbol) + : semanticModel.GetSymbolInfo(node, cancellationToken).GetBestOrAllSymbols(); } public bool IsInsideNameOfExpression(SemanticModel semanticModel, [NotNullWhen(true)] SyntaxNode? node, CancellationToken cancellationToken) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb index 0a4f3b2a6172c..4280ce49e68ea 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb @@ -262,15 +262,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return ImmutableArray(Of ISymbol).Empty End If - If token.IsKind(SyntaxKind.IdentifierToken) Then - Dim tokenParent = token.GetRequiredParent() - Dim preprocessingSymbol = semanticModel.GetPreprocessingSymbolInfo(tokenParent).Symbol - If preprocessingSymbol IsNot Nothing Then - Return ImmutableArray.Create(Of ISymbol)(preprocessingSymbol) - End If - End If - - Return semanticModel.GetSymbolInfo(node, cancellationToken).GetBestOrAllSymbols() + Dim preprocessingSymbol = semanticModel.GetPreprocessingSymbolInfo(node).Symbol + Return If(preprocessingSymbol IsNot Nothing, + ImmutableArray.Create(Of ISymbol)(preprocessingSymbol), + semanticModel.GetSymbolInfo(node, cancellationToken).GetBestOrAllSymbols()) End Function Public Function IsInsideNameOfExpression(semanticModel As SemanticModel, node As SyntaxNode, cancellationToken As CancellationToken) As Boolean Implements ISemanticFacts.IsInsideNameOfExpression From b1e46690121cceeff64f83612342fa649e6ef208 Mon Sep 17 00:00:00 2001 From: ayoub elouahili Date: Wed, 18 Oct 2023 03:35:49 +0100 Subject: [PATCH 064/508] fix Inline temporary variable adds unnecessary cast --- .../InlineTemporaryCodeRefactoringProvider.cs | 4 +-- .../InlineTemporary/InlineTemporaryTests.cs | 29 ++++++++++++++++++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs index 497e5049139cf..fe9147f3da09d 100644 --- a/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs @@ -178,7 +178,7 @@ private static async Task InlineTemporaryAsync(Document document, Vari newScope = GetScope(declarator); // Note that we only remove the local declaration if there weren't any conflicts, - if (conflictReferences.Count == 0) + if (conflictReferences.Count == 0) { // No semantic conflicts, we can remove the definition. document = await document.ReplaceNodeAsync( @@ -414,7 +414,7 @@ ExpressionSyntax CreateExpressionToInline() return SyntaxFactory.ArrayCreationExpression(arrayType, arrayInitializer); } - else if (isVar && expression is ObjectCreationExpressionSyntax or ArrayCreationExpressionSyntax or CastExpressionSyntax) + else if (isVar && expression is ObjectCreationExpressionSyntax or ArrayCreationExpressionSyntax or CastExpressionSyntax or InvocationExpressionSyntax) { // if we have `var x = new Y();` there's no need to do any casting as the type is indicated // directly in the existing code. The same holds for `new Y[]` or `(Y)...` diff --git a/src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs b/src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs index c780c545bb5e4..631de049f0bb3 100644 --- a/src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs +++ b/src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs @@ -6,11 +6,11 @@ using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.CodeRefactorings.InlineTemporary; -using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; +using static ICSharpCode.Decompiler.IL.Transforms.Stepper; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings.InlineTemporary { @@ -5835,5 +5835,32 @@ void SomeMethod(string _) { } await TestInRegularAndScriptAsync(code, expected); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69869")] + public async Task InlineTemporaryNoNeededVariable() + { + await TestInRegularAndScriptAsync( + """ + using System; + class A + { + void M(string[] args) + { + var [||]a = Math.Round(1.1D); + var b = a; + } + } + """, + """ + using System; + class A + { + void M(string[] args) + { + var b = Math.Round(1.1D); + } + } + """); + } } } From eaac957b9e28710422d7aff0d5f099530c4ad1d1 Mon Sep 17 00:00:00 2001 From: ayoub elouahili Date: Wed, 18 Oct 2023 03:39:34 +0100 Subject: [PATCH 065/508] undo space remove unused using --- .../CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs | 1 - .../InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs index 2177c6f371c5d..3dcc475f22bd0 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs @@ -22,7 +22,6 @@ using Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Remote.Testing; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; diff --git a/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs index fe9147f3da09d..f5b50a36ad653 100644 --- a/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs @@ -178,7 +178,7 @@ private static async Task InlineTemporaryAsync(Document document, Vari newScope = GetScope(declarator); // Note that we only remove the local declaration if there weren't any conflicts, - if (conflictReferences.Count == 0) + if (conflictReferences.Count == 0) { // No semantic conflicts, we can remove the definition. document = await document.ReplaceNodeAsync( From 400c42e6018556a6b7da83db1deaffe2f644cbee Mon Sep 17 00:00:00 2001 From: ayoub elouahili Date: Mon, 23 Oct 2023 17:50:28 +0100 Subject: [PATCH 066/508] undo change add comment --- .../CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs | 1 + .../InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs index 3dcc475f22bd0..2177c6f371c5d 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs @@ -22,6 +22,7 @@ using Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Remote.Testing; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; diff --git a/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs index f5b50a36ad653..f3a4f2d9b0481 100644 --- a/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs @@ -416,6 +416,7 @@ ExpressionSyntax CreateExpressionToInline() } else if (isVar && expression is ObjectCreationExpressionSyntax or ArrayCreationExpressionSyntax or CastExpressionSyntax or InvocationExpressionSyntax) { + // if we have an InvocationExpressionSyntax `var x = Math.Round(1.1D);` there's no need to do any casting. // if we have `var x = new Y();` there's no need to do any casting as the type is indicated // directly in the existing code. The same holds for `new Y[]` or `(Y)...` return expression; From 10e9aed822b320a29c98f238580911150c4ce80e Mon Sep 17 00:00:00 2001 From: ELouahili Ayoub Date: Tue, 24 Oct 2023 19:23:04 +0100 Subject: [PATCH 067/508] Update InlineTemporaryCodeRefactoringProvider.cs add explication --- .../InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs index f3a4f2d9b0481..6b040eec479ad 100644 --- a/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs @@ -416,7 +416,7 @@ ExpressionSyntax CreateExpressionToInline() } else if (isVar && expression is ObjectCreationExpressionSyntax or ArrayCreationExpressionSyntax or CastExpressionSyntax or InvocationExpressionSyntax) { - // if we have an InvocationExpressionSyntax `var x = Math.Round(1.1D);` there's no need to do any casting. + // if we have an InvocationExpressionSyntax `var x = Math.Round(1.1D);` there's no need to do any casting as x is var and the method return type is known. // if we have `var x = new Y();` there's no need to do any casting as the type is indicated // directly in the existing code. The same holds for `new Y[]` or `(Y)...` return expression; From 952ee2fda7749c6a2ed56d36f4022c0cce589d44 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 24 Oct 2023 11:54:49 -0700 Subject: [PATCH 068/508] Update src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs --- .../InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs index 6b040eec479ad..c7dbe4b499a97 100644 --- a/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs @@ -416,7 +416,8 @@ ExpressionSyntax CreateExpressionToInline() } else if (isVar && expression is ObjectCreationExpressionSyntax or ArrayCreationExpressionSyntax or CastExpressionSyntax or InvocationExpressionSyntax) { - // if we have an InvocationExpressionSyntax `var x = Math.Round(1.1D);` there's no need to do any casting as x is var and the method return type is known. + // if we have an InvocationExpressionSyntax `var x = Math.Round(1.1D);` there's no need to do any casting + // as x's type will be entirely determined by the return type of the invoked expression. // if we have `var x = new Y();` there's no need to do any casting as the type is indicated // directly in the existing code. The same holds for `new Y[]` or `(Y)...` return expression; From 7a77389cb1c3d9377a0a0edcc9560f8156f59c1f Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Sat, 6 Apr 2024 18:35:37 +0300 Subject: [PATCH 069/508] Remove trailing whitespace --- .../FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs index 559f10a97f84d..9ff479c9971cb 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs @@ -163,7 +163,7 @@ public bool ContainsCollectionInitializer public bool ContainsDirective => (_containingNodes & ContainingNodes.ContainsDirective) == ContainingNodes.ContainsDirective; - + public void WriteTo(ObjectWriter writer) { writer.WriteInt32(_predefinedTypes); From 684ffeb1adde004f75678b77cdcabf7aa2c2f920 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Sat, 6 Apr 2024 20:45:11 +0300 Subject: [PATCH 070/508] Remove trailing whitespace --- .../Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs b/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs index a028c20ad87fd..0b78a77b904cb 100644 --- a/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs +++ b/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs @@ -149,7 +149,7 @@ MethodKind.Conversion or case SymbolKind.Preprocessing: return Glyph.Keyword; - + case SymbolKind.RangeVariable: return Glyph.RangeVariable; From 2c26786b65ce07fad0ecf874fe093c410d4388c9 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Sun, 7 Apr 2024 00:01:17 +0300 Subject: [PATCH 071/508] Revert unrelated changes --- .../CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs index ff2138718cfe0..602946b93212e 100644 --- a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs @@ -10,7 +10,6 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; -using System.Runtime.CompilerServices; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -211,6 +210,7 @@ internal override IOperation GetOperationWorker(CSharpSyntaxNode node, Cancellat SymbolInfo result; XmlNameAttributeSyntax attrSyntax; + CrefSyntax crefSyntax; if (model != null) { @@ -272,7 +272,7 @@ internal override IOperation GetOperationWorker(CSharpSyntaxNode node, Cancellat } } } - else if (node is CrefSyntax crefSyntax) + else if ((crefSyntax = node as CrefSyntax) != null) { int adjustedPosition = GetAdjustedNodePosition(crefSyntax); result = GetCrefSymbolInfo(adjustedPosition, crefSyntax, options, HasParameterList(crefSyntax)); From eea79f89c7dd25566991912aa6ccc141726f143d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 14 Aug 2024 13:49:52 -0700 Subject: [PATCH 072/508] Do not run 'remove unnecessary imports' on generated code --- .../RemoveUnnecessaryImportsTests.cs | 26 ++----------------- ...oveUnnecessaryImportsDiagnosticAnalyzer.cs | 2 -- 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryImports/RemoveUnnecessaryImportsTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryImports/RemoveUnnecessaryImportsTests.cs index d06499cad9610..c538aa68e5bf9 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryImports/RemoveUnnecessaryImportsTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryImports/RemoveUnnecessaryImportsTests.cs @@ -186,22 +186,9 @@ public async Task TestGeneratedCode() var source = """ // - [|{|IDE0005_gen:using System;|} - using System.Collections.Generic; - {|IDE0005_gen:using System.Linq;|}|] - - class Program - { - static void Main(string[] args) - { - List d; - } - } - """; - var fixedSource = """ - // - + using System; using System.Collections.Generic; + using System.Linq; class Program { @@ -212,18 +199,9 @@ static void Main(string[] args) } """; - // Fix All operations in generated code do not apply changes - var batchFixedSource = source; - await new VerifyCS.Test { TestCode = source, - FixedCode = fixedSource, - BatchFixedState = - { - Sources = { batchFixedSource }, - MarkupHandling = MarkupMode.Allow, - }, }.RunAsync(); } diff --git a/src/Analyzers/Core/Analyzers/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsDiagnosticAnalyzer.cs index 6c860d117931f..72325a9dc6379 100644 --- a/src/Analyzers/Core/Analyzers/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsDiagnosticAnalyzer.cs @@ -71,8 +71,6 @@ private static ImmutableArray GetDescriptors(LocalizableSt protected abstract bool IsRegularCommentOrDocComment(SyntaxTrivia trivia); protected abstract IUnnecessaryImportsProvider UnnecessaryImportsProvider { get; } - protected override GeneratedCodeAnalysisFlags GeneratedCodeAnalysisFlags => GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics; - protected abstract SyntaxToken? TryGetLastToken(SyntaxNode node); protected override void InitializeWorker(AnalysisContext context) From 2bdc6bdf7657705a20e8cdfdc0d90485bf4dbafa Mon Sep 17 00:00:00 2001 From: Rekkonnect <8298332+Rekkonnect@users.noreply.github.com> Date: Mon, 26 Aug 2024 02:41:32 +0300 Subject: [PATCH 073/508] Fix preprocessing symbol tests in LSP Also marks Location as nullable because of the special cases like pp symbols that do not have a definition location --- ...ditorLspReferencesResultCreationService.cs | 5 +- .../AbstractLanguageServerProtocolTests.cs | 19 ++++++- .../FindUsages/DefinitionItemFactory.cs | 4 +- .../Internal/VSInternalReferenceItem.cs | 2 +- .../FindAllReferencesHandlerTests.cs | 42 ++++++++------- .../PreprocessingSymbolReferenceFinder.cs | 54 +++++++++---------- 6 files changed, 69 insertions(+), 57 deletions(-) diff --git a/src/EditorFeatures/Core/LanguageServer/EditorLspReferencesResultCreationService.cs b/src/EditorFeatures/Core/LanguageServer/EditorLspReferencesResultCreationService.cs index 6dd91d0515498..3005866d892e9 100644 --- a/src/EditorFeatures/Core/LanguageServer/EditorLspReferencesResultCreationService.cs +++ b/src/EditorFeatures/Core/LanguageServer/EditorLspReferencesResultCreationService.cs @@ -40,6 +40,7 @@ internal sealed class EditorLspReferencesResultCreationService() : ILspReference DefinitionId = definitionId, DefinitionText = definitionText, // Only definitions should have a non-null DefinitionText DefinitionIcon = new ImageElement(imageId.ToLSPImageId()), + Location = location, DisplayPath = location?.Uri.LocalPath, Id = id, Kind = symbolUsageInfo.HasValue ? ProtocolConversions.SymbolUsageInfoToReferenceKinds(symbolUsageInfo.Value) : [], @@ -47,10 +48,6 @@ internal sealed class EditorLspReferencesResultCreationService() : ILspReference Text = text, }; - // There are certain items that may not have locations, such as namespace definitions. - if (location != null) - result.Location = location; - if (documentSpan != null) { result.DocumentName = documentSpan.Value.Document.Name; diff --git a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs index 764938a38ec41..77c2159a62ad8 100644 --- a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs +++ b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs @@ -102,9 +102,9 @@ public Task> MapSpansAsync(Document document, I } } - private protected class OrderLocations : Comparer + private protected class OrderLocations : Comparer { - public override int Compare(LSP.Location x, LSP.Location y) => CompareLocations(x, y); + public override int Compare(LSP.Location? x, LSP.Location? y) => CompareNullableLocations(x, y); } protected virtual TestComposition Composition => EditorFeaturesLspComposition; @@ -148,6 +148,21 @@ private protected static void AssertLocationsEqual(IEnumerable exp AssertJsonEquals(orderedExpectedLocations, orderedActualLocations); } + private protected static int CompareNullableLocations(LSP.Location? l1, LSP.Location? l2) + { + var equalReferences = ReferenceEquals(l1, l2); + if (equalReferences) + return 0; + + if (l1 is null) + return -1; + + if (l2 is null) + return 1; + + return CompareLocations(l1, l2); + } + private protected static int CompareLocations(LSP.Location l1, LSP.Location l2) { var compareDocument = l1.Uri.AbsoluteUri.CompareTo(l2.Uri.AbsoluteUri); diff --git a/src/Features/Core/Portable/FindUsages/DefinitionItemFactory.cs b/src/Features/Core/Portable/FindUsages/DefinitionItemFactory.cs index dbe6714f0003f..c50d0c0763b54 100644 --- a/src/Features/Core/Portable/FindUsages/DefinitionItemFactory.cs +++ b/src/Features/Core/Portable/FindUsages/DefinitionItemFactory.cs @@ -222,9 +222,9 @@ internal static ImmutableArray GetMetadataLocations(ISymbol de private static ImmutableArray GetSourceLocations(ISymbol definition, ImmutableArray locations, Solution solution, bool includeHiddenLocations) { - // Assembly, module and global namespace locations include all source documents; displaying all of them is not useful. + // Assembly, module, global namespace and preprocessing symbol locations include all source documents; displaying all of them is not useful. // We could consider creating a definition item that points to the project source instead. - if (definition is IAssemblySymbol or IModuleSymbol or INamespaceSymbol { IsGlobalNamespace: true }) + if (definition is IAssemblySymbol or IModuleSymbol or INamespaceSymbol { IsGlobalNamespace: true } or IPreprocessingSymbol) { return []; } diff --git a/src/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceItem.cs b/src/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceItem.cs index add3d0afb01b7..fefbd26e43797 100644 --- a/src/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceItem.cs +++ b/src/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceItem.cs @@ -31,7 +31,7 @@ public int Id /// Gets or sets the reference location. /// [JsonPropertyName("_vs_location")] - public Location Location + public Location? Location { get; set; diff --git a/src/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs b/src/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs index 2fcc32c3f19c3..4634f92454e24 100644 --- a/src/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs @@ -203,7 +203,7 @@ void M() await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, CapabilitiesWithVSExtensions); var results = await RunFindAllReferencesAsync(testLspServer, testLspServer.GetLocations("caret").First()); - Assert.NotNull(results[0].Location.Uri); + Assert.NotNull(results[0].Location!.Uri); AssertHighlightCount(results, expectedDefinitionCount: 0, expectedWrittenReferenceCount: 0, expectedReferenceCount: 1); } @@ -274,27 +274,29 @@ public async Task TestFindAllReferencesAsync_StaticClassification(bool mutatingL public async Task TestFindAllReferencesAsync_PreprocessingSymbol(bool mutatingLspWorkspace) { var markup = -@"#define {|reference:PREPROCESSING_SYMBOL|} -#define MORE_PREPROCESSING_SYMBOL - -#if {|reference:PREPROCESSING_SYMBOL|} -namespace SimpleNamespace; -#elif true && (!false || {|caret:|}{|reference:PREPROCESSING_SYMBOL|}) -namespace AnotherNamespace; -#elif MORE_PREPROCESSING_SYMBOL -namespace MoreSimpleNamespace; -#else -namespace ComplexNamespace; -#endif - -// PREPROCESSING_SYMBOL -class PREPROCESSING_SYMBOL -{ -} -"; + """ + #define {|reference:PREPROCESSING_SYMBOL|} + #define MORE_PREPROCESSING_SYMBOL + + #if {|reference:PREPROCESSING_SYMBOL|} + namespace SimpleNamespace; + #elif true && (!false || {|caret:|}{|reference:PREPROCESSING_SYMBOL|}) + namespace AnotherNamespace; + #elif MORE_PREPROCESSING_SYMBOL + namespace MoreSimpleNamespace; + #else + namespace ComplexNamespace; + #endif + + // PREPROCESSING_SYMBOL + class PREPROCESSING_SYMBOL + { + } + + """; await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, CapabilitiesWithVSExtensions); - var results = await RunFindAllReferencesAsync(testLspServer, testLspServer.GetLocations("caret").First()); + var results = await RunFindAllReferencesAsync(testLspServer, testLspServer.GetLocations("caret").First()); // Do not assert the glyph AssertHighlightCount(results, expectedDefinitionCount: 0, expectedWrittenReferenceCount: 0, expectedReferenceCount: 3); diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs index 374eefc27ea6f..fcf44e61c5427 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; @@ -16,49 +17,46 @@ internal sealed class PreprocessingSymbolReferenceFinder : AbstractReferenceFind protected override bool CanFind(IPreprocessingSymbol symbol) => true; - protected sealed override async ValueTask> FindReferencesInDocumentAsync( + protected override async Task DetermineDocumentsToSearchAsync( IPreprocessingSymbol symbol, - FindReferencesDocumentState state, + HashSet? globalAliases, + Project project, + IImmutableSet? documents, + Action processResult, + TData processResultData, FindReferencesSearchOptions options, CancellationToken cancellationToken) { - var tokens = await FindMatchingIdentifierTokensAsync(state, symbol.Name, cancellationToken).ConfigureAwait(false); + await FindDocumentsWithPredicateAsync(project, documents, DetermineDocument, processResult, processResultData, cancellationToken).ConfigureAwait(false); - using var _ = ArrayBuilder.GetInstance(out var locations); - foreach (var token in tokens) + bool DetermineDocument(SyntaxTreeIndex syntaxTreeIndex) { - cancellationToken.ThrowIfCancellationRequested(); - - var targetSymbol = state.SemanticModel.GetPreprocessingSymbolInfo(token.GetRequiredParent()).Symbol; - var matched = await SymbolFinder.OriginalSymbolsMatchAsync(state.Solution, symbol, targetSymbol, cancellationToken).ConfigureAwait(false); - - if (matched) - locations.Add(CreateFinderLocation(state, token, CandidateReason.None, cancellationToken)); + return syntaxTreeIndex.ContainsDirective && syntaxTreeIndex.ProbablyContainsIdentifier(symbol.Name); } - - return locations.ToImmutable(); } - protected override async Task> DetermineDocumentsToSearchAsync( + protected override void FindReferencesInDocument( IPreprocessingSymbol symbol, - HashSet? globalAliases, - Project project, - IImmutableSet? documents, + FindReferencesDocumentState state, + Action processResult, + TData processResultData, FindReferencesSearchOptions options, CancellationToken cancellationToken) { - using var _ = ArrayBuilder.GetInstance(out var resultDocuments); + var tokens = FindMatchingIdentifierTokens(state, symbol.Name, cancellationToken); - var projectDocuments = (IEnumerable?)documents - ?? await project.GetAllRegularAndSourceGeneratedDocumentsAsync(cancellationToken).ConfigureAwait(false); - - foreach (var document in projectDocuments) + foreach (var token in tokens) { - var syntaxTreeIndex = await document.GetSyntaxTreeIndexAsync(cancellationToken).ConfigureAwait(false); - if (syntaxTreeIndex.ContainsDirective && syntaxTreeIndex.ProbablyContainsIdentifier(symbol.Name)) - resultDocuments.Add(document); - } + cancellationToken.ThrowIfCancellationRequested(); + + var targetSymbol = state.SemanticModel.GetPreprocessingSymbolInfo(token.GetRequiredParent()).Symbol; + var matched = SymbolFinder.OriginalSymbolsMatch(state.Solution, symbol, targetSymbol); - return resultDocuments.ToImmutable(); + if (matched) + { + var location = CreateFinderLocation(state, token, CandidateReason.None, cancellationToken); + processResult(location, processResultData); + } + } } } From ee163d84e018c605a6154484673025c1305f82e2 Mon Sep 17 00:00:00 2001 From: Rekkonnect <8298332+Rekkonnect@users.noreply.github.com> Date: Mon, 26 Aug 2024 02:50:19 +0300 Subject: [PATCH 074/508] Support pp symbols in CanBeReferencedByName --- src/Compilers/VisualBasic/Portable/Symbols/Symbol.vb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Symbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Symbol.vb index cac4cfd3bedd2..17e8d23450c6b 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Symbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Symbol.vb @@ -604,7 +604,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic SymbolKind.Event, SymbolKind.Parameter, SymbolKind.TypeParameter, - SymbolKind.ErrorType + SymbolKind.ErrorType, + SymbolKind.Preprocessing Exit Select Case SymbolKind.NamedType From b363549991090a6d87d25eb3fbc33d69c90e93da Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 3 Oct 2024 11:22:49 -0700 Subject: [PATCH 075/508] Add initial tests --- .../Test/Semantic/Semantics/NameOfTests.cs | 137 +++++++++++++++++- 1 file changed, 136 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index 572f5f8ddd22d..3cf9634d44994 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -16,7 +16,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests { - public class NameofTests : CSharpTestBase + public sealed class NameofTests : CSharpTestBase { [Fact] public void TestGoodNameofInstances() @@ -2373,5 +2373,140 @@ class Attr : System.Attribute { public Attr(string s) {} }"; CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics(expectedDiagnostics); } + + [Fact] + public void OpenTypeInNameof_CSharp13() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List<>); + Console.WriteLine(v); + """, parseOptions: TestOptions.Regular13).VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_BaseCase() + { + CompileAndVerify( + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List<>); + Console.WriteLine(v); + """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(), + expectedOutput: "List"); + } + + [Fact] + public void OpenTypeInNameof_MultipleTypeArguments() + { + CompileAndVerify( + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(Dictionary<,>); + Console.WriteLine(v); + """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(), + expectedOutput: "Dictionary"); + } + + [Fact] + public void OpenTypeInNameof_NoNestedOpenTypes() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List>); + Console.WriteLine(v); + """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_NoPartialOpenTypes() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(Dictionary<,int>); + Console.WriteLine(v); + """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_MemberAccessThatDoesNotUseTypeArgument() + { + CompileAndVerify( + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List<>.Count); + Console.WriteLine(v); + """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(), + expectedOutput: "Count"); + } + + [Fact] + public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument() + { + CompileAndVerify( + CreateCompilation(""" + using System; + using System.Collections.Generic; + + interface IGoo + { + T Count { get; } + } + + var v = nameof(IGoo<>.Count); + Console.WriteLine(v); + """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(), + expectedOutput: "Count"); + } + + [Fact] + public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument_ReferenceObjectMember() + { + CompileAndVerify( + CreateCompilation(""" + using System; + using System.Collections.Generic; + + interface IGoo + { + T Count { get; } + } + + var v = nameof(IGoo<>.Count.ToString); + Console.WriteLine(v); + """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(), + expectedOutput: "ToString"); + } + + [Fact] + public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument_ReferenceConstraintMember() + { + CompileAndVerify( + CreateCompilation(""" + using System; + using System.Collections.Generic; + + interface IGoo where T : IComparable + { + T X { get; } + } + + var v = nameof(IGoo<>.Count.X.CompareTo); + Console.WriteLine(v); + """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(), + expectedOutput: "CompareTo"); + } } } From f1f276b405126c629843e46fd3f5fcaeedb6b3a1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 3 Oct 2024 12:00:51 -0700 Subject: [PATCH 076/508] Initial work --- .../CSharp/Portable/Binder/Binder_Symbols.cs | 42 +++--- .../CSharp/Portable/CSharpResources.resx | 3 + .../CSharp/Portable/Errors/MessageID.cs | 3 + .../Portable/xlf/CSharpResources.cs.xlf | 5 + .../Portable/xlf/CSharpResources.de.xlf | 5 + .../Portable/xlf/CSharpResources.es.xlf | 5 + .../Portable/xlf/CSharpResources.fr.xlf | 5 + .../Portable/xlf/CSharpResources.it.xlf | 5 + .../Portable/xlf/CSharpResources.ja.xlf | 5 + .../Portable/xlf/CSharpResources.ko.xlf | 5 + .../Portable/xlf/CSharpResources.pl.xlf | 5 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 5 + .../Portable/xlf/CSharpResources.ru.xlf | 5 + .../Portable/xlf/CSharpResources.tr.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 5 + .../Test/Semantic/Semantics/NameOfTests.cs | 131 +++++++++++++++++- 17 files changed, 227 insertions(+), 17 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index c35c4512565cd..079a5c04668c4 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -1212,7 +1212,7 @@ private TypeWithAnnotations BindGenericSimpleNamespaceOrTypeOrAliasSymbol( if (isUnboundTypeExpr) { - if (!IsUnboundTypeAllowed(node)) + if (!IsInsideNameof && !IsUnboundTypeAllowed(node)) { // If we already have an error type then skip reporting that the unbound type is illegal. if (!unconstructedType.IsErrorType()) @@ -1373,22 +1373,32 @@ private NamedTypeSymbol ConstructNamedTypeUnlessTypeArgumentOmitted(SyntaxNode t { if (typeArgumentsSyntax.Any(SyntaxKind.OmittedTypeArgument)) { - // Note: lookup won't have reported this, since the arity was correct. - // CONSIDER: the text of this error message makes sense, but we might want to add a separate code. - Error(diagnostics, ErrorCode.ERR_BadArity, typeSyntax, type, MessageID.IDS_SK_TYPE.Localize(), typeArgumentsSyntax.Count); - - // If the syntax looks like an unbound generic type, then they probably wanted the definition. - // Give an error indicating that the syntax is incorrect and then use the definition. - // CONSIDER: we could construct an unbound generic type symbol, but that would probably be confusing - // outside a typeof. - return type; - } - else - { - // we pass an empty basesBeingResolved here because this invocation is not on any possible path of - // infinite recursion in binding base clauses. - return ConstructNamedType(type, typeSyntax, typeArgumentsSyntax, typeArguments, basesBeingResolved: null, diagnostics: diagnostics); + if (this.IsInsideNameof) + { + // Inside a nameof an open-generic type is acceptable. Fall through and bind the remainder accordingly. + CheckFeatureAvailability(typeSyntax, MessageID.IDS_FeatureOpenTypeInNameof, diagnostics); + typeArguments = UnboundArgumentErrorTypeSymbol.CreateTypeArguments( + type.TypeParameters, + type.TypeParameters.Length, + errorInfo: null); + } + else + { + // Note: lookup won't have reported this, since the arity was correct. + // CONSIDER: the text of this error message makes sense, but we might want to add a separate code. + Error(diagnostics, ErrorCode.ERR_BadArity, typeSyntax, type, MessageID.IDS_SK_TYPE.Localize(), typeArgumentsSyntax.Count); + + // If the syntax looks like an unbound generic type, then they probably wanted the definition. + // Give an error indicating that the syntax is incorrect and then use the definition. + // CONSIDER: we could construct an unbound generic type symbol, but that would probably be confusing + // outside a typeof. + return type; + } } + + // we pass an empty basesBeingResolved here because this invocation is not on any possible path of + // infinite recursion in binding base clauses. + return ConstructNamedType(type, typeSyntax, typeArgumentsSyntax, typeArguments, basesBeingResolved: null, diagnostics: diagnostics); } /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 4210d8664c209..7ec2cc161e79b 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -4926,6 +4926,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ nameof operator + + Open generic types in nameof operator + dictionary initializer diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index e7b0601c7c545..fc738ddef1990 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -292,6 +292,8 @@ internal enum MessageID IDS_FeatureAllowsRefStructConstraint = MessageBase + 12847, IDS_OverloadResolutionPriority = MessageBase + 12848, + + IDS_FeatureOpenTypeInNameof = MessageBase + 12849, } // Message IDs may refer to strings that need to be localized. @@ -473,6 +475,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) // C# preview features. case MessageID.IDS_FeatureFieldKeyword: + case MessageID.IDS_FeatureOpenTypeInNameof: return LanguageVersion.Preview; // C# 13.0 features. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 88e737c1beb27..5ec0a6129833a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -2497,6 +2497,11 @@ nové řádky v interpolacích + + Open generic types in nameof operator + Open generic types in nameof operator + + parameterless struct constructors konstruktory struktury bez parametrů diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 7791b4941b495..504b9777b379f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -2497,6 +2497,11 @@ Zeilenumbrüche in Interpolationen + + Open generic types in nameof operator + Open generic types in nameof operator + + parameterless struct constructors Parameterlose Strukturkonstruktoren diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 49dc784c465a9..fc3b27f7e9b05 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -2497,6 +2497,11 @@ Nuevas líneas en interpolaciones + + Open generic types in nameof operator + Open generic types in nameof operator + + parameterless struct constructors constructores de estructuras sin parámetros diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index ebc8306793f12..17773b98e1355 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -2497,6 +2497,11 @@ sauts de ligne dans les interpolations + + Open generic types in nameof operator + Open generic types in nameof operator + + parameterless struct constructors constructeurs de struct sans paramètre diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 268e0502cd458..88133a0470931 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -2497,6 +2497,11 @@ nuove linee nelle interpolazioni + + Open generic types in nameof operator + Open generic types in nameof operator + + parameterless struct constructors costruttori struct senza parametri diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 7ac7b47912be2..67304ba3e1e4d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -2497,6 +2497,11 @@ 補間における改行 + + Open generic types in nameof operator + Open generic types in nameof operator + + parameterless struct constructors パラメーターのない構造体コンストラクター diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 1c3127f912223..5a2323ea3fbc6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -2497,6 +2497,11 @@ 보간에서 줄 바꿈 + + Open generic types in nameof operator + Open generic types in nameof operator + + parameterless struct constructors 매개 변수 없는 구조체 생성자 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 4a0500365e1e0..35f821350ac49 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -2497,6 +2497,11 @@ nowe wiersze w interpolacjach + + Open generic types in nameof operator + Open generic types in nameof operator + + parameterless struct constructors Konstruktory struktury bez parametrów diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 2c3070ffc18a6..4fdd79bc09bc7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -2497,6 +2497,11 @@ novas linhas em interpolações + + Open generic types in nameof operator + Open generic types in nameof operator + + parameterless struct constructors construtores struct sem parâmetros diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 5ad07dbfa5172..1ce13800d4a40 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -2497,6 +2497,11 @@ новые линии в интерполяции + + Open generic types in nameof operator + Open generic types in nameof operator + + parameterless struct constructors конструкторы структуры без параметров diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index f6b9d136c9e3c..73d03ea9f3418 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -2497,6 +2497,11 @@ ilişkilendirmedeki yeni satırlar + + Open generic types in nameof operator + Open generic types in nameof operator + + parameterless struct constructors parametresiz yapı oluşturucuları diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 430ad886cfb0a..ef27a87597d14 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -2497,6 +2497,11 @@ 内插中的换行符 + + Open generic types in nameof operator + Open generic types in nameof operator + + parameterless struct constructors 参数结构构造函数 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 6d362a02305c7..a1f23a79f900b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -2497,6 +2497,11 @@ 插補中的新行 + + Open generic types in nameof operator + Open generic types in nameof operator + + parameterless struct constructors 無參數結構建構函式 diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index 3cf9634d44994..ae9334fa7c863 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -2383,6 +2383,51 @@ public void OpenTypeInNameof_CSharp13() var v = nameof(List<>); Console.WriteLine(v); + """, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (4,16): error CS8652: The feature 'Open generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var v = nameof(List<>); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "List<>").WithArguments("Open generic types in nameof operator").WithLocation(4, 16)); + } + + [Fact] + public void OpenTypeInNameof_CSharp13_Nested1() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(A<>.B); + Console.WriteLine(v); + + class A { public class B; } + """, parseOptions: TestOptions.Regular13).VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_CSharp13_Nested2() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(A.B<>); + Console.WriteLine(v); + + class A { public class B; } + """, parseOptions: TestOptions.Regular13).VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_CSharp13_Nested3() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(A<>.B<>); + Console.WriteLine(v); + + class A { class B; } """, parseOptions: TestOptions.Regular13).VerifyDiagnostics(); } @@ -2400,6 +2445,54 @@ public void OpenTypeInNameof_BaseCase() expectedOutput: "List"); } + [Fact] + public void OpenTypeInNameof_Nested1() + { + CompileAndVerify( + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(A<>.B); + Console.WriteLine(v); + + class A { public class B; } + """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(), + expectedOutput: "B"); + } + + [Fact] + public void OpenTypeInNameof_Nested2() + { + CompileAndVerify( + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(A.B<>); + Console.WriteLine(v); + + class A { public class B; } + """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(), + expectedOutput: "B"); + } + + [Fact] + public void OpenTypeInNameof_Nested3() + { + CompileAndVerify( + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(A<>.B<>); + Console.WriteLine(v); + + class A { public class B; } + """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(), + expectedOutput: "B"); + } + [Fact] public void OpenTypeInNameof_MultipleTypeArguments() { @@ -2414,6 +2507,30 @@ public void OpenTypeInNameof_MultipleTypeArguments() expectedOutput: "Dictionary"); } + [Fact] + public void OpenTypeInNameof_IncorrectTypeArgumentCount1() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(Dictionary<>); + Console.WriteLine(v); + """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_IncorrectTypeArgumentCount2() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List<,>); + Console.WriteLine(v); + """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(); + } + [Fact] public void OpenTypeInNameof_NoNestedOpenTypes() { @@ -2427,7 +2544,7 @@ public void OpenTypeInNameof_NoNestedOpenTypes() } [Fact] - public void OpenTypeInNameof_NoPartialOpenTypes() + public void OpenTypeInNameof_NoPartialOpenTypes_1() { CreateCompilation(""" using System; @@ -2438,6 +2555,18 @@ public void OpenTypeInNameof_NoPartialOpenTypes() """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(); } + [Fact] + public void OpenTypeInNameof_NoPartialOpenTypes_2() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(Dictionary); + Console.WriteLine(v); + """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(); + } + [Fact] public void OpenTypeInNameof_MemberAccessThatDoesNotUseTypeArgument() { From d5d1423864190a0caf9ef8b041e2df7a9bdcc211 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 3 Oct 2024 12:03:18 -0700 Subject: [PATCH 077/508] more working --- .../Test/Semantic/Semantics/NameOfTests.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index ae9334fa7c863..c8c5ea221f451 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -2451,7 +2451,6 @@ public void OpenTypeInNameof_Nested1() CompileAndVerify( CreateCompilation(""" using System; - using System.Collections.Generic; var v = nameof(A<>.B); Console.WriteLine(v); @@ -2467,7 +2466,6 @@ public void OpenTypeInNameof_Nested2() CompileAndVerify( CreateCompilation(""" using System; - using System.Collections.Generic; var v = nameof(A.B<>); Console.WriteLine(v); @@ -2483,7 +2481,6 @@ public void OpenTypeInNameof_Nested3() CompileAndVerify( CreateCompilation(""" using System; - using System.Collections.Generic; var v = nameof(A<>.B<>); Console.WriteLine(v); @@ -2516,7 +2513,13 @@ public void OpenTypeInNameof_IncorrectTypeArgumentCount1() var v = nameof(Dictionary<>); Console.WriteLine(v); - """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(); + """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (2,1): hidden CS8019: Unnecessary using directive. + // using System.Collections.Generic; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using System.Collections.Generic;").WithLocation(2, 1), + // (4,16): error CS0305: Using the generic type 'Dictionary' requires 2 type arguments + // var v = nameof(Dictionary<>); + Diagnostic(ErrorCode.ERR_BadArity, "Dictionary<>").WithArguments("System.Collections.Generic.Dictionary", "type", "2").WithLocation(4, 16)); } [Fact] @@ -2528,7 +2531,13 @@ public void OpenTypeInNameof_IncorrectTypeArgumentCount2() var v = nameof(List<,>); Console.WriteLine(v); - """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(); + """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (2,1): hidden CS8019: Unnecessary using directive. + // using System.Collections.Generic; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using System.Collections.Generic;").WithLocation(2, 1), + // (4,16): error CS0305: Using the generic type 'List' requires 1 type arguments + // var v = nameof(List<,>); + Diagnostic(ErrorCode.ERR_BadArity, "List<,>").WithArguments("System.Collections.Generic.List", "type", "1").WithLocation(4, 16)); } [Fact] From f16971d19c1bbbdb0a01f250b02cbc592d00162a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 3 Oct 2024 12:13:22 -0700 Subject: [PATCH 078/508] Better parsing --- src/Compilers/CSharp/Portable/Parser/LanguageParser.cs | 9 +++++++++ .../CSharp/Test/Semantic/Semantics/NameOfTests.cs | 10 ++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 1b2c4707840fb..1f4a35c326fa8 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -6104,6 +6104,15 @@ private ScanTypeFlags ScanPossibleTypeArgumentList( return result; } + // Allow for any chain of errant commas in teh generic name. like `Dictionary<,int>` or + // `Dictionary` We still want to think of these as generics, just with missing type-arguments, vs + // some invalid tree-expression that we would otherwise form. + if (this.CurrentToken.Kind == SyntaxKind.CommaToken) + { + lastScannedType = default; + continue; + } + lastScannedType = this.ScanType(out _); switch (lastScannedType) { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index c8c5ea221f451..d1c1c243d916a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -2561,7 +2561,10 @@ public void OpenTypeInNameof_NoPartialOpenTypes_1() var v = nameof(Dictionary<,int>); Console.WriteLine(v); - """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(); + """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (4,27): error CS1031: Type expected + // var v = nameof(Dictionary<,int>); + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(4, 27)); } [Fact] @@ -2573,7 +2576,10 @@ public void OpenTypeInNameof_NoPartialOpenTypes_2() var v = nameof(Dictionary); Console.WriteLine(v); - """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(); + """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (4,31): error CS1031: Type expected + // var v = nameof(Dictionary); + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(4, 31)); } [Fact] From 6d9a0682296aff720673fa8cb777706c84531dc1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 3 Oct 2024 12:17:12 -0700 Subject: [PATCH 079/508] more work --- .../Test/Semantic/Semantics/NameOfTests.cs | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index d1c1c243d916a..0f2024536bab2 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -2394,13 +2394,15 @@ public void OpenTypeInNameof_CSharp13_Nested1() { CreateCompilation(""" using System; - using System.Collections.Generic; var v = nameof(A<>.B); Console.WriteLine(v); class A { public class B; } - """, parseOptions: TestOptions.Regular13).VerifyDiagnostics(); + """, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (3,16): error CS8652: The feature 'Open generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var v = nameof(A<>.B); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "A<>").WithArguments("Open generic types in nameof operator").WithLocation(3, 16)); } [Fact] @@ -2408,13 +2410,15 @@ public void OpenTypeInNameof_CSharp13_Nested2() { CreateCompilation(""" using System; - using System.Collections.Generic; var v = nameof(A.B<>); Console.WriteLine(v); class A { public class B; } - """, parseOptions: TestOptions.Regular13).VerifyDiagnostics(); + """, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (3,23): error CS8652: The feature 'Open generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var v = nameof(A.B<>); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "B<>").WithArguments("Open generic types in nameof operator").WithLocation(3, 23)); } [Fact] @@ -2422,13 +2426,18 @@ public void OpenTypeInNameof_CSharp13_Nested3() { CreateCompilation(""" using System; - using System.Collections.Generic; var v = nameof(A<>.B<>); Console.WriteLine(v); - class A { class B; } - """, parseOptions: TestOptions.Regular13).VerifyDiagnostics(); + class A { public class B; } + """, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (3,16): error CS8652: The feature 'Open generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var v = nameof(A<>.B<>); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "A<>").WithArguments("Open generic types in nameof operator").WithLocation(3, 16), + // (3,20): error CS8652: The feature 'Open generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var v = nameof(A<>.B<>); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "B<>").WithArguments("Open generic types in nameof operator").WithLocation(3, 20)); } [Fact] From b4ff542dd062613e66faddc41570bbe642a7934b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 3 Oct 2024 12:31:03 -0700 Subject: [PATCH 080/508] names --- .../Portable/Binder/Binder_Invocation.cs | 28 +++++++++++++++++++ .../CSharp/Portable/CSharpResources.resx | 7 +++-- .../CSharp/Portable/Errors/ErrorCode.cs | 2 ++ .../CSharp/Portable/Errors/MessageID.cs | 2 +- .../Portable/xlf/CSharpResources.cs.xlf | 15 ++++++---- .../Portable/xlf/CSharpResources.de.xlf | 15 ++++++---- .../Portable/xlf/CSharpResources.es.xlf | 15 ++++++---- .../Portable/xlf/CSharpResources.fr.xlf | 15 ++++++---- .../Portable/xlf/CSharpResources.it.xlf | 15 ++++++---- .../Portable/xlf/CSharpResources.ja.xlf | 15 ++++++---- .../Portable/xlf/CSharpResources.ko.xlf | 15 ++++++---- .../Portable/xlf/CSharpResources.pl.xlf | 15 ++++++---- .../Portable/xlf/CSharpResources.pt-BR.xlf | 15 ++++++---- .../Portable/xlf/CSharpResources.ru.xlf | 15 ++++++---- .../Portable/xlf/CSharpResources.tr.xlf | 15 ++++++---- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 15 ++++++---- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 15 ++++++---- 17 files changed, 166 insertions(+), 68 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index b5885aa1e4af0..060ef2cbab72a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -2273,6 +2273,9 @@ private BoundExpression BindNameofOperatorInternal(InvocationExpressionSyntax no { CheckFeatureAvailability(node, MessageID.IDS_FeatureNameof, diagnostics); var argument = node.ArgumentList.Arguments[0].Expression; + + validateOpenGenericNames(argument, topLevel: true); + var boundArgument = BindExpression(argument, diagnostics); bool syntaxIsOk = CheckSyntaxForNameofArgument(argument, out string name, boundArgument.HasAnyErrors ? BindingDiagnosticBag.Discarded : diagnostics); @@ -2297,6 +2300,31 @@ private BoundExpression BindNameofOperatorInternal(InvocationExpressionSyntax no boundArgument = BindToNaturalType(boundArgument, diagnostics, reportNoTargetType: false); return new BoundNameOfOperator(node, boundArgument, ConstantValue.Create(name), Compilation.GetSpecialType(SpecialType.System_String)); + + void validateOpenGenericNames(SyntaxNode current, bool topLevel) + { + if (current is TypeArgumentListSyntax typeArgumentList) + { + if (!topLevel) + { + foreach (var typeArgument in typeArgumentList.Arguments) + { + if (typeArgument.Kind() == SyntaxKind.OmittedTypeArgument) + diagnostics.Add(ErrorCode.ERR_NestedUnboundTypeNotAllowedInNameofExpression, typeArgumentList.Parent); + } + } + + // Once we hit a type argument list, we're no longer at the top level, and we want to report errors + // for any omitted type arguments we see at a deeper level. + topLevel = false; + } + + foreach (var child in current.ChildNodesAndTokens()) + { + if (child.IsNode) + validateOpenGenericNames(child.AsNode(), topLevel); + } + } } private void EnsureNameofExpressionSymbols(BoundMethodGroup methodGroup, BindingDiagnosticBag diagnostics) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 7ec2cc161e79b..8c2a8f3d85427 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -4926,8 +4926,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ nameof operator - - Open generic types in nameof operator + + Unbound generic types in nameof operator dictionary initializer @@ -8005,4 +8005,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Non-nullable property must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + + Nested unbound generic type not allowed in 'nameof' operator + diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 7c07bb82d271e..f8e35c71b849d 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2345,6 +2345,8 @@ internal enum ErrorCode ERR_CannotApplyOverloadResolutionPriorityToMember = 9262, ERR_PartialPropertyDuplicateInitializer = 9263, + ERR_NestedUnboundTypeNotAllowedInNameofExpression = 9270, + WRN_UninitializedNonNullableBackingField = 9264, // Note: you will need to do the following after adding errors: diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index fc738ddef1990..69f3ef9e592d9 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -293,7 +293,7 @@ internal enum MessageID IDS_FeatureAllowsRefStructConstraint = MessageBase + 12847, IDS_OverloadResolutionPriority = MessageBase + 12848, - IDS_FeatureOpenTypeInNameof = MessageBase + 12849, + IDS_FeatureUnboundGenericTypesInNameof = MessageBase + 12849, } // Message IDs may refer to strings that need to be localized. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 5ec0a6129833a..bcf84888d9b9d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -1452,6 +1452,11 @@ Přístup k vloženému poli nemůže mít specifikátor pojmenovaného argumentu. + + Nested unbound generic type not allowed in 'nameof' operator + Nested unbound generic type not allowed in 'nameof' operator + + The 'new()' constraint cannot be used with the 'unmanaged' constraint Omezení new() nejde používat s omezením unmanaged. @@ -2497,11 +2502,6 @@ nové řádky v interpolacích - - Open generic types in nameof operator - Open generic types in nameof operator - - parameterless struct constructors konstruktory struktury bez parametrů @@ -2577,6 +2577,11 @@ Inicializátory polí struktury + + Unbound generic types in nameof operator + Unbound generic types in nameof operator + + unsigned right shift nepodepsaný pravý posun diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 504b9777b379f..e8f86c296ff16 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -1452,6 +1452,11 @@ Ein Inlinearrayzugriff verfügt möglicherweise nicht über einen benannten Argumentspezifizierer. + + Nested unbound generic type not allowed in 'nameof' operator + Nested unbound generic type not allowed in 'nameof' operator + + The 'new()' constraint cannot be used with the 'unmanaged' constraint Die new()-Einschränkung kann nicht mit der unmanaged-Einschränkung verwendet werden. @@ -2497,11 +2502,6 @@ Zeilenumbrüche in Interpolationen - - Open generic types in nameof operator - Open generic types in nameof operator - - parameterless struct constructors Parameterlose Strukturkonstruktoren @@ -2577,6 +2577,11 @@ Strukturfeldinitialisierer + + Unbound generic types in nameof operator + Unbound generic types in nameof operator + + unsigned right shift Unsignierte Rechtsverschiebung diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index fc3b27f7e9b05..05899a2868505 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -1452,6 +1452,11 @@ Un acceso de matriz insertado no puede tener un especificador de argumento con nombre + + Nested unbound generic type not allowed in 'nameof' operator + Nested unbound generic type not allowed in 'nameof' operator + + The 'new()' constraint cannot be used with the 'unmanaged' constraint La restricción "new()" no se puede utilizar con la restricción "unmanaged" @@ -2497,11 +2502,6 @@ Nuevas líneas en interpolaciones - - Open generic types in nameof operator - Open generic types in nameof operator - - parameterless struct constructors constructores de estructuras sin parámetros @@ -2577,6 +2577,11 @@ inicializadores de campo de estructura + + Unbound generic types in nameof operator + Unbound generic types in nameof operator + + unsigned right shift cambio derecho sin firmar diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 17773b98e1355..e92fa9d3466e8 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -1452,6 +1452,11 @@ Un accès au tableau en ligne peut ne pas avoir de spécificateur d'argument nommé + + Nested unbound generic type not allowed in 'nameof' operator + Nested unbound generic type not allowed in 'nameof' operator + + The 'new()' constraint cannot be used with the 'unmanaged' constraint La contrainte 'new()' ne peut pas être utilisée avec la contrainte 'unmanaged' @@ -2497,11 +2502,6 @@ sauts de ligne dans les interpolations - - Open generic types in nameof operator - Open generic types in nameof operator - - parameterless struct constructors constructeurs de struct sans paramètre @@ -2577,6 +2577,11 @@ initialiseurs de champ de struct + + Unbound generic types in nameof operator + Unbound generic types in nameof operator + + unsigned right shift shift droit non signé diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 88133a0470931..7b1615bb369b1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -1452,6 +1452,11 @@ Un accesso a matrice inline non può includere un identificatore di argomento denominato + + Nested unbound generic type not allowed in 'nameof' operator + Nested unbound generic type not allowed in 'nameof' operator + + The 'new()' constraint cannot be used with the 'unmanaged' constraint Non è possibile usare il vincolo 'new()' con il vincolo 'unmanaged' @@ -2497,11 +2502,6 @@ nuove linee nelle interpolazioni - - Open generic types in nameof operator - Open generic types in nameof operator - - parameterless struct constructors costruttori struct senza parametri @@ -2577,6 +2577,11 @@ inizializzatori di campo struct + + Unbound generic types in nameof operator + Unbound generic types in nameof operator + + unsigned right shift spostamento a destra senza segno diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 67304ba3e1e4d..d599d0bee034a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -1452,6 +1452,11 @@ インライン配列のアクセスには名前付き引数の指定子を指定できません + + Nested unbound generic type not allowed in 'nameof' operator + Nested unbound generic type not allowed in 'nameof' operator + + The 'new()' constraint cannot be used with the 'unmanaged' constraint new()' 制約は 'unmanaged' 制約と一緒には使用できません @@ -2497,11 +2502,6 @@ 補間における改行 - - Open generic types in nameof operator - Open generic types in nameof operator - - parameterless struct constructors パラメーターのない構造体コンストラクター @@ -2577,6 +2577,11 @@ 構造体フィールド初期化子 + + Unbound generic types in nameof operator + Unbound generic types in nameof operator + + unsigned right shift 符号なし右シフト diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 5a2323ea3fbc6..b4031f12c35b6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -1452,6 +1452,11 @@ 인라인 배열 액세스에는 명명된 인수 지정자가 없을 수 있습니다. + + Nested unbound generic type not allowed in 'nameof' operator + Nested unbound generic type not allowed in 'nameof' operator + + The 'new()' constraint cannot be used with the 'unmanaged' constraint new()' 제약 조건은 'unmanaged' 제약 조건과 함께 사용할 수 없습니다. @@ -2497,11 +2502,6 @@ 보간에서 줄 바꿈 - - Open generic types in nameof operator - Open generic types in nameof operator - - parameterless struct constructors 매개 변수 없는 구조체 생성자 @@ -2577,6 +2577,11 @@ 구조체 필드 이니셜라이저 + + Unbound generic types in nameof operator + Unbound generic types in nameof operator + + unsigned right shift 부호 없는 오른쪽 시프트 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 35f821350ac49..c05d86c27269a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -1452,6 +1452,11 @@ Dostęp do tablicy śródwierszowej nie może mieć specyfikatora argumentu nazwanego + + Nested unbound generic type not allowed in 'nameof' operator + Nested unbound generic type not allowed in 'nameof' operator + + The 'new()' constraint cannot be used with the 'unmanaged' constraint Ograniczenie „new()” nie może być używane z ograniczeniem „unmanaged” @@ -2497,11 +2502,6 @@ nowe wiersze w interpolacjach - - Open generic types in nameof operator - Open generic types in nameof operator - - parameterless struct constructors Konstruktory struktury bez parametrów @@ -2577,6 +2577,11 @@ inicjatory pola struktury + + Unbound generic types in nameof operator + Unbound generic types in nameof operator + + unsigned right shift niepodpisane przesunięcie w prawo diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 4fdd79bc09bc7..d7a0c6c7250a3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -1452,6 +1452,11 @@ Um acesso à matriz não pode ter um especificador de argumento nomeado + + Nested unbound generic type not allowed in 'nameof' operator + Nested unbound generic type not allowed in 'nameof' operator + + The 'new()' constraint cannot be used with the 'unmanaged' constraint A restrição 'new()' não pode ser usada com a restrição 'unmanaged' @@ -2497,11 +2502,6 @@ novas linhas em interpolações - - Open generic types in nameof operator - Open generic types in nameof operator - - parameterless struct constructors construtores struct sem parâmetros @@ -2577,6 +2577,11 @@ inicializadores de campo de struct + + Unbound generic types in nameof operator + Unbound generic types in nameof operator + + unsigned right shift deslocamento direito não atribuído diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 1ce13800d4a40..e66949fa608e1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -1452,6 +1452,11 @@ Доступ к встроенному массиву может не иметь спецификатора именованного аргумента. + + Nested unbound generic type not allowed in 'nameof' operator + Nested unbound generic type not allowed in 'nameof' operator + + The 'new()' constraint cannot be used with the 'unmanaged' constraint Ограничение "new()" невозможно использовать вместе с ограничением "unmanaged" @@ -2497,11 +2502,6 @@ новые линии в интерполяции - - Open generic types in nameof operator - Open generic types in nameof operator - - parameterless struct constructors конструкторы структуры без параметров @@ -2577,6 +2577,11 @@ инициализаторы полей структуры + + Unbound generic types in nameof operator + Unbound generic types in nameof operator + + unsigned right shift сдвиг вправо без знака diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 73d03ea9f3418..63c9552944960 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -1452,6 +1452,11 @@ Satır içi dizi erişiminin adlandırılmış bağımsız değişken belirticisi olamaz + + Nested unbound generic type not allowed in 'nameof' operator + Nested unbound generic type not allowed in 'nameof' operator + + The 'new()' constraint cannot be used with the 'unmanaged' constraint 'new()' kısıtlaması, 'unmanaged' kısıtlamasıyla kullanılamaz @@ -2497,11 +2502,6 @@ ilişkilendirmedeki yeni satırlar - - Open generic types in nameof operator - Open generic types in nameof operator - - parameterless struct constructors parametresiz yapı oluşturucuları @@ -2577,6 +2577,11 @@ struct alan başlatıcıları + + Unbound generic types in nameof operator + Unbound generic types in nameof operator + + unsigned right shift işaretsiz sağ kaydırma diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index ef27a87597d14..af11b1abb32e3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -1452,6 +1452,11 @@ 内联数组访问可能没有命名参数说明符 + + Nested unbound generic type not allowed in 'nameof' operator + Nested unbound generic type not allowed in 'nameof' operator + + The 'new()' constraint cannot be used with the 'unmanaged' constraint "new()" 约束不能与 "unmanaged" 约束一起使用 @@ -2497,11 +2502,6 @@ 内插中的换行符 - - Open generic types in nameof operator - Open generic types in nameof operator - - parameterless struct constructors 参数结构构造函数 @@ -2577,6 +2577,11 @@ 结构字段初始化表达式 + + Unbound generic types in nameof operator + Unbound generic types in nameof operator + + unsigned right shift 未签名的右移位 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index a1f23a79f900b..3ce02c815c6bd 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -1452,6 +1452,11 @@ 內嵌陣列存取不能有具名引數指定名稱 + + Nested unbound generic type not allowed in 'nameof' operator + Nested unbound generic type not allowed in 'nameof' operator + + The 'new()' constraint cannot be used with the 'unmanaged' constraint new()' 條件約束不能和 'unmanaged' 條件約束一起使用 @@ -2497,11 +2502,6 @@ 插補中的新行 - - Open generic types in nameof operator - Open generic types in nameof operator - - parameterless struct constructors 無參數結構建構函式 @@ -2577,6 +2577,11 @@ 結構欄位初始設定式 + + Unbound generic types in nameof operator + Unbound generic types in nameof operator + + unsigned right shift 未簽署右移位 From 092b89cf7f4f9a022f631449bf22e33f864770fd Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 3 Oct 2024 12:35:00 -0700 Subject: [PATCH 081/508] nesting --- .../CSharp/Portable/Binder/Binder_Symbols.cs | 2 +- .../CSharp/Portable/Errors/MessageID.cs | 2 +- .../Test/Semantic/Semantics/NameOfTests.cs | 27 ++++++++++--------- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index 079a5c04668c4..aa1593be79fe3 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -1376,7 +1376,7 @@ private NamedTypeSymbol ConstructNamedTypeUnlessTypeArgumentOmitted(SyntaxNode t if (this.IsInsideNameof) { // Inside a nameof an open-generic type is acceptable. Fall through and bind the remainder accordingly. - CheckFeatureAvailability(typeSyntax, MessageID.IDS_FeatureOpenTypeInNameof, diagnostics); + CheckFeatureAvailability(typeSyntax, MessageID.IDS_FeatureUnboundGenericTypesInNameof, diagnostics); typeArguments = UnboundArgumentErrorTypeSymbol.CreateTypeArguments( type.TypeParameters, type.TypeParameters.Length, diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index 69f3ef9e592d9..07cc6f54fdf2f 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -475,7 +475,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) // C# preview features. case MessageID.IDS_FeatureFieldKeyword: - case MessageID.IDS_FeatureOpenTypeInNameof: + case MessageID.IDS_FeatureUnboundGenericTypesInNameof: return LanguageVersion.Preview; // C# 13.0 features. diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index 0f2024536bab2..971b5c9bf3c2a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -2384,9 +2384,9 @@ public void OpenTypeInNameof_CSharp13() var v = nameof(List<>); Console.WriteLine(v); """, parseOptions: TestOptions.Regular13).VerifyDiagnostics( - // (4,16): error CS8652: The feature 'Open generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // (4,16): error CS8652: The feature 'Unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // var v = nameof(List<>); - Diagnostic(ErrorCode.ERR_FeatureInPreview, "List<>").WithArguments("Open generic types in nameof operator").WithLocation(4, 16)); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "List<>").WithArguments("Unbound generic types in nameof operator").WithLocation(4, 16)); } [Fact] @@ -2400,9 +2400,9 @@ public void OpenTypeInNameof_CSharp13_Nested1() class A { public class B; } """, parseOptions: TestOptions.Regular13).VerifyDiagnostics( - // (3,16): error CS8652: The feature 'Open generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // var v = nameof(A<>.B); - Diagnostic(ErrorCode.ERR_FeatureInPreview, "A<>").WithArguments("Open generic types in nameof operator").WithLocation(3, 16)); + // (3,16): error CS8652: The feature 'Unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var v = nameof(A<>.B); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "A<>").WithArguments("Unbound generic types in nameof operator").WithLocation(3, 16)); } [Fact] @@ -2416,9 +2416,9 @@ public void OpenTypeInNameof_CSharp13_Nested2() class A { public class B; } """, parseOptions: TestOptions.Regular13).VerifyDiagnostics( - // (3,23): error CS8652: The feature 'Open generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // (3,23): error CS8652: The feature 'Unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // var v = nameof(A.B<>); - Diagnostic(ErrorCode.ERR_FeatureInPreview, "B<>").WithArguments("Open generic types in nameof operator").WithLocation(3, 23)); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "B<>").WithArguments("Unbound generic types in nameof operator").WithLocation(3, 23)); } [Fact] @@ -2432,12 +2432,12 @@ public void OpenTypeInNameof_CSharp13_Nested3() class A { public class B; } """, parseOptions: TestOptions.Regular13).VerifyDiagnostics( - // (3,16): error CS8652: The feature 'Open generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // (3,16): error CS8652: The feature 'Unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // var v = nameof(A<>.B<>); - Diagnostic(ErrorCode.ERR_FeatureInPreview, "A<>").WithArguments("Open generic types in nameof operator").WithLocation(3, 16), - // (3,20): error CS8652: The feature 'Open generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + Diagnostic(ErrorCode.ERR_FeatureInPreview, "A<>").WithArguments("Unbound generic types in nameof operator").WithLocation(3, 16), + // (3,20): error CS8652: The feature 'Unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // var v = nameof(A<>.B<>); - Diagnostic(ErrorCode.ERR_FeatureInPreview, "B<>").WithArguments("Open generic types in nameof operator").WithLocation(3, 20)); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "B<>").WithArguments("Unbound generic types in nameof operator").WithLocation(3, 20)); } [Fact] @@ -2558,7 +2558,10 @@ public void OpenTypeInNameof_NoNestedOpenTypes() var v = nameof(List>); Console.WriteLine(v); - """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(); + """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (4,21): error CS9270: Nested unbound generic type not allowed in 'nameof' operator + // var v = nameof(List>); + Diagnostic(ErrorCode.ERR_NestedUnboundTypeNotAllowedInNameofExpression, "List<>").WithLocation(4, 21)); } [Fact] From d0b2d12ee3997a0ccedd69c1df5e2ba0a459ef82 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 3 Oct 2024 12:35:39 -0700 Subject: [PATCH 082/508] Fixup tests --- .../Test/Semantic/Semantics/NameOfTests.cs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index 971b5c9bf3c2a..6c8469580e36c 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -2614,15 +2614,14 @@ public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument() CompileAndVerify( CreateCompilation(""" using System; - using System.Collections.Generic; + + var v = nameof(IGoo<>.Count); + Console.WriteLine(v); interface IGoo { T Count { get; } } - - var v = nameof(IGoo<>.Count); - Console.WriteLine(v); """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(), expectedOutput: "Count"); } @@ -2634,14 +2633,14 @@ public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument_ReferenceObject CreateCompilation(""" using System; using System.Collections.Generic; + + var v = nameof(IGoo<>.Count.ToString); + Console.WriteLine(v); interface IGoo { T Count { get; } } - - var v = nameof(IGoo<>.Count.ToString); - Console.WriteLine(v); """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(), expectedOutput: "ToString"); } @@ -2653,14 +2652,14 @@ public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument_ReferenceConstr CreateCompilation(""" using System; using System.Collections.Generic; + + var v = nameof(IGoo<>.Count.X.CompareTo); + Console.WriteLine(v); interface IGoo where T : IComparable { T X { get; } } - - var v = nameof(IGoo<>.Count.X.CompareTo); - Console.WriteLine(v); """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(), expectedOutput: "CompareTo"); } From 2d0155ab08def9d4fabd62089ad6d76d55a25e37 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 3 Oct 2024 12:39:14 -0700 Subject: [PATCH 083/508] Fixup tests --- .../CSharp/Test/Semantic/Semantics/NameOfTests.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index 6c8469580e36c..9c97c99de1bc5 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -249,9 +249,6 @@ class Test // (17,66): error CS1031: Type expected // s = nameof(System.Collections.Generic.Dictionary.KeyCollection); Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(17, 66), - // (11,27): error CS0305: Using the generic type 'Action' requires 1 type arguments - // s = nameof(System.Action<>); - Diagnostic(ErrorCode.ERR_BadArity, "Action<>").WithArguments("System.Action", "type", "1").WithLocation(11, 27), // (16,20): error CS0103: The name 'List' does not exist in the current context // s = nameof(List.Enumerator); Diagnostic(ErrorCode.ERR_NameNotInContext, "List").WithArguments("List").WithLocation(16, 20), @@ -282,9 +279,6 @@ class Test // (31,20): error CS8083: An alias-qualified name is not an expression. // s = nameof(global::Program); // not an expression Diagnostic(ErrorCode.ERR_AliasQualifiedNameNotAnExpression, "global::Program").WithLocation(31, 20), - // (32,20): error CS0305: Using the generic type 'Test' requires 1 type arguments - // s = nameof(Test<>.s); // inaccessible - Diagnostic(ErrorCode.ERR_BadArity, "Test<>").WithArguments("Test", "type", "1").WithLocation(32, 20), // (32,27): error CS0122: 'Test.s' is inaccessible due to its protection level // s = nameof(Test<>.s); // inaccessible Diagnostic(ErrorCode.ERR_BadAccess, "s").WithArguments("Test.s").WithLocation(32, 27), @@ -2632,7 +2626,6 @@ public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument_ReferenceObject CompileAndVerify( CreateCompilation(""" using System; - using System.Collections.Generic; var v = nameof(IGoo<>.Count.ToString); Console.WriteLine(v); @@ -2653,7 +2646,7 @@ public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument_ReferenceConstr using System; using System.Collections.Generic; - var v = nameof(IGoo<>.Count.X.CompareTo); + var v = nameof(IGoo<>.X.CompareTo); Console.WriteLine(v); interface IGoo where T : IComparable From db4626624dc43dbd187c45a11a9bdbfae9247245 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 3 Oct 2024 12:43:20 -0700 Subject: [PATCH 084/508] Fixup tests --- .../Test/Semantic/Semantics/NameOfTests.cs | 45 +++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index 9c97c99de1bc5..9b657176b542d 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -4,7 +4,6 @@ #nullable disable -using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; @@ -2639,12 +2638,11 @@ interface IGoo } [Fact] - public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument_ReferenceConstraintMember() + public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument_ReferenceConstraintMember_Interface() { CompileAndVerify( CreateCompilation(""" using System; - using System.Collections.Generic; var v = nameof(IGoo<>.X.CompareTo); Console.WriteLine(v); @@ -2656,5 +2654,46 @@ interface IGoo where T : IComparable """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(), expectedOutput: "CompareTo"); } + + [Fact] + public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument_ReferenceConstraintMember_ThroughTypeParameter() + { + CompileAndVerify( + CreateCompilation(""" + using System; + + var v = nameof(IGoo<,>.X.CompareTo); + Console.WriteLine(v); + + interface IGoo where T : U where U : IComparable + { + T X { get; } + } + """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(), + expectedOutput: "CompareTo"); + } + + [Fact] + public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument_ReferenceConstraintMember_Class() + { + CompileAndVerify( + CreateCompilation(""" + using System; + + var v = nameof(IGoo<>.X.Z); + Console.WriteLine(v); + + class Base + { + public int Z { get; } + } + + interface IGoo where T : Base + { + T X { get; } + } + """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(), + expectedOutput: "Z"); + } } } From 074bd23c2f186111320eec48d187467e07e6ee4a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 3 Oct 2024 12:53:26 -0700 Subject: [PATCH 085/508] Fix check --- src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index 4c8a7e03fa39c..150d1d88e3eff 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -2460,6 +2460,7 @@ or ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToOverride or ErrorCode.ERR_CannotApplyOverloadResolutionPriorityToMember or ErrorCode.ERR_PartialPropertyDuplicateInitializer or ErrorCode.WRN_UninitializedNonNullableBackingField + or ErrorCode.ERR_NestedUnboundTypeNotAllowedInNameofExpression => false, }; #pragma warning restore CS8524 // The switch expression does not handle some values of its input type (it is not exhaustive) involving an unnamed enum value. From 08e46f4cdf0ba4ebfb0bf14690e7fcb666fac5ff Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 8 Oct 2024 12:11:19 -0700 Subject: [PATCH 086/508] Fix test --- .../Emit3/FlowAnalysis/FlowDiagnosticTests.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/FlowDiagnosticTests.cs b/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/FlowDiagnosticTests.cs index 4a8797b493957..1cc9d48984c2c 100644 --- a/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/FlowDiagnosticTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/FlowDiagnosticTests.cs @@ -858,10 +858,17 @@ public static int goo(int i) return 1; } }"; - var comp = CreateCompilation(program); - var parseErrors = comp.SyntaxTrees[0].GetDiagnostics(); - var errors = comp.GetDiagnostics(); - Assert.Equal(parseErrors.Count(), errors.Count()); + var comp = CreateCompilation(program).VerifyDiagnostics( + // (6,17): error CS0305: Using the generic method 'Program.goo(int)' requires 1 type arguments + // var s = goo<,int>(123); + Diagnostic(ErrorCode.ERR_BadArity, "goo<,int>").WithArguments("Program.goo(int)", "method", "1").WithLocation(6, 17), + // (6,21): error CS1031: Type expected + // var s = goo<,int>(123); + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(6, 21)); + comp.SyntaxTrees[0].GetDiagnostics().Verify( + // (6,21): error CS1031: Type expected + // var s = goo<,int>(123); + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(6, 21)); } [Fact] From 32d713b22570a4acd2927f70d68ba4898c86d875 Mon Sep 17 00:00:00 2001 From: Rekkonnect <8298332+Rekkonnect@users.noreply.github.com> Date: Tue, 8 Oct 2024 23:59:15 +0300 Subject: [PATCH 087/508] Set the location of VSInternalReferenceItem --- .../Protocol/Handler/References/FindUsagesLSPContext.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/LanguageServer/Protocol/Handler/References/FindUsagesLSPContext.cs b/src/LanguageServer/Protocol/Handler/References/FindUsagesLSPContext.cs index 9328c140b3804..eb6ae129e6aff 100644 --- a/src/LanguageServer/Protocol/Handler/References/FindUsagesLSPContext.cs +++ b/src/LanguageServer/Protocol/Handler/References/FindUsagesLSPContext.cs @@ -216,6 +216,7 @@ public override async ValueTask OnReferencesFoundAsync(IAsyncEnumerable Date: Wed, 9 Oct 2024 02:02:56 +0300 Subject: [PATCH 088/508] Nits --- .../Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs | 2 +- .../Compiler/Core/SymbolKey/SymbolKey.cs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs index 49a6dddbd0e1b..cf152d3c1752c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs @@ -27,13 +27,13 @@ private enum SymbolKeyType Field = 'F', FunctionPointer = 'G', DynamicType = 'I', + Preprocessing = 'J', BuiltinOperator = 'L', Method = 'M', Namespace = 'N', PointerType = 'O', Parameter = 'P', Property = 'Q', - Preprocessing = 'J', ArrayType = 'R', Assembly = 'S', TupleType = 'T', diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.cs index 3412917555406..d993f44ba3e89 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.cs @@ -66,6 +66,8 @@ namespace Microsoft.CodeAnalysis; /// Two s are the "same" if they have /// the "same" and /// the "same" . +/// Two s are the "same" if they have +/// the "same" . /// /// /// From 2198c857cf94d83a89bbcb3b8be6196eb242f8b2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 10 Oct 2024 13:36:07 -0700 Subject: [PATCH 089/508] message id --- src/Compilers/CSharp/Portable/Errors/MessageID.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index 3583a4c4af182..0783ddd7a7a0f 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -476,11 +476,8 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) // C# preview features. case MessageID.IDS_FeatureFieldKeyword: -<<<<<<< HEAD - case MessageID.IDS_FeatureUnboundGenericTypesInNameof: -======= case MessageID.IDS_FeatureFirstClassSpan: ->>>>>>> upstream/main + case MessageID.IDS_FeatureUnboundGenericTypesInNameof: return LanguageVersion.Preview; // C# 13.0 features. From e7f211c769ff5e80ed7e4a073a0f3dd1b89cf1f8 Mon Sep 17 00:00:00 2001 From: Rekkonnect <8298332+Rekkonnect@users.noreply.github.com> Date: Sat, 12 Oct 2024 01:39:55 +0300 Subject: [PATCH 090/508] Verification APIs --- .../AbstractCSharpCompletionProviderTests.cs | 10 + .../AbstractCompletionProviderTests.cs | 413 ++++++++++++++---- ...tractVisualBasicCompletionProviderTests.vb | 44 ++ 3 files changed, 376 insertions(+), 91 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractCSharpCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractCSharpCompletionProviderTests.cs index 9ed96d8441b55..965832f98cb9c 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractCSharpCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractCSharpCompletionProviderTests.cs @@ -62,6 +62,16 @@ private protected override Task BaseVerifyWorkerAsync( displayTextPrefix, inlineDescription, isComplexTextEdit, matchingFilters, flags, options, skipSpeculation: skipSpeculation); } + private protected override Task BaseVerifyWorkerAsync( + string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, + SourceCodeKind sourceCodeKind, CompletionTestExpectedResult[] expectedResults, + List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) + { + return base.VerifyWorkerAsync( + code, position, usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionItem, sourceCodeKind, + expectedResults, matchingFilters, flags, options, skipSpeculation); + } + private protected override async Task VerifyWorkerAsync( string code, int position, string expectedItemOrNull, string expectedDescriptionOrNull, diff --git a/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs b/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs index c2ea493aaa752..65f2e0fca4b2a 100644 --- a/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs +++ b/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs @@ -124,6 +124,11 @@ private protected abstract Task BaseVerifyWorkerAsync( List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false); + private protected abstract Task BaseVerifyWorkerAsync( + string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, + SourceCodeKind sourceCodeKind, CompletionTestExpectedResult[] expectedResults, + List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false); + internal Task GetCompletionListAsync( CompletionService service, Document document, @@ -143,6 +148,30 @@ private protected async Task CheckResultsAsync( List matchingFilters, CompletionItemFlags? flags, CompletionOptions options) + { + var expectedResult = new CompletionTestExpectedResult( + Name: expectedItemOrNull, + IsAbsent: checkForAbsence, + ExpectedDescription: expectedDescriptionOrNull, + Glyph: glyph, + MatchPriority: matchPriority, + DisplayTextSuffix: displayTextSuffix, + DisplayTextPrefix: displayTextPrefix, + InlineDescription: inlineDescription, + IsComplexTextEdit: isComplexTextEdit); + + var expectedResultArray = new[] { expectedResult }; + await CheckResultsAsync(document, position, usePreviousCharAsTrigger, deletedCharTrigger, + hasSuggestionModeItem, expectedResultArray, matchingFilters, flags, options); + } + + private protected async Task CheckResultsAsync( + Document document, int position, bool usePreviousCharAsTrigger, + char? deletedCharTrigger, bool? hasSuggestionModeItem, + CompletionTestExpectedResult[] expectedResults, + List matchingFilters, + CompletionItemFlags? flags, + CompletionOptions options) { options ??= GetCompletionOptions(); @@ -169,64 +198,105 @@ private protected async Task CheckResultsAsync( Assert.Equal(hasSuggestionModeItem.Value, completionList.SuggestionModeItem != null); } - if (checkForAbsence) + if (expectedResults.Length is 0) { - if (items == null) - { - return; - } + Assert.Equal(items.Count, 0); + } - if (expectedItemOrNull == null) + foreach (var result in expectedResults) + { + if (result.SourceCodeKind is not null && result.SourceCodeKind != document.SourceCodeKind) + continue; + + if (result.IsAbsent) { - Assert.Empty(items); + if (items == null) + { + return; + } + + if (result.Name == null) + { + Assert.Empty(items); + } + else + { + AssertEx.None( + items, + c => CompareItems(c.DisplayText, result.Name) + && CompareItems(c.DisplayTextSuffix, result.DisplayTextSuffix ?? "") + && CompareItems(c.DisplayTextPrefix, result.DisplayTextPrefix ?? "") + && CompareItems(c.InlineDescription, result.InlineDescription ?? "") + && (result.ExpectedDescription != null ? completionService.GetDescriptionAsync(document, c, options, displayOptions).Result.Text == result.ExpectedDescription : true)); + } } else { - AssertEx.None( - items, - c => CompareItems(c.DisplayText, expectedItemOrNull) - && CompareItems(c.DisplayTextSuffix, displayTextSuffix ?? "") - && CompareItems(c.DisplayTextPrefix, displayTextPrefix ?? "") - && CompareItems(c.InlineDescription, inlineDescription ?? "") - && (expectedDescriptionOrNull != null ? completionService.GetDescriptionAsync(document, c, options, displayOptions).Result.Text == expectedDescriptionOrNull : true)); - } - } - else - { - if (expectedItemOrNull == null) - { - Assert.NotEmpty(items); + if (result.Name == null) + { + Assert.NotEmpty(items); + } + else + { + AssertEx.Any(items, Predicate); + } } - else + + bool Predicate(RoslynCompletion.CompletionItem c) { - AssertEx.Any(items, Predicate); + if (!CompareItems(c.DisplayText, result.Name)) + return false; + if (!CompareItems(c.DisplayTextSuffix, result.DisplayTextSuffix ?? "")) + return false; + if (!CompareItems(c.DisplayTextPrefix, result.DisplayTextPrefix ?? "")) + return false; + if (!CompareItems(c.InlineDescription, result.InlineDescription ?? "")) + return false; + if (result.ExpectedDescription != null && completionService.GetDescriptionAsync(document, c, options, displayOptions).Result.Text != result.ExpectedDescription) + return false; + if (result.Glyph.HasValue && !c.Tags.SequenceEqual(GlyphTags.GetTags((Glyph)result.Glyph.Value))) + return false; + if (result.MatchPriority.HasValue && c.Rules.MatchPriority != result.MatchPriority.Value) + return false; + if (matchingFilters != null && !FiltersMatch(matchingFilters, c)) + return false; + if (flags != null && flags.Value != c.Flags) + return false; + if (result.IsComplexTextEdit is bool textEdit && textEdit != c.IsComplexTextEdit) + return false; + + return true; } } + } + + private protected record CompletionTestExpectedResult( + string Name, + bool IsAbsent, + string ExpectedDescription = null, + int? Glyph = null, + int? MatchPriority = null, + string DisplayTextSuffix = null, + string DisplayTextPrefix = null, + string InlineDescription = null, + bool? IsComplexTextEdit = null, + SourceCodeKind? SourceCodeKind = null) + { + public static CompletionTestExpectedResult[] None = CreateGeneralMatchingArray(true); + public static CompletionTestExpectedResult[] Any = CreateGeneralMatchingArray(false); + + private static CompletionTestExpectedResult[] CreateGeneralMatchingArray(bool absent) + { + return [new CompletionTestExpectedResult(Name: null, IsAbsent: absent)]; + } - bool Predicate(RoslynCompletion.CompletionItem c) + public static CompletionTestExpectedResult Exists(string name) { - if (!CompareItems(c.DisplayText, expectedItemOrNull)) - return false; - if (!CompareItems(c.DisplayTextSuffix, displayTextSuffix ?? "")) - return false; - if (!CompareItems(c.DisplayTextPrefix, displayTextPrefix ?? "")) - return false; - if (!CompareItems(c.InlineDescription, inlineDescription ?? "")) - return false; - if (expectedDescriptionOrNull != null && completionService.GetDescriptionAsync(document, c, options, displayOptions).Result.Text != expectedDescriptionOrNull) - return false; - if (glyph.HasValue && !c.Tags.SequenceEqual(GlyphTags.GetTags((Glyph)glyph.Value))) - return false; - if (matchPriority.HasValue && c.Rules.MatchPriority != matchPriority.Value) - return false; - if (matchingFilters != null && !FiltersMatch(matchingFilters, c)) - return false; - if (flags != null && flags.Value != c.Flags) - return false; - if (isComplexTextEdit is bool textEdit && textEdit != c.IsComplexTextEdit) - return false; - - return true; + return new CompletionTestExpectedResult(name, false); + } + public static CompletionTestExpectedResult Absent(string name) + { + return new CompletionTestExpectedResult(name, true); } } @@ -246,7 +316,8 @@ private async Task VerifyAsync( string displayTextPrefix, string inlineDescription, bool? isComplexTextEdit, List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) { - foreach (var sourceKind in sourceCodeKind.HasValue ? [sourceCodeKind.Value] : new[] { SourceCodeKind.Regular, SourceCodeKind.Script }) + IReadOnlyList evaluatedSourceCodeKinds = sourceCodeKind.HasValue ? [sourceCodeKind.Value] : [SourceCodeKind.Regular, SourceCodeKind.Script]; + foreach (var sourceKind in evaluatedSourceCodeKinds) { using var workspaceFixture = GetOrCreateWorkspaceFixture(); @@ -266,6 +337,29 @@ await VerifyWorkerAsync( } } + private async Task VerifyAsync( + string markup, SourceCodeKind? sourceCodeKind, char? deletedCharTrigger, bool usePreviousCharAsTrigger, + CompletionTestExpectedResult[] results, bool? hasSuggestionModeItem, + List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) + { + IReadOnlyList evaluatedSourceCodeKinds = sourceCodeKind.HasValue ? [sourceCodeKind.Value] : [SourceCodeKind.Regular, SourceCodeKind.Script]; + foreach (var sourceKind in evaluatedSourceCodeKinds) + { + using var workspaceFixture = GetOrCreateWorkspaceFixture(); + + var workspace = workspaceFixture.Target.GetWorkspace(markup, GetComposition()); + var code = workspaceFixture.Target.Code; + var position = workspaceFixture.Target.Position; + + // Set options that are not CompletionOptions + NonCompletionOptions?.SetGlobalOptions(workspace.GlobalOptions); + + await VerifyWorkerAsync( + code, position, usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionModeItem, + sourceKind, results, matchingFilters, flags, options, skipSpeculation); + } + } + protected async Task GetCompletionListAsync(string markup, string workspaceKind = null) { using var workspaceFixture = GetOrCreateWorkspaceFixture(); @@ -366,30 +460,35 @@ await VerifyAsync(markup, expectedItem, expectedDescriptionOrNull, sourceCodeKin } private protected async Task VerifyAnyItemExistsAsync( - string markup, SourceCodeKind? sourceCodeKind = null, bool usePreviousCharAsTrigger = false, char? deletedCharTrigger = null, - bool? hasSuggestionModeItem = null, string displayTextSuffix = null, string displayTextPrefix = null, - string inlineDescription = null, CompletionOptions options = null) + string markup, SourceCodeKind? sourceCodeKind = null, bool usePreviousCharAsTrigger = false, + bool? hasSuggestionModeItem = null, CompletionOptions options = null) { - await VerifyAsync(markup, expectedItemOrNull: null, expectedDescriptionOrNull: null, - sourceCodeKind, usePreviousCharAsTrigger: usePreviousCharAsTrigger, deletedCharTrigger: deletedCharTrigger, - checkForAbsence: false, glyph: null, matchPriority: null, - hasSuggestionModeItem: hasSuggestionModeItem, displayTextSuffix: displayTextSuffix, - displayTextPrefix: displayTextPrefix, inlineDescription: inlineDescription, - isComplexTextEdit: null, matchingFilters: null, flags: null, options); + await VerifyExpectedItemsAsync( + markup, results: CompletionTestExpectedResult.Any, sourceCodeKind, usePreviousCharAsTrigger: usePreviousCharAsTrigger, + hasSuggestionModeItem: hasSuggestionModeItem, options: options); } private protected async Task VerifyNoItemsExistAsync( string markup, SourceCodeKind? sourceCodeKind = null, bool usePreviousCharAsTrigger = false, bool? hasSuggestionModeItem = null, - string displayTextSuffix = null, string inlineDescription = null, CompletionOptions options = null) + CompletionOptions options = null) { - await VerifyAsync( - markup, expectedItemOrNull: null, expectedDescriptionOrNull: null, - sourceCodeKind, usePreviousCharAsTrigger: usePreviousCharAsTrigger, deletedCharTrigger: null, - checkForAbsence: true, glyph: null, matchPriority: null, - hasSuggestionModeItem: hasSuggestionModeItem, displayTextSuffix: displayTextSuffix, - displayTextPrefix: null, inlineDescription: inlineDescription, - isComplexTextEdit: null, matchingFilters: null, flags: null, options); + await VerifyExpectedItemsAsync( + markup, results: CompletionTestExpectedResult.None, sourceCodeKind, usePreviousCharAsTrigger: usePreviousCharAsTrigger, + hasSuggestionModeItem: hasSuggestionModeItem, options: options); + } + + private protected async Task VerifyExpectedItemsAsync( + string markup, CompletionTestExpectedResult[] results, + SourceCodeKind? sourceCodeKind = null, + char? deletedCharTrigger = null, + bool usePreviousCharAsTrigger = false, + bool? hasSuggestionModeItem = null, CompletionOptions options = null) + { + await VerifyAsync(markup, + sourceCodeKind, deletedCharTrigger: deletedCharTrigger, usePreviousCharAsTrigger, results: results, + hasSuggestionModeItem: hasSuggestionModeItem, matchingFilters: null, + flags: null, options); } internal abstract Type GetCompletionProviderType(); @@ -413,6 +512,40 @@ private protected virtual async Task VerifyWorkerAsync( List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) + { + var expectedResult = new CompletionTestExpectedResult( + Name: expectedItemOrNull, + IsAbsent: checkForAbsence, + ExpectedDescription: expectedDescriptionOrNull, + Glyph: glyph, + MatchPriority: matchPriority, + DisplayTextSuffix: displayTextSuffix, + DisplayTextPrefix: displayTextPrefix, + InlineDescription: inlineDescription, + IsComplexTextEdit: isComplexTextEdit); + + var expectedResultArray = new[] { expectedResult }; + + await VerifyWorkerCoreAsync( + code, position, usePreviousCharAsTrigger, deletedCharTrigger, + hasSuggestionModeItem, sourceCodeKind, expectedResultArray, + matchingFilters, flags, options, skipSpeculation); + } + + /// + /// Override this to change parameters or return without verifying anything, e.g. for script sources. Or to test in other code contexts. + /// + /// The source code (not markup). + /// The expected results. If this is empty, verifies that item shows up for this CompletionProvider (or no items show up if checkForAbsence is true). + /// Whether or not the previous character in markup should be used to trigger IntelliSense for this provider. If false, invokes it through the invoke IntelliSense command. + private protected async Task VerifyWorkerCoreAsync( + string code, int position, bool usePreviousCharAsTrigger, + char? deletedCharTrigger, bool? hasSuggestionModeItem, SourceCodeKind sourceCodeKind, + CompletionTestExpectedResult[] expectedResults, + List matchingFilters, + CompletionItemFlags? flags, + CompletionOptions options, + bool skipSpeculation = false) { using var workspaceFixture = GetOrCreateWorkspaceFixture(); @@ -420,23 +553,38 @@ private protected virtual async Task VerifyWorkerAsync( var document1 = workspaceFixture.Target.UpdateDocument(code, sourceCodeKind); await CheckResultsAsync( - document1, position, expectedItemOrNull, - expectedDescriptionOrNull, usePreviousCharAsTrigger, deletedCharTrigger, - checkForAbsence, glyph, matchPriority, - hasSuggestionModeItem, displayTextSuffix, displayTextPrefix, - inlineDescription, isComplexTextEdit, matchingFilters, flags, options); + document1, position, usePreviousCharAsTrigger, + deletedCharTrigger, hasSuggestionModeItem, expectedResults, + matchingFilters, flags, options); if (!skipSpeculation && await CanUseSpeculativeSemanticModelAsync(document1, position)) { var document2 = workspaceFixture.Target.UpdateDocument(code, sourceCodeKind, cleanBeforeUpdate: false); - await CheckResultsAsync( - document2, position, expectedItemOrNull, expectedDescriptionOrNull, - usePreviousCharAsTrigger, deletedCharTrigger, checkForAbsence, glyph, matchPriority, - hasSuggestionModeItem, displayTextSuffix, displayTextPrefix, - inlineDescription, isComplexTextEdit, matchingFilters, flags, options); + await CheckResultsAsync(document2, position, usePreviousCharAsTrigger, + deletedCharTrigger, hasSuggestionModeItem, expectedResults, + matchingFilters, flags, options); } } + /// + /// Override this to change parameters or return without verifying anything, e.g. for script sources. Or to test in other code contexts. + /// + /// The source code (not markup). + /// The expected results. If this is empty, verifies that item shows up for this CompletionProvider (or no items show up if checkForAbsence is true). + /// Whether or not the previous character in markup should be used to trigger IntelliSense for this provider. If false, invokes it through the invoke IntelliSense command. + private protected virtual Task VerifyWorkerAsync( + string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, + bool? hasSuggestionModeItem, SourceCodeKind sourceCodeKind, + CompletionTestExpectedResult[] expectedResults, + List matchingFilters, + CompletionItemFlags? flags, + CompletionOptions options, + bool skipSpeculation = false) + { + return VerifyWorkerCoreAsync(code, position, usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionModeItem, + sourceCodeKind, expectedResults, matchingFilters, flags, options, skipSpeculation); + } + /// /// Override this to change parameters or return without verifying anything, e.g. for script sources. Or to test in other code contexts. /// @@ -913,6 +1061,39 @@ protected async Task VerifyItemInLinkedFilesAsync(string xmlString, string expec } } + private protected async Task VerifyAtPositionAsync( + string code, int position, string insertText, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, SourceCodeKind sourceCodeKind, + CompletionTestExpectedResult[] expectedResults, + List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) + { + code = code[..position] + insertText + code[position..]; + position += insertText.Length; + + await BaseVerifyWorkerAsync(code, position, usePreviousCharAsTrigger, + deletedCharTrigger, hasSuggestionItem, sourceCodeKind, expectedResults, + matchingFilters, flags, options, skipSpeculation: skipSpeculation); + } + + private protected async Task VerifyAtPositionAsync( + string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, + SourceCodeKind sourceCodeKind, CompletionTestExpectedResult[] expectedResults, + List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) + { + await VerifyAtPositionAsync(code, position, string.Empty, usePreviousCharAsTrigger, + deletedCharTrigger, hasSuggestionItem, sourceCodeKind, expectedResults, + matchingFilters, flags, options, skipSpeculation: skipSpeculation); + } + + private protected async Task VerifyAtPosition_ItemPartiallyWrittenAsync( + string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, + SourceCodeKind sourceCodeKind, CompletionTestExpectedResult[] expectedResults, string partialItem, + List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) + { + await VerifyAtPositionAsync(code, position, ItemPartiallyWritten(partialItem), + usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionItem, sourceCodeKind, expectedResults, + matchingFilters, flags, options, skipSpeculation: skipSpeculation); + } + private protected async Task VerifyAtPositionAsync( string code, int position, string insertText, bool usePreviousCharAsTrigger, char? deletedCharTrigger, string expectedItemOrNull, string expectedDescriptionOrNull, @@ -946,6 +1127,62 @@ await VerifyAtPositionAsync( inlineDescription, isComplexTextEdit, matchingFilters, flags, options, skipSpeculation: skipSpeculation); } + private protected async Task VerifyAtPosition_ItemPartiallyWrittenAsync( + string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, + string expectedItemOrNull, string expectedDescriptionOrNull, + SourceCodeKind sourceCodeKind, bool checkForAbsence, int? glyph, + int? matchPriority, bool? hasSuggestionItem, string displayTextSuffix, + string displayTextPrefix, string inlineDescription = null, bool? isComplexTextEdit = null, + List matchingFilters = null, CompletionItemFlags? flags = null, + CompletionOptions options = null, bool skipSpeculation = false) + { + await VerifyAtPositionAsync( + code, position, ItemPartiallyWritten(expectedItemOrNull), usePreviousCharAsTrigger, + deletedCharTrigger, expectedItemOrNull, expectedDescriptionOrNull, sourceCodeKind, + checkForAbsence, glyph, matchPriority, hasSuggestionItem, displayTextSuffix, + displayTextPrefix, inlineDescription, isComplexTextEdit, matchingFilters, flags, options, + skipSpeculation: skipSpeculation); + } + + private protected async Task VerifyAtEndOfFileAsync( + string code, int position, string insertText, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, + SourceCodeKind sourceCodeKind, CompletionTestExpectedResult[] expectedResults, + List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) + { + // only do this if the placeholder was at the end of the text. + if (code.Length != position) + { + return; + } + + code = code[..position] + insertText + code[position..]; + position += insertText.Length; + + await BaseVerifyWorkerAsync(code, position, + usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionItem, sourceCodeKind, expectedResults, + matchingFilters, flags, options, skipSpeculation: skipSpeculation); + } + + private protected async Task VerifyAtEndOfFileAsync( + string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, + SourceCodeKind sourceCodeKind, CompletionTestExpectedResult[] expectedResults, + List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) + { + await VerifyAtEndOfFileAsync(code, position, string.Empty, + usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionItem, sourceCodeKind, expectedResults, + matchingFilters, flags, options, skipSpeculation: skipSpeculation); + } + + private protected async Task VerifyAtEndOfFileAsync( + string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, + SourceCodeKind sourceCodeKind, CompletionTestExpectedResult[] expectedResults, + List matchingFilters, CompletionItemFlags? flags, CompletionOptions options) + { + await VerifyAtEndOfFileAsync(code, position, string.Empty, + usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionItem, sourceCodeKind, expectedResults, + matchingFilters, flags, options); + } + private protected async Task VerifyAtEndOfFileAsync( string code, int position, string insertText, bool usePreviousCharAsTrigger, char? deletedCharTrigger, string expectedItemOrNull, string expectedDescriptionOrNull, @@ -971,23 +1208,6 @@ await BaseVerifyWorkerAsync( inlineDescription, isComplexTextEdit, matchingFilters, flags, options); } - private protected async Task VerifyAtPosition_ItemPartiallyWrittenAsync( - string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, - string expectedItemOrNull, string expectedDescriptionOrNull, - SourceCodeKind sourceCodeKind, bool checkForAbsence, int? glyph, - int? matchPriority, bool? hasSuggestionItem, string displayTextSuffix, - string displayTextPrefix, string inlineDescription = null, bool? isComplexTextEdit = null, - List matchingFilters = null, CompletionItemFlags? flags = null, - CompletionOptions options = null, bool skipSpeculation = false) - { - await VerifyAtPositionAsync( - code, position, ItemPartiallyWritten(expectedItemOrNull), usePreviousCharAsTrigger, deletedCharTrigger, - expectedItemOrNull, expectedDescriptionOrNull, sourceCodeKind, - checkForAbsence, glyph, matchPriority, hasSuggestionItem, displayTextSuffix, - displayTextPrefix, inlineDescription, isComplexTextEdit, matchingFilters, flags, options, - skipSpeculation: skipSpeculation); - } - private protected async Task VerifyAtEndOfFileAsync( string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, string expectedItemOrNull, string expectedDescriptionOrNull, @@ -1003,6 +1223,17 @@ await VerifyAtEndOfFileAsync(code, position, string.Empty, usePreviousCharAsTrig displayTextPrefix, inlineDescription, isComplexTextEdit, matchingFilters, flags, options); } + private protected async Task VerifyAtEndOfFile_ItemPartiallyWrittenAsync( + string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, + SourceCodeKind sourceCodeKind, CompletionTestExpectedResult[] expectedResults, string partialItem, + List matchingFilters, CompletionItemFlags? flags, CompletionOptions options) + { + await VerifyAtEndOfFileAsync( + code, position, ItemPartiallyWritten(partialItem), + usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionItem, sourceCodeKind, expectedResults, + matchingFilters, flags, options); + } + private protected async Task VerifyAtEndOfFile_ItemPartiallyWrittenAsync( string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, string expectedItemOrNull, string expectedDescriptionOrNull, diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AbstractVisualBasicCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AbstractVisualBasicCompletionProviderTests.vb index 3d1737bdf71b2..3145c09bbed08 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AbstractVisualBasicCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AbstractVisualBasicCompletionProviderTests.vb @@ -37,6 +37,15 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet isComplexTextEdit, matchingFilters, flags, options, skipSpeculation) End Function + Private Protected Overrides Function BaseVerifyWorkerAsync( + code As String, position As Integer, usePreviousCharAsTrigger As Boolean, deletedCharTrigger As Char?, + hasSuggestionItem As Boolean?, sourceCodeKind As SourceCodeKind, expectedResults() As CompletionTestExpectedResult, + matchingFilters As List(Of CompletionFilter), flags As CompletionItemFlags?, options As CompletionOptions, Optional skipSpeculation As Boolean = False) As Task + Return MyBase.VerifyWorkerAsync( + code, position, usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionItem, sourceCodeKind, + expectedResults, matchingFilters, flags, options, skipSpeculation) + End Function + Private Protected Overrides Async Function VerifyWorkerAsync( code As String, position As Integer, expectedItemOrNull As String, expectedDescriptionOrNull As String, @@ -75,6 +84,41 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet End If End Function + Private Protected Overrides Async Function VerifyWorkerAsync( + code As String, position As Integer, usePreviousCharAsTrigger As Boolean, deletedCharTrigger As Char?, + hasSuggestionModeItem As Boolean?, sourceCodeKind As SourceCodeKind, + expectedResults() As CompletionTestExpectedResult, matchingFilters As List(Of CompletionFilter), + flags As CompletionItemFlags?, options As CompletionOptions, Optional skipSpeculation As Boolean = False) As Task + + ' Script/interactive support removed for now. + ' TODO: Re-enable these when interactive is back in the product. + If sourceCodeKind <> SourceCodeKind.Regular Then + Return + End If + + Await VerifyAtPositionAsync( + code, position, usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionModeItem, sourceCodeKind, + expectedResults, matchingFilters, flags, options, skipSpeculation) + + Await VerifyAtEndOfFileAsync( + code, position, usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionModeItem, sourceCodeKind, + expectedResults, matchingFilters, flags, options) + + ' Items cannot be partially written if we're checking for their absence, + ' or if we're verifying that the list will show up (without specifying an actual item) + For Each item In expectedResults + If Not item.IsAbsent AndAlso item.Name IsNot Nothing Then + Await VerifyAtPosition_ItemPartiallyWrittenAsync( + code, position, usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionModeItem, sourceCodeKind, + expectedResults, item.Name, matchingFilters, flags:=Nothing, options, skipSpeculation) + + Await VerifyAtEndOfFile_ItemPartiallyWrittenAsync( + code, position, usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionModeItem, sourceCodeKind, + expectedResults, item.Name, matchingFilters, flags:=Nothing, options) + End If + Next + + End Function Protected Overrides Async Function VerifyCustomCommitProviderWorkerAsync(codeBeforeCommit As String, position As Integer, itemToCommit As String, expectedCodeAfterCommit As String, sourceCodeKind As SourceCodeKind, Optional commitChar As Char? = Nothing) As Task ' Script/interactive support removed for now. ' TODO: Re-enable these when interactive is back in the product. From 25ea82cc1b14335c2e80ff49131707c59f11ec2e Mon Sep 17 00:00:00 2001 From: Rekkonnect <8298332+Rekkonnect@users.noreply.github.com> Date: Sat, 12 Oct 2024 12:59:24 +0300 Subject: [PATCH 091/508] Adjust tests as in previous PR --- .../SymbolCompletionProviderTests.cs | 597 +++++++++++------- 1 file changed, 365 insertions(+), 232 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs index f5cdbe0e0dcb7..d411e082f5a5f 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs @@ -253,50 +253,75 @@ class C { [Fact] public async Task OpenStringLiteral() { - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", AddInsideMethod("string s = \"$$")), @"String"); - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", AddInsideMethod("string s = \"$$")), @"System"); + var code = AddUsingDirectives("using System;", AddInsideMethod("string s = \"$$")); + await VerifyExpectedItemsAsync(code, [ + CompletionTestExpectedResult.Absent("String"), + CompletionTestExpectedResult.Absent("System") + ]); } [Fact] public async Task OpenStringLiteralInDirective() { - await VerifyItemIsAbsentAsync("#r \"$$", "String", expectedDescriptionOrNull: null, sourceCodeKind: SourceCodeKind.Script); - await VerifyItemIsAbsentAsync("#r \"$$", "System", expectedDescriptionOrNull: null, sourceCodeKind: SourceCodeKind.Script); + var code = "#r \"$$\""; + await VerifyExpectedItemsAsync( + code, [ + CompletionTestExpectedResult.Absent("String"), + CompletionTestExpectedResult.Absent("System") + ], + sourceCodeKind: SourceCodeKind.Script); } [Fact] public async Task StringLiteral() { - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", AddInsideMethod("string s = \"$$\";")), @"System"); - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", AddInsideMethod("string s = \"$$\";")), @"String"); + var code = AddUsingDirectives("using System;", AddInsideMethod("string s = \"$$\";")); + await VerifyExpectedItemsAsync(code, [ + CompletionTestExpectedResult.Absent("String"), + CompletionTestExpectedResult.Absent("System") + ]); } [Fact] public async Task StringLiteralInDirective() { - await VerifyItemIsAbsentAsync("#r \"$$\"", "String", expectedDescriptionOrNull: null, sourceCodeKind: SourceCodeKind.Script); - await VerifyItemIsAbsentAsync("#r \"$$\"", "System", expectedDescriptionOrNull: null, sourceCodeKind: SourceCodeKind.Script); + var code = "#r \"$$\""; + await VerifyExpectedItemsAsync( + code, [ + CompletionTestExpectedResult.Absent("String"), + CompletionTestExpectedResult.Absent("System") + ], + sourceCodeKind: SourceCodeKind.Script); } [Fact] public async Task OpenCharLiteral() { - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", AddInsideMethod("char c = '$$")), @"System"); - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", AddInsideMethod("char c = '$$")), @"String"); + var code = AddUsingDirectives("using System;", AddInsideMethod("char c = '$$")); + await VerifyExpectedItemsAsync(code, [ + CompletionTestExpectedResult.Absent("String"), + CompletionTestExpectedResult.Absent("System") + ]); } [Fact] public async Task AssemblyAttribute1() { - await VerifyItemExistsAsync(@"[assembly: $$]", @"System"); - await VerifyItemIsAbsentAsync(@"[assembly: $$]", @"String"); + var code = @"[assembly: $$]"; + await VerifyExpectedItemsAsync(code, [ + CompletionTestExpectedResult.Absent("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task AssemblyAttribute2() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", @"[assembly: $$]"), @"System"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", @"[assembly: $$]"), @"AttributeUsage"); + var code = @"[assembly: $$]"; + await VerifyExpectedItemsAsync(code, [ + CompletionTestExpectedResult.Absent("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] @@ -496,32 +521,33 @@ namespace A namespace B { namespace $$ - namespace C1 { } } - namespace B.C2 { } } namespace A.B.C3 { }"; - // Ideally, all the C* namespaces would be recommended but, because of how the parser - // recovers from the missing braces, they end up with the following qualified names... - // - // C1 => A.B.?.C1 - // C2 => A.B.B.C2 - // C3 => A.A.B.C3 - // - // ...none of which are found by the current algorithm. - await VerifyItemIsAbsentAsync(source, "C1", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "C2", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "C3", sourceCodeKind: SourceCodeKind.Regular); - - await VerifyItemIsAbsentAsync(source, "A", sourceCodeKind: SourceCodeKind.Regular); - - // Because of the above, B does end up in the completion list - // since A.B.B appears to be a peer of the new declaration - await VerifyItemExistsAsync(source, "B", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync( + source, [ + // Ideally, all the C* namespaces would be recommended but, because of how the parser + // recovers from the missing braces, they end up with the following qualified names... + // + // C1 => A.B.?.C1 + // C2 => A.B.B.C2 + // C3 => A.A.B.C3 + // + // ...none of which are found by the current algorithm. + CompletionTestExpectedResult.Absent("C1"), + CompletionTestExpectedResult.Absent("C2"), + CompletionTestExpectedResult.Absent("C3"), + CompletionTestExpectedResult.Absent("A"), + + // Because of the above, B does end up in the completion list + // since A.B.B appears to be a peer of the new declaration + CompletionTestExpectedResult.Exists("B") + ], + SourceCodeKind.Regular); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7213")] @@ -648,21 +674,25 @@ namespace B.C.D2 { } namespace A.B.C.D3 { }"; - await VerifyItemIsAbsentAsync(source, "A", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "B", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "C", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync( + source, [ + CompletionTestExpectedResult.Absent("A"), + CompletionTestExpectedResult.Absent("B"), + CompletionTestExpectedResult.Absent("C"), - // Ideally, all the D* namespaces would be recommended but, because of how the parser - // recovers from the missing braces, they end up with the following qualified names... - // - // D1 => A.B.C.C.?.D1 - // D2 => A.B.B.C.D2 - // D3 => A.A.B.C.D3 - // - // ...none of which are found by the current algorithm. - await VerifyItemIsAbsentAsync(source, "D1", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "D2", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "D3", sourceCodeKind: SourceCodeKind.Regular); + // Ideally, all the D* namespaces would be recommended but, because of how the parser + // recovers from the missing braces, they end up with the following qualified names... + // + // D1 => A.B.C.C.?.D1 + // D2 => A.B.B.C.D2 + // D3 => A.A.B.C.D3 + // + // ...none of which are found by the current algorithm. + CompletionTestExpectedResult.Absent("D1"), + CompletionTestExpectedResult.Absent("D2"), + CompletionTestExpectedResult.Absent("D3") + ], + SourceCodeKind.Regular); } [Fact] @@ -2336,10 +2366,12 @@ void M() } } "; - await VerifyItemExistsAsync(markup, "ToString"); - await VerifyItemIsAbsentAsync(markup, "GetType"); - await VerifyItemIsAbsentAsync(markup, "Equals"); - await VerifyItemIsAbsentAsync(markup, "GetHashCode"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("ToString"), + CompletionTestExpectedResult.Absent("GetType"), + CompletionTestExpectedResult.Absent("Equals"), + CompletionTestExpectedResult.Absent("GetHashCode") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35178")] @@ -2356,10 +2388,12 @@ void M() } } "; - await VerifyItemExistsAsync(markup, "ToString"); - await VerifyItemExistsAsync(markup, "GetType"); - await VerifyItemExistsAsync(markup, "Equals"); - await VerifyItemExistsAsync(markup, "GetHashCode"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("ToString"), + CompletionTestExpectedResult.Exists("GetType"), + CompletionTestExpectedResult.Exists("Equals"), + CompletionTestExpectedResult.Exists("GetHashCode") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/53585")] @@ -2375,8 +2409,10 @@ void M(String parameter) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Absent("parameter") + ]); } [Theory] @@ -2401,8 +2437,10 @@ void M(String parameter) }} }} "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Absent("parameter") + ]); } [Theory] @@ -2425,8 +2463,10 @@ void M(String parameter) }} }} "; - await VerifyItemIsAbsentAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Absent("String"), + CompletionTestExpectedResult.Absent("parameter") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/60341")] @@ -2442,8 +2482,10 @@ async async $$ } } "; - await VerifyItemIsAbsentAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Absent("String"), + CompletionTestExpectedResult.Absent("parameter") + ]); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/53585")] @@ -2466,8 +2508,10 @@ void M(String parameter) }} }} "; - await VerifyItemIsAbsentAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Absent("String"), + CompletionTestExpectedResult.Absent("parameter") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25569")] @@ -2481,8 +2525,10 @@ class C ref $$ } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "field"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Absent("field") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25569")] @@ -2496,8 +2542,10 @@ class C ref readonly $$ } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "field"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Absent("field") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539217")] @@ -2513,8 +2561,10 @@ class R } } "; - await VerifyItemExistsAsync(markup, "Q"); - await VerifyItemExistsAsync(markup, "R"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("Q"), + CompletionTestExpectedResult.Exists("R") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539217")] @@ -2529,8 +2579,10 @@ class R } } "; - await VerifyItemExistsAsync(markup, "Q"); - await VerifyItemExistsAsync(markup, "R"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("Q"), + CompletionTestExpectedResult.Exists("R") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539217")] @@ -2545,8 +2597,10 @@ class R $$ } "; - await VerifyItemExistsAsync(markup, "Q"); - await VerifyItemExistsAsync(markup, "R"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("Q"), + CompletionTestExpectedResult.Exists("R") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539217")] @@ -2596,8 +2650,10 @@ class R { } $$"; // At EOF - await VerifyItemExistsAsync(markup, "Q"); - await VerifyItemExistsAsync(markup, "R"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("Q"), + CompletionTestExpectedResult.Exists("R") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539217")] @@ -2609,8 +2665,10 @@ class Q class R { $$"; // At EOF - await VerifyItemExistsAsync(markup, "Q"); - await VerifyItemExistsAsync(markup, "R"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("Q"), + CompletionTestExpectedResult.Exists("R") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540574")] @@ -2740,8 +2798,10 @@ public void M() } "; - await VerifyItemExistsAsync(markup, "ToString"); - await VerifyItemExistsAsync(markup, "Invoke"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("ToString"), + CompletionTestExpectedResult.Exists("Invoke") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/541216")] @@ -2865,8 +2925,10 @@ public async Task AttributeName() using System; [$$"; - await VerifyItemExistsAsync(markup, "CLSCompliant"); - await VerifyItemIsAbsentAsync(markup, "CLSCompliantAttribute"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("CLSCompliant"), + CompletionTestExpectedResult.Absent("CLSCompliantAttribute") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542225")] @@ -2877,8 +2939,10 @@ public async Task AttributeNameAfterSpecifier() [assembly:$$ "; - await VerifyItemExistsAsync(markup, "CLSCompliant"); - await VerifyItemIsAbsentAsync(markup, "CLSCompliantAttribute"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("CLSCompliant"), + CompletionTestExpectedResult.Absent("CLSCompliantAttribute") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542225")] @@ -2888,8 +2952,10 @@ public async Task AttributeNameInAttributeList() using System; [CLSCompliant, $$"; - await VerifyItemExistsAsync(markup, "CLSCompliant"); - await VerifyItemIsAbsentAsync(markup, "CLSCompliantAttribute"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("CLSCompliant"), + CompletionTestExpectedResult.Absent("CLSCompliantAttribute") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542225")] @@ -2900,8 +2966,10 @@ public async Task AttributeNameBeforeClass() [$$ class C { }"; - await VerifyItemExistsAsync(markup, "CLSCompliant"); - await VerifyItemIsAbsentAsync(markup, "CLSCompliantAttribute"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("CLSCompliant"), + CompletionTestExpectedResult.Absent("CLSCompliantAttribute") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542225")] @@ -2912,8 +2980,10 @@ public async Task AttributeNameAfterSpecifierBeforeClass() [assembly:$$ class C { }"; - await VerifyItemExistsAsync(markup, "CLSCompliant"); - await VerifyItemIsAbsentAsync(markup, "CLSCompliantAttribute"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("CLSCompliant"), + CompletionTestExpectedResult.Absent("CLSCompliantAttribute") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542225")] @@ -2924,8 +2994,10 @@ public async Task AttributeNameInAttributeArgumentList() [CLSCompliant($$ class C { }"; - await VerifyItemExistsAsync(markup, "CLSCompliantAttribute"); - await VerifyItemIsAbsentAsync(markup, "CLSCompliant"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("CLSCompliantAttribute"), + CompletionTestExpectedResult.Absent("CLSCompliant") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542225")] @@ -2935,8 +3007,10 @@ public async Task AttributeNameInsideClass() using System; class C { $$ }"; - await VerifyItemExistsAsync(markup, "CLSCompliantAttribute"); - await VerifyItemIsAbsentAsync(markup, "CLSCompliant"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("CLSCompliantAttribute"), + CompletionTestExpectedResult.Absent("CLSCompliant") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542954")] @@ -2989,8 +3063,10 @@ class MyAttribute : System.Attribute { } [Test.$$ class Program { } }"; - await VerifyItemExistsAsync(markup, "My"); - await VerifyItemIsAbsentAsync(markup, "MyAttribute"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("My"), + CompletionTestExpectedResult.Absent("MyAttribute") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545121")] @@ -3006,8 +3082,10 @@ class MyAttribute : System.Attribute { } class Program { } } }"; - await VerifyItemExistsAsync(markup, "My"); - await VerifyItemIsAbsentAsync(markup, "MyAttribute"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("My"), + CompletionTestExpectedResult.Absent("MyAttribute") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545121")] @@ -3020,9 +3098,11 @@ class namespaceAttribute : System.Attribute { } [$$ class Program { } }"; - await VerifyItemExistsAsync(markup, "namespaceAttribute"); - await VerifyItemIsAbsentAsync(markup, "namespace"); - await VerifyItemIsAbsentAsync(markup, "@namespace"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("namespaceAttribute"), + CompletionTestExpectedResult.Absent("namespace"), + CompletionTestExpectedResult.Absent("@namespace") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545121")] @@ -3035,9 +3115,11 @@ class namespaceAttribute : System.Attribute { } [Test.$$ class Program { } }"; - await VerifyItemExistsAsync(markup, "namespaceAttribute"); - await VerifyItemIsAbsentAsync(markup, "namespace"); - await VerifyItemIsAbsentAsync(markup, "@namespace"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("namespaceAttribute"), + CompletionTestExpectedResult.Absent("namespace"), + CompletionTestExpectedResult.Absent("@namespace") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545348")] @@ -3055,17 +3137,19 @@ void M() } }"; - // preprocessor keyword - await VerifyItemExistsAsync(markup, "error"); - await VerifyItemIsAbsentAsync(markup, "@error"); + await VerifyExpectedItemsAsync(markup, [ + // preprocessor keyword + CompletionTestExpectedResult.Exists("error"), + CompletionTestExpectedResult.Absent("@error"), - // contextual keyword - await VerifyItemExistsAsync(markup, "method"); - await VerifyItemIsAbsentAsync(markup, "@method"); + // contextual keyword + CompletionTestExpectedResult.Exists("method"), + CompletionTestExpectedResult.Absent("@method"), - // full keyword - await VerifyItemExistsAsync(markup, "@int"); - await VerifyItemIsAbsentAsync(markup, "int"); + // full keyword + CompletionTestExpectedResult.Exists("@int"), + CompletionTestExpectedResult.Absent("int") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545348")] @@ -3081,8 +3165,10 @@ void M() } }"; - await VerifyItemExistsAsync(markup, "@from"); - await VerifyItemIsAbsentAsync(markup, "from"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("@from"), + CompletionTestExpectedResult.Absent("from") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545348")] @@ -3100,10 +3186,12 @@ void M() } }"; - await VerifyItemExistsAsync(markup, "@from"); - await VerifyItemIsAbsentAsync(markup, "from"); - await VerifyItemExistsAsync(markup, "@where"); - await VerifyItemIsAbsentAsync(markup, "where"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("@from"), + CompletionTestExpectedResult.Absent("from"), + CompletionTestExpectedResult.Exists("@where"), + CompletionTestExpectedResult.Absent("where") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545348")] @@ -3121,10 +3209,12 @@ void M() } }"; - await VerifyItemExistsAsync(markup, "@from"); - await VerifyItemIsAbsentAsync(markup, "from"); - await VerifyItemExistsAsync(markup, "@where"); - await VerifyItemIsAbsentAsync(markup, "where"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("@from"), + CompletionTestExpectedResult.Absent("from"), + CompletionTestExpectedResult.Exists("@where"), + CompletionTestExpectedResult.Absent("where") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545121")] @@ -3134,8 +3224,12 @@ public async Task AttributeNameAfterGlobalAlias() class MyAttribute : System.Attribute { } [global::$$ class Program { }"; - await VerifyItemExistsAsync(markup, "My", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(markup, "MyAttribute", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync( + markup, [ + CompletionTestExpectedResult.Exists("My"), + CompletionTestExpectedResult.Absent("MyAttribute") + ], + SourceCodeKind.Regular); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545121")] @@ -3145,9 +3239,13 @@ public async Task AttributeNameAfterGlobalAliasWhenSuffixlessFormIsKeyword() class namespaceAttribute : System.Attribute { } [global::$$ class Program { }"; - await VerifyItemExistsAsync(markup, "namespaceAttribute", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(markup, "namespace", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(markup, "@namespace", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync( + markup, [ + CompletionTestExpectedResult.Exists("namespaceAttribute"), + CompletionTestExpectedResult.Absent("namespace"), + CompletionTestExpectedResult.Absent("@namespace") + ], + SourceCodeKind.Regular); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25589")] @@ -9831,25 +9929,27 @@ void goo() } }" + TestResources.NetFX.ValueTuple.tuplelib_cs; - await VerifyItemExistsAsync(markup, "Alice"); - await VerifyItemExistsAsync(markup, "Bob"); - await VerifyItemExistsAsync(markup, "CompareTo"); - await VerifyItemExistsAsync(markup, "Equals"); - await VerifyItemExistsAsync(markup, "GetHashCode"); - await VerifyItemExistsAsync(markup, "GetType"); - await VerifyItemExistsAsync(markup, "Item2"); - await VerifyItemExistsAsync(markup, "ITEM3"); - for (var i = 4; i <= 8; i++) - { - await VerifyItemExistsAsync(markup, "Item" + i); - } - - await VerifyItemExistsAsync(markup, "ToString"); - - await VerifyItemIsAbsentAsync(markup, "Item1"); - await VerifyItemIsAbsentAsync(markup, "Item9"); - await VerifyItemIsAbsentAsync(markup, "Rest"); - await VerifyItemIsAbsentAsync(markup, "Item3"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("Alice"), + CompletionTestExpectedResult.Exists("Bob"), + CompletionTestExpectedResult.Exists("CompareTo"), + CompletionTestExpectedResult.Exists("Equals"), + CompletionTestExpectedResult.Exists("GetHashCode"), + CompletionTestExpectedResult.Exists("GetType"), + CompletionTestExpectedResult.Exists("Item2"), + CompletionTestExpectedResult.Exists("ITEM3"), + CompletionTestExpectedResult.Exists("Item4"), + CompletionTestExpectedResult.Exists("Item5"), + CompletionTestExpectedResult.Exists("Item6"), + CompletionTestExpectedResult.Exists("Item7"), + CompletionTestExpectedResult.Exists("Item8"), + CompletionTestExpectedResult.Exists("ToString"), + + CompletionTestExpectedResult.Absent("Item1"), + CompletionTestExpectedResult.Absent("Item9"), + CompletionTestExpectedResult.Absent("Rest"), + CompletionTestExpectedResult.Absent("Item3") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/14546")] @@ -10728,11 +10828,13 @@ class Builder public int Something { get; set; } }"; - await VerifyItemExistsAsync(markup, "Something"); - await VerifyItemIsAbsentAsync(markup, "BeginInvoke"); - await VerifyItemIsAbsentAsync(markup, "Clone"); - await VerifyItemIsAbsentAsync(markup, "Method"); - await VerifyItemIsAbsentAsync(markup, "Target"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("Something"), + CompletionTestExpectedResult.Absent("BeginInvoke"), + CompletionTestExpectedResult.Absent("Clone"), + CompletionTestExpectedResult.Absent("Method"), + CompletionTestExpectedResult.Absent("Target") + ]); } [Fact] @@ -10755,19 +10857,21 @@ public void Test() } }"; - // Guid - await VerifyItemExistsAsync(markup, "ToByteArray"); + await VerifyExpectedItemsAsync(markup, [ + // Guid + CompletionTestExpectedResult.Exists("ToByteArray"), - // Uri - await VerifyItemExistsAsync(markup, "AbsoluteUri"); - await VerifyItemExistsAsync(markup, "Fragment"); - await VerifyItemExistsAsync(markup, "Query"); + // Uri + CompletionTestExpectedResult.Exists("AbsoluteUri"), + CompletionTestExpectedResult.Exists("Fragment"), + CompletionTestExpectedResult.Exists("Query"), - // Should not appear for Delegate - await VerifyItemIsAbsentAsync(markup, "BeginInvoke"); - await VerifyItemIsAbsentAsync(markup, "Clone"); - await VerifyItemIsAbsentAsync(markup, "Method"); - await VerifyItemIsAbsentAsync(markup, "Target"); + // Should not appear for Delegate + CompletionTestExpectedResult.Absent("BeginInvoke"), + CompletionTestExpectedResult.Absent("Clone"), + CompletionTestExpectedResult.Absent("Method"), + CompletionTestExpectedResult.Absent("Target") + ]); } [Fact] @@ -10789,20 +10893,21 @@ public void Test() M(d => d.$$) } }"; + await VerifyExpectedItemsAsync(markup, [ + // Guid + CompletionTestExpectedResult.Exists("ToByteArray"), - // Guid - await VerifyItemExistsAsync(markup, "ToByteArray"); - - // Should not appear for Uri - await VerifyItemIsAbsentAsync(markup, "AbsoluteUri"); - await VerifyItemIsAbsentAsync(markup, "Fragment"); - await VerifyItemIsAbsentAsync(markup, "Query"); + // Should not appear for Uri + CompletionTestExpectedResult.Absent("AbsoluteUri"), + CompletionTestExpectedResult.Absent("Fragment"), + CompletionTestExpectedResult.Absent("Query"), - // Should not appear for Delegate - await VerifyItemIsAbsentAsync(markup, "BeginInvoke"); - await VerifyItemIsAbsentAsync(markup, "Clone"); - await VerifyItemIsAbsentAsync(markup, "Method"); - await VerifyItemIsAbsentAsync(markup, "Target"); + // Should not appear for Delegate + CompletionTestExpectedResult.Absent("BeginInvoke"), + CompletionTestExpectedResult.Absent("Clone"), + CompletionTestExpectedResult.Absent("Method"), + CompletionTestExpectedResult.Absent("Target") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36029")] @@ -11550,12 +11655,15 @@ void M(T x) where T : I1, I2 } } "; - await VerifyItemIsAbsentAsync(source, "M0"); - await VerifyItemExistsAsync(source, "M1"); - await VerifyItemExistsAsync(source, "M2"); - await VerifyItemExistsAsync(source, "M3"); - await VerifyItemExistsAsync(source, "P1"); - await VerifyItemExistsAsync(source, "E1"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Absent("M0"), + + CompletionTestExpectedResult.Exists("M1"), + CompletionTestExpectedResult.Exists("M2"), + CompletionTestExpectedResult.Exists("M3"), + CompletionTestExpectedResult.Exists("P1"), + CompletionTestExpectedResult.Exists("E1") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58081")] @@ -11577,10 +11685,12 @@ void TestMethod(TestStruct* a) } } "; - await VerifyItemExistsAsync(source, "X"); - await VerifyItemExistsAsync(source, "Y"); - await VerifyItemExistsAsync(source, "Method"); - await VerifyItemExistsAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("X"), + CompletionTestExpectedResult.Exists("Y"), + CompletionTestExpectedResult.Exists("Method"), + CompletionTestExpectedResult.Exists("ToString") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58081")] @@ -11602,10 +11712,12 @@ await a->$$ } } "; - await VerifyItemExistsAsync(source, "X"); - await VerifyItemExistsAsync(source, "Y"); - await VerifyItemExistsAsync(source, "Method"); - await VerifyItemExistsAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("X"), + CompletionTestExpectedResult.Exists("Y"), + CompletionTestExpectedResult.Exists("Method"), + CompletionTestExpectedResult.Exists("ToString") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58081")] @@ -11629,10 +11741,12 @@ TestLambda TestMethod() } } "; - await VerifyItemExistsAsync(source, "X"); - await VerifyItemExistsAsync(source, "Y"); - await VerifyItemExistsAsync(source, "Method"); - await VerifyItemExistsAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("X"), + CompletionTestExpectedResult.Exists("Y"), + CompletionTestExpectedResult.Exists("Method"), + CompletionTestExpectedResult.Exists("ToString") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58081")] @@ -11667,8 +11781,10 @@ void TestMethod() => Overloaded(a => a->$$); } "; - await VerifyItemExistsAsync(source, "X"); - await VerifyItemExistsAsync(source, "Y"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("X"), + CompletionTestExpectedResult.Exists("Y") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58081")] @@ -11703,8 +11819,10 @@ void TestMethod() => Overloaded((TestStruct1* a) => a->$$); } "; - await VerifyItemExistsAsync(source, "X"); - await VerifyItemIsAbsentAsync(source, "Y"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("X"), + CompletionTestExpectedResult.Absent("Y") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58081")] @@ -11761,8 +11879,10 @@ void TestMethod() => Overloaded(a => a.$$); } "; - await VerifyItemIsAbsentAsync(source, "X"); - await VerifyItemIsAbsentAsync(source, "Y"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Absent("X"), + CompletionTestExpectedResult.Absent("Y") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58081")] @@ -11797,8 +11917,10 @@ void TestMethod() => Overloaded((TestStruct1* a) => a.$$); } "; - await VerifyItemIsAbsentAsync(source, "X"); - await VerifyItemIsAbsentAsync(source, "Y"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Absent("X"), + CompletionTestExpectedResult.Absent("Y") + ]); } [InlineData("m.MyObject?.$$MyValue!!()")] @@ -12146,13 +12268,16 @@ public async Task EnumBaseList3(string underlyingType) enum E : $$ """; - await VerifyItemExistsAsync(source, "System"); - await VerifyItemExistsAsync(source, underlyingType); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("System"), + + CompletionTestExpectedResult.Exists(underlyingType), - // Verify that other things from `System` namespace are not present - await VerifyItemIsAbsentAsync(source, "Console"); - await VerifyItemIsAbsentAsync(source, "Action"); - await VerifyItemIsAbsentAsync(source, "DateTime"); + // Verify that other things from `System` namespace are not present + CompletionTestExpectedResult.Absent("Console"), + CompletionTestExpectedResult.Absent("Action"), + CompletionTestExpectedResult.Absent("DateTime") + ]); } [Theory, MemberData(nameof(ValidEnumUnderlyingTypeNames))] @@ -12166,13 +12291,15 @@ namespace MyNamespace enum E : global::$$ """; - await VerifyItemIsAbsentAsync(source, "E"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Absent("E"), - await VerifyItemExistsAsync(source, "System"); - await VerifyItemIsAbsentAsync(source, "MyNamespace"); + CompletionTestExpectedResult.Exists("System"), + CompletionTestExpectedResult.Absent("MyNamespace"), - // Not accessible in the given context - await VerifyItemIsAbsentAsync(source, underlyingType); + // Not accessible in the given context + CompletionTestExpectedResult.Absent(underlyingType) + ]); } [Theory, MemberData(nameof(ValidEnumUnderlyingTypeNames))] @@ -12180,14 +12307,16 @@ public async Task EnumBaseList5(string underlyingType) { var source = "enum E : System.$$"; - await VerifyItemIsAbsentAsync(source, "System"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Absent("System"), - await VerifyItemExistsAsync(source, underlyingType); + CompletionTestExpectedResult.Exists(underlyingType), - // Verify that other things from `System` namespace are not present - await VerifyItemIsAbsentAsync(source, "Console"); - await VerifyItemIsAbsentAsync(source, "Action"); - await VerifyItemIsAbsentAsync(source, "DateTime"); + // Verify that other things from `System` namespace are not present + CompletionTestExpectedResult.Absent("Console"), + CompletionTestExpectedResult.Absent("Action"), + CompletionTestExpectedResult.Absent("DateTime") + ]); } [Theory, MemberData(nameof(ValidEnumUnderlyingTypeNames))] @@ -12195,14 +12324,16 @@ public async Task EnumBaseList6(string underlyingType) { var source = "enum E : global::System.$$"; - await VerifyItemIsAbsentAsync(source, "System"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Absent("System"), - await VerifyItemExistsAsync(source, underlyingType); + CompletionTestExpectedResult.Exists(underlyingType), - // Verify that other things from `System` namespace are not present - await VerifyItemIsAbsentAsync(source, "Console"); - await VerifyItemIsAbsentAsync(source, "Action"); - await VerifyItemIsAbsentAsync(source, "DateTime"); + // Verify that other things from `System` namespace are not present + CompletionTestExpectedResult.Absent("Console"), + CompletionTestExpectedResult.Absent("Action"), + CompletionTestExpectedResult.Absent("DateTime") + ]); } [Fact] @@ -12269,15 +12400,17 @@ public async Task EnumBaseList11(string underlyingType) enum E : MySystem.$$ """; - await VerifyItemIsAbsentAsync(source, "System"); - await VerifyItemIsAbsentAsync(source, "MySystem"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Absent("System"), + CompletionTestExpectedResult.Absent("MySystem"), - await VerifyItemExistsAsync(source, underlyingType); + CompletionTestExpectedResult.Exists(underlyingType), - // Verify that other things from `System` namespace are not present - await VerifyItemIsAbsentAsync(source, "Console"); - await VerifyItemIsAbsentAsync(source, "Action"); - await VerifyItemIsAbsentAsync(source, "DateTime"); + // Verify that other things from `System` namespace are not present + CompletionTestExpectedResult.Absent("Console"), + CompletionTestExpectedResult.Absent("Action"), + CompletionTestExpectedResult.Absent("DateTime") + ]); } [Fact] From 2d105cab54815548da6d090586bf5d81042ea141 Mon Sep 17 00:00:00 2001 From: Rekkonnect <8298332+Rekkonnect@users.noreply.github.com> Date: Sat, 12 Oct 2024 14:58:37 +0300 Subject: [PATCH 092/508] Adjust a batch of tests --- .../SymbolCompletionProviderTests.cs | 759 +++++++++++++----- 1 file changed, 554 insertions(+), 205 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs index d411e082f5a5f..ce352480f009b 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs @@ -346,8 +346,11 @@ class CL {}"; [Fact] public async Task TypeParamAttribute() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", @"class CL<[A$$]T> {}"), @"AttributeUsage"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", @"class CL<[A$$]T> {}"), @"System"); + var code = AddUsingDirectives("using System;", @"class CL<[A$$]T> {}"); + await VerifyExpectedItemsAsync(code, [ + CompletionTestExpectedResult.Exists("AttributeUsage"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] @@ -357,8 +360,11 @@ public async Task MethodAttribute() [$$] void Method() {} }"; - await VerifyItemExistsAsync(AddUsingDirectives("using System;", content), @"AttributeUsage"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", content), @"System"); + var code = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(code, [ + CompletionTestExpectedResult.Exists("AttributeUsage"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] @@ -367,8 +373,11 @@ public async Task MethodTypeParamAttribute() var content = @"class CL{ void Method<[A$$]T> () {} }"; - await VerifyItemExistsAsync(AddUsingDirectives("using System;", content), @"AttributeUsage"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", content), @"System"); + var code = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(code, [ + CompletionTestExpectedResult.Exists("AttributeUsage"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] @@ -377,8 +386,11 @@ public async Task MethodParamAttribute() var content = @"class CL{ void Method ([$$]int i) {} }"; - await VerifyItemExistsAsync(AddUsingDirectives("using System;", content), @"AttributeUsage"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", content), @"System"); + var code = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(code, [ + CompletionTestExpectedResult.Exists("AttributeUsage"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7213")] @@ -408,8 +420,12 @@ public async Task NamespaceName_Unqualified_TopLevelNoPeers() namespace $$"; - await VerifyItemExistsAsync(source, "System", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "String", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync(source, + [ + CompletionTestExpectedResult.Exists("System"), + CompletionTestExpectedResult.Absent("String") + ], + sourceCodeKind: SourceCodeKind.Regular); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7213")] @@ -419,8 +435,12 @@ public async Task NamespaceName_Unqualified_TopLevelNoPeers_FileScopedNamespace( namespace $$;"; - await VerifyItemExistsAsync(source, "System", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "String", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync(source, + [ + CompletionTestExpectedResult.Exists("System"), + CompletionTestExpectedResult.Absent("String") + ], + sourceCodeKind: SourceCodeKind.Regular); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7213")] @@ -457,8 +477,12 @@ namespace B { } namespace $$ }"; - await VerifyItemIsAbsentAsync(source, "A", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemExistsAsync(source, "B", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync(source, + [ + CompletionTestExpectedResult.Absent("A"), + CompletionTestExpectedResult.Exists("B") + ], + sourceCodeKind: SourceCodeKind.Regular); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7213")] @@ -481,8 +505,12 @@ namespace B { } } }"; - await VerifyItemIsAbsentAsync(source, "A", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "B", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync(source, + [ + CompletionTestExpectedResult.Absent("A"), + CompletionTestExpectedResult.Absent("B") + ], + sourceCodeKind: SourceCodeKind.Regular); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7213")] @@ -499,8 +527,12 @@ namespace B { } } }"; - await VerifyItemIsAbsentAsync(source, "A", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemExistsAsync(source, "B", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync(source, + [ + CompletionTestExpectedResult.Absent("A"), + CompletionTestExpectedResult.Exists("B") + ], + sourceCodeKind: SourceCodeKind.Regular); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7213")] @@ -508,8 +540,12 @@ public async Task NamespaceName_Unqualified_InnerCompletionPosition() { var source = @"namespace Sys$$tem { }"; - await VerifyItemExistsAsync(source, "System", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "Runtime", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync(source, + [ + CompletionTestExpectedResult.Exists("System"), + CompletionTestExpectedResult.Absent("Runtime") + ], + sourceCodeKind: SourceCodeKind.Regular); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7213")] @@ -591,9 +627,13 @@ namespace B.C { } namespace B.$$ }"; - await VerifyItemIsAbsentAsync(source, "A", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "B", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemExistsAsync(source, "C", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync(source, + [ + CompletionTestExpectedResult.Absent("A"), + CompletionTestExpectedResult.Absent("B"), + CompletionTestExpectedResult.Exists("C") + ], + sourceCodeKind: SourceCodeKind.Regular); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7213")] @@ -606,8 +646,12 @@ namespace B { } } "; - await VerifyItemIsAbsentAsync(source, "A", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "B", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync(source, + [ + CompletionTestExpectedResult.Absent("A"), + CompletionTestExpectedResult.Absent("B") + ], + sourceCodeKind: SourceCodeKind.Regular); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7213")] @@ -622,8 +666,12 @@ namespace B { } } "; - await VerifyItemIsAbsentAsync(source, "A", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemExistsAsync(source, "B", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync(source, + [ + CompletionTestExpectedResult.Absent("A"), + CompletionTestExpectedResult.Exists("B") + ], + sourceCodeKind: SourceCodeKind.Regular); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7213")] @@ -631,8 +679,12 @@ public async Task NamespaceName_Qualified_InnerCompletionPosition() { var source = @"namespace Sys$$tem.Runtime { }"; - await VerifyItemExistsAsync(source, "System", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "Runtime", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync(source, + [ + CompletionTestExpectedResult.Exists("System"), + CompletionTestExpectedResult.Absent("Runtime") + ], + sourceCodeKind: SourceCodeKind.Regular); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7213")] @@ -652,8 +704,12 @@ namespace System name$$space Runtime { } }"; - await VerifyItemIsAbsentAsync(source, "System", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "Runtime", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync(source, + [ + CompletionTestExpectedResult.Absent("System"), + CompletionTestExpectedResult.Absent("Runtime") + ], + sourceCodeKind: SourceCodeKind.Regular); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7213")] @@ -698,19 +754,23 @@ await VerifyExpectedItemsAsync( [Fact] public async Task UnderNamespace() { - await VerifyItemIsAbsentAsync(@"namespace NS { $$", @"String"); - await VerifyItemIsAbsentAsync(@"namespace NS { $$", @"System"); + var source = @"namespace NS { $$"; + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Absent("String"), + CompletionTestExpectedResult.Absent("System") + ]); } [Fact] public async Task OutsideOfType1() { - await VerifyItemIsAbsentAsync(@"namespace NS { -class CL {} -$$", @"String"); - await VerifyItemIsAbsentAsync(@"namespace NS { + var source = @"namespace NS { class CL {} -$$", @"System"); +$$"; + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Absent("String"), + CompletionTestExpectedResult.Absent("System") + ]); } [Fact] @@ -719,14 +779,17 @@ public async Task OutsideOfType2() var content = @"namespace NS { class CL {} $$"; - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", content), @"String"); - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", content), @"System"); + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Absent("String"), + CompletionTestExpectedResult.Absent("System") + ]); } [Fact] public async Task CompletionInsideProperty() { - var content = @"class C + var source = @"class C { private string name; public string Name @@ -734,22 +797,30 @@ public string Name set { name = $$"; - await VerifyItemExistsAsync(content, @"value"); - await VerifyItemExistsAsync(content, @"C"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("value"), + CompletionTestExpectedResult.Exists("C") + ]); } [Fact] public async Task AfterDot() { - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", @"[assembly: A.$$"), @"String"); - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", @"[assembly: A.$$"), @"System"); + var source = AddUsingDirectives("using System;", @"[assembly: A.$$"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Absent("String"), + CompletionTestExpectedResult.Absent("System") + ]); } [Fact] public async Task UsingAlias() { - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", @"using MyType = $$"), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", @"using MyType = $$"), @"System"); + var source = AddUsingDirectives("using System;", @"using MyType = $$"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Absent("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] @@ -758,8 +829,11 @@ public async Task IncompleteMember() var content = @"class CL { $$ "; - await VerifyItemExistsAsync(AddUsingDirectives("using System;", content), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", content), @"System"); + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] @@ -768,106 +842,151 @@ public async Task IncompleteMemberAccessibility() var content = @"class CL { public $$ "; - await VerifyItemExistsAsync(AddUsingDirectives("using System;", content), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", content), @"System"); + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task BadStatement() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = $$)c")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = $$)c")), @"System"); + var source = AddUsingDirectives("using System;", AddInsideMethod(@"var t = $$)c")); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task TypeTypeParameter() { - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", @"class CL<$$"), @"String"); - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", @"class CL<$$"), @"System"); + var source = AddUsingDirectives("using System;", @"class CL<$$"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Absent("String"), + CompletionTestExpectedResult.Absent("System") + ]); } [Fact] public async Task TypeTypeParameterList() { - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", @"class CL10;$$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"for(i=0;i>10;$$")), @"System"); + var content = @"for(i=0;i>10;$$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task DoStatementConditionPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"do {} while($$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"do {} while($$")), @"System"); + var content = @"do {} while($$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task WhileStatementConditionPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"while($$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"while($$")), @"System"); + var content = @"while($$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task ArrayRankSpecifierSizesPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"int [$$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"int [$$")), @"System"); + var content = @"int [$$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task PrefixUnaryExpression() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"+$$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"+$$")), @"System"); + var content = @"+$$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task PostfixUnaryExpression() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"$$++")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"$$++")), @"System"); + var content = @"$$++"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task BinaryExpressionLeftPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"$$ + 1")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"$$ + 1")), @"System"); + var content = @"$$ + 1"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task BinaryExpressionRightPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"1 + $$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"1 + $$")), @"System"); + var content = @"1 + $$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task AssignmentExpressionLeftPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"$$ = 1")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"$$ = 1")), @"System"); + var content = @"$$ = 1"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task AssignmentExpressionRightPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"1 = $$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"1 = $$")), @"System"); + var content = @"1 = $$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task ConditionalExpressionConditionPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"$$? 1:")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"$$? 1:")), @"System"); + var content = @"$$? 1:"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task ConditionalExpressionWhenTruePart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"true? $$:")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"true? $$:")), @"System"); + var content = @"true? $$:"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task ConditionalExpressionWhenFalsePart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"true? 1:$$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"true? 1:$$")), @"System"); + var content = @"true? 1:$$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task JoinClauseInExpressionPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = from c in C join p in $$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = from c in C join p in $$")), @"System"); + var content = @"var t = from c in C join p in $$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task JoinClauseLeftExpressionPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = from c in C join p in P on $$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = from c in C join p in P on $$")), @"System"); + var content = @"var t = from c in C join p in P on $$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task JoinClauseRightExpressionPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = from c in C join p in P on id equals $$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = from c in C join p in P on id equals $$")), @"System"); + var content = @"var t = from c in C join p in P on id equals $$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task WhereClauseConditionPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = from c in C where $$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = from c in C where $$")), @"System"); + var content = @"var t = from c in C where $$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task GroupClauseGroupExpressionPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = from c in C group $$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = from c in C group $$")), @"System"); + var content = @"var t = from c in C group $$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task GroupClauseByExpressionPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = from c in C group g by $$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = from c in C group g by $$")), @"System"); + var content = @"var t = from c in C group g by $$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task IfStatement() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"if ($$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"if ($$")), @"System"); + var content = @"if ($$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task SwitchStatement() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"switch($$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"switch($$")), @"System"); + var content = @"switch($$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task SwitchLabelCase() { var content = @"switch(i) { case $$"; - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"System"); + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task SwitchPatternLabelCase() { var content = @"switch(i) { case $$ when"; - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"System"); + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33915")] public async Task SwitchExpressionFirstBranch() { var content = @"i switch { $$"; - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"System"); + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33915")] public async Task SwitchExpressionSecondBranch() { var content = @"i switch { 1 => true, $$"; - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"System"); + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33915")] public async Task PositionalPatternFirstPosition() { var content = @"i is ($$"; - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"System"); + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33915")] public async Task PositionalPatternSecondPosition() { var content = @"i is (1, $$"; - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"System"); + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33915")] public async Task PropertyPatternFirstPosition() { var content = @"i is { P: $$"; - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"System"); + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33915")] public async Task PropertyPatternSecondPosition() { var content = @"i is { P1: 1, P2: $$"; - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"System"); + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task InitializerExpression() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = new [] { $$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = new [] { $$")), @"System"); + var content = @"var t = new [] { $$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/30784")] From 6b6a5e8423a947a64333b992331747442818e566 Mon Sep 17 00:00:00 2001 From: Rekkonnect <8298332+Rekkonnect@users.noreply.github.com> Date: Sat, 12 Oct 2024 15:14:09 +0300 Subject: [PATCH 093/508] Another batch of tests --- .../SymbolCompletionProviderTests.cs | 212 +++++++++++------- 1 file changed, 126 insertions(+), 86 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs index ce352480f009b..0fd6b7597ea69 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs @@ -12584,10 +12584,12 @@ public async Task EnumBaseList1(string underlyingType) { var source = "enum E : $$"; - await VerifyItemExistsAsync(source, "System"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("System"), - // Not accessible in the given context - await VerifyItemIsAbsentAsync(source, underlyingType); + // Not accessible in the given context + CompletionTestExpectedResult.Absent(underlyingType), + ]); } [Theory, MemberData(nameof(ValidEnumUnderlyingTypeNames))] @@ -12840,9 +12842,11 @@ void M(string s) } """; - await VerifyItemExistsAsync(source, "endIndex"); - await VerifyItemExistsAsync(source, "Test"); - await VerifyItemExistsAsync(source, "C"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("endIndex"), + CompletionTestExpectedResult.Exists("Test"), + CompletionTestExpectedResult.Exists("C"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66903")] @@ -12861,9 +12865,11 @@ void M(string s) } """; - await VerifyItemExistsAsync(source, "endIndex"); - await VerifyItemExistsAsync(source, "Test"); - await VerifyItemExistsAsync(source, "C"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("endIndex"), + CompletionTestExpectedResult.Exists("Test"), + CompletionTestExpectedResult.Exists("C"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25572")] @@ -12886,10 +12892,12 @@ void M() } """; - await VerifyItemExistsAsync(source, "foo"); - await VerifyItemExistsAsync(source, "M"); - await VerifyItemExistsAsync(source, "System"); - await VerifyItemIsAbsentAsync(source, "Int32"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("foo"), + CompletionTestExpectedResult.Exists("M"), + CompletionTestExpectedResult.Exists("System"), + CompletionTestExpectedResult.Absent("Int32"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25572")] @@ -12910,9 +12918,11 @@ void A() { } } """; - await VerifyItemExistsAsync(source, "System"); - await VerifyItemExistsAsync(source, "C"); - await VerifyItemIsAbsentAsync(source, "other"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("System"), + CompletionTestExpectedResult.Exists("C"), + CompletionTestExpectedResult.Absent("other"), + ]); } public static readonly IEnumerable PatternMatchingPrecedingPatterns = new object[][] @@ -12945,12 +12955,14 @@ public async Task PatternMatching_01(string precedingPattern) var expression = $"return input {precedingPattern} Constants.$$"; var source = WrapPatternMatchingSource(expression); - await VerifyItemExistsAsync(source, "A"); - await VerifyItemExistsAsync(source, "B"); - await VerifyItemExistsAsync(source, "C"); - await VerifyItemIsAbsentAsync(source, "D"); - await VerifyItemIsAbsentAsync(source, "M"); - await VerifyItemExistsAsync(source, "R"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("A"), + CompletionTestExpectedResult.Exists("B"), + CompletionTestExpectedResult.Exists("C"), + CompletionTestExpectedResult.Absent("D"), + CompletionTestExpectedResult.Absent("M"), + CompletionTestExpectedResult.Exists("R"), + ]); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/70226")] @@ -12960,12 +12972,14 @@ public async Task PatternMatching_02(string precedingPattern) var expression = $"return input {precedingPattern} Constants.R.$$"; var source = WrapPatternMatchingSource(expression); - await VerifyItemExistsAsync(source, "A"); - await VerifyItemExistsAsync(source, "B"); - await VerifyItemIsAbsentAsync(source, "C"); - await VerifyItemIsAbsentAsync(source, "D"); - await VerifyItemIsAbsentAsync(source, "M"); - await VerifyItemIsAbsentAsync(source, "R"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("A"), + CompletionTestExpectedResult.Exists("B"), + CompletionTestExpectedResult.Absent("C"), + CompletionTestExpectedResult.Absent("D"), + CompletionTestExpectedResult.Absent("M"), + CompletionTestExpectedResult.Absent("R"), + ]); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/70226")] @@ -12975,9 +12989,11 @@ public async Task PatternMatching_03(string precedingPattern) var expression = $"return input {precedingPattern} $$"; var source = WrapPatternMatchingSource(expression); - await VerifyItemExistsAsync(source, "C"); - await VerifyItemExistsAsync(source, "Constants"); - await VerifyItemExistsAsync(source, "System"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("C"), + CompletionTestExpectedResult.Exists("Constants"), + CompletionTestExpectedResult.Exists("System"), + ]); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/70226")] @@ -12988,8 +13004,12 @@ public async Task PatternMatching_04(string precedingPattern) var source = WrapPatternMatchingSource(expression); // In scripts, we also get a Script class containing our defined types - await VerifyItemExistsAsync(source, "C", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemExistsAsync(source, "Constants", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync(source, + [ + CompletionTestExpectedResult.Exists("C"), + CompletionTestExpectedResult.Exists("Constants"), + ], + sourceCodeKind: SourceCodeKind.Regular); await VerifyItemExistsAsync(source, "System"); } @@ -12999,10 +13019,12 @@ public async Task PatternMatching_05() var expression = $"return $$ is Constants.A"; var source = WrapPatternMatchingSource(expression); - await VerifyItemExistsAsync(source, "input"); - await VerifyItemExistsAsync(source, "Constants"); - await VerifyItemExistsAsync(source, "C"); - await VerifyItemExistsAsync(source, "M"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("input"), + CompletionTestExpectedResult.Exists("Constants"), + CompletionTestExpectedResult.Exists("C"), + CompletionTestExpectedResult.Exists("M"), + ]); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/70226")] @@ -13012,12 +13034,14 @@ public async Task PatternMatching_06(string precedingPattern) var expression = $"return input {precedingPattern} Constants.$$.A"; var source = WrapPatternMatchingSource(expression); - await VerifyItemExistsAsync(source, "A"); - await VerifyItemExistsAsync(source, "B"); - await VerifyItemExistsAsync(source, "C"); - await VerifyItemIsAbsentAsync(source, "D"); - await VerifyItemIsAbsentAsync(source, "M"); - await VerifyItemExistsAsync(source, "R"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("A"), + CompletionTestExpectedResult.Exists("B"), + CompletionTestExpectedResult.Exists("C"), + CompletionTestExpectedResult.Absent("D"), + CompletionTestExpectedResult.Absent("M"), + CompletionTestExpectedResult.Exists("R"), + ]); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/70226")] @@ -13027,12 +13051,14 @@ public async Task PatternMatching_07(string precedingPattern) var expression = $"return input {precedingPattern} Enum.$$"; var source = WrapPatternMatchingSource(expression); - await VerifyItemExistsAsync(source, "A"); - await VerifyItemExistsAsync(source, "B"); - await VerifyItemExistsAsync(source, "C"); - await VerifyItemExistsAsync(source, "D"); - await VerifyItemIsAbsentAsync(source, "M"); - await VerifyItemIsAbsentAsync(source, "R"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("A"), + CompletionTestExpectedResult.Exists("B"), + CompletionTestExpectedResult.Exists("C"), + CompletionTestExpectedResult.Exists("D"), + CompletionTestExpectedResult.Absent("M"), + CompletionTestExpectedResult.Absent("R"), + ]); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/70226")] @@ -13042,14 +13068,16 @@ public async Task PatternMatching_08(string precedingPattern) var expression = $"return input {precedingPattern} nameof(Constants.$$"; var source = WrapPatternMatchingSource(expression); - await VerifyItemExistsAsync(source, "A"); - await VerifyItemExistsAsync(source, "B"); - await VerifyItemExistsAsync(source, "C"); - await VerifyItemExistsAsync(source, "D"); - await VerifyItemExistsAsync(source, "E"); - await VerifyItemExistsAsync(source, "M"); - await VerifyItemExistsAsync(source, "R"); - await VerifyItemExistsAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("A"), + CompletionTestExpectedResult.Exists("B"), + CompletionTestExpectedResult.Exists("C"), + CompletionTestExpectedResult.Exists("D"), + CompletionTestExpectedResult.Exists("E"), + CompletionTestExpectedResult.Exists("M"), + CompletionTestExpectedResult.Exists("R"), + CompletionTestExpectedResult.Exists("ToString"), + ]); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/70226")] @@ -13059,11 +13087,13 @@ public async Task PatternMatching_09(string precedingPattern) var expression = $"return input {precedingPattern} nameof(Constants.R.$$"; var source = WrapPatternMatchingSource(expression); - await VerifyItemExistsAsync(source, "A"); - await VerifyItemExistsAsync(source, "B"); - await VerifyItemExistsAsync(source, "D"); - await VerifyItemExistsAsync(source, "E"); - await VerifyItemExistsAsync(source, "M"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("A"), + CompletionTestExpectedResult.Exists("B"), + CompletionTestExpectedResult.Exists("D"), + CompletionTestExpectedResult.Exists("E"), + CompletionTestExpectedResult.Exists("M"), + ]); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/70226")] @@ -13073,13 +13103,15 @@ public async Task PatternMatching_10(string precedingPattern) var expression = $"return input {precedingPattern} nameof(Constants.$$.A"; var source = WrapPatternMatchingSource(expression); - await VerifyItemExistsAsync(source, "A"); - await VerifyItemExistsAsync(source, "B"); - await VerifyItemExistsAsync(source, "C"); - await VerifyItemExistsAsync(source, "D"); - await VerifyItemExistsAsync(source, "E"); - await VerifyItemExistsAsync(source, "M"); - await VerifyItemExistsAsync(source, "R"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("A"), + CompletionTestExpectedResult.Exists("B"), + CompletionTestExpectedResult.Exists("C"), + CompletionTestExpectedResult.Exists("D"), + CompletionTestExpectedResult.Exists("E"), + CompletionTestExpectedResult.Exists("M"), + CompletionTestExpectedResult.Exists("R"), + ]); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/70226")] @@ -13089,12 +13121,14 @@ public async Task PatternMatching_11(string precedingPattern) var expression = $"return input {precedingPattern} [Constants.R(Constants.$$, nameof(Constants.R))]"; var source = WrapPatternMatchingSource(expression); - await VerifyItemExistsAsync(source, "A"); - await VerifyItemExistsAsync(source, "B"); - await VerifyItemExistsAsync(source, "C"); - await VerifyItemIsAbsentAsync(source, "D"); - await VerifyItemIsAbsentAsync(source, "M"); - await VerifyItemExistsAsync(source, "R"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("A"), + CompletionTestExpectedResult.Exists("B"), + CompletionTestExpectedResult.Exists("C"), + CompletionTestExpectedResult.Absent("D"), + CompletionTestExpectedResult.Absent("M"), + CompletionTestExpectedResult.Exists("R"), + ]); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/70226")] @@ -13104,12 +13138,14 @@ public async Task PatternMatching_12(string precedingPattern) var expression = $"return input {precedingPattern} [Constants.R(Constants.$$), nameof(Constants.R)]"; var source = WrapPatternMatchingSource(expression); - await VerifyItemExistsAsync(source, "A"); - await VerifyItemExistsAsync(source, "B"); - await VerifyItemExistsAsync(source, "C"); - await VerifyItemIsAbsentAsync(source, "D"); - await VerifyItemIsAbsentAsync(source, "M"); - await VerifyItemExistsAsync(source, "R"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("A"), + CompletionTestExpectedResult.Exists("B"), + CompletionTestExpectedResult.Exists("C"), + CompletionTestExpectedResult.Absent("D"), + CompletionTestExpectedResult.Absent("M"), + CompletionTestExpectedResult.Exists("R"), + ]); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/70226")] @@ -13119,8 +13155,10 @@ public async Task PatternMatching_13(string precedingPattern) var expression = $"return input {precedingPattern} [Constants.R(Constants.A) {{ P.$$: Constants.A, InstanceProperty: 153 }}, nameof(Constants.R)]"; var source = WrapPatternMatchingSource(expression); - await VerifyItemExistsAsync(source, "Length"); - await VerifyItemIsAbsentAsync(source, "Constants"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("Length"), + CompletionTestExpectedResult.Absent("Constants"), + ]); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/70226")] @@ -13130,9 +13168,11 @@ public async Task PatternMatching_14(string precedingPattern) var expression = $"return input {precedingPattern} [Constants.R(Constants.A) {{ P: $$ }}, nameof(Constants.R)]"; var source = WrapPatternMatchingSource(expression); - await VerifyItemIsAbsentAsync(source, "InstanceProperty"); - await VerifyItemIsAbsentAsync(source, "P"); - await VerifyItemExistsAsync(source, "Constants"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("Constants"), + CompletionTestExpectedResult.Absent("InstanceProperty"), + CompletionTestExpectedResult.Absent("P"), + ]); } private static string WrapPatternMatchingSource(string returnedExpression) From 2a0f83d333d1652ce5ef0fc6b4636ad8ffc50cbb Mon Sep 17 00:00:00 2001 From: Rekkonnect <8298332+Rekkonnect@users.noreply.github.com> Date: Sat, 12 Oct 2024 20:52:34 +0300 Subject: [PATCH 094/508] Optimize more tests --- .../SymbolCompletionProviderTests.cs | 630 ++++++++++++------ 1 file changed, 417 insertions(+), 213 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs index 0fd6b7597ea69..6122be59c133a 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs @@ -1741,8 +1741,12 @@ class CL where T : $$", @"Test"); [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/30784")] public async Task TypeParameterConstraintClause_StillShowStaticAndSealedTypesNotDirectlyInConstraint() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", @"class CL where T : IList<$$"), @"System"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", @"class CL where T : IList<$$"), @"String"); + var content = @"class CL where T : IList<$$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/30784")] @@ -1790,15 +1794,23 @@ class CL where T : A, $$", @"Test"); [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/30784")] public async Task TypeParameterConstraintClauseList_StillShowStaticAndSealedTypesNotDirectlyInConstraint() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", @"class CL where T : A, IList<$$"), @"System"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", @"class CL where T : A, IList<$$"), @"String"); + var content = @"class CL where T : A, IList<$$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task TypeParameterConstraintClauseAnotherWhere() { - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", @"class CL where T : A where$$"), @"System"); - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", @"class CL where T : A where$$"), @"String"); + var content = @"class CL where T : A where$$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Absent("String"), + CompletionTestExpectedResult.Absent("System") + ]); } [Fact] @@ -1826,29 +1838,45 @@ public async Task TypeSymbolOfTypeParameterConstraintClause3() [Fact] public async Task BaseList1() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", @"class CL : $$"), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", @"class CL : $$"), @"System"); + var content = @"class CL : $$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task BaseList2() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", @"class CL : B, $$"), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", @"class CL : B, $$"), @"System"); + var content = @"class CL : B, $$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] public async Task BaseListWhere() { - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", @"class CL : B where$$"), @"String"); - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", @"class CL : B where$$"), @"System"); + var content = @"class CL : B where$$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Absent("String"), + CompletionTestExpectedResult.Absent("System") + ]); } [Fact] public async Task AliasedName() { - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", AddInsideMethod(@"global::$$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"global::$$")), @"System"); + var content = @"global::$$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Absent("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] @@ -1862,15 +1890,23 @@ public async Task AliasedType() [Fact] public async Task ConstructorInitializer() { - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", @"class C { C() : $$"), @"String"); - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", @"class C { C() : $$"), @"System"); + var content = @"class C { C() : $$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Absent("String"), + CompletionTestExpectedResult.Absent("System") + ]); } [Fact] public async Task Typeof1() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"typeof($$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"typeof($$")), @"System"); + var content = @"typeof($$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] @@ -1880,8 +1916,12 @@ public async Task Typeof2() [Fact] public async Task Sizeof1() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"sizeof($$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"sizeof($$")), @"System"); + var content = @"sizeof($$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] @@ -1891,8 +1931,12 @@ public async Task Sizeof2() [Fact] public async Task Default1() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"default($$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"default($$")), @"System"); + var content = @"default($$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("System") + ]); } [Fact] @@ -2121,8 +2165,10 @@ void Bar() } } "; - await VerifyItemIsAbsentAsync(markup, "Hidden"); - await VerifyItemExistsAsync(markup, "Goo"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Absent("Hidden"), + CompletionTestExpectedResult.Exists("Goo") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539812")] @@ -2142,8 +2188,10 @@ void Bar() } } "; - await VerifyItemIsAbsentAsync(markup, "Hidden"); - await VerifyItemExistsAsync(markup, "Goo"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Absent("Hidden"), + CompletionTestExpectedResult.Exists("Goo") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539812")] @@ -2163,9 +2211,11 @@ void Bar() } } "; - await VerifyItemIsAbsentAsync(markup, "Hidden"); - await VerifyItemExistsAsync(markup, "Goo"); - await VerifyItemIsAbsentAsync(markup, "Bar"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Absent("Hidden"), + CompletionTestExpectedResult.Exists("Goo"), + CompletionTestExpectedResult.Absent("Bar"), + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539812")] @@ -2185,8 +2235,10 @@ void Bar() } } "; - await VerifyItemIsAbsentAsync(markup, "Hidden"); - await VerifyItemExistsAsync(markup, "Goo"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Absent("Hidden"), + CompletionTestExpectedResult.Exists("Goo") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539812")] @@ -2206,8 +2258,10 @@ void Bar() } } "; - await VerifyItemIsAbsentAsync(markup, "Hidden"); - await VerifyItemExistsAsync(markup, "Goo"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Absent("Hidden"), + CompletionTestExpectedResult.Exists("Goo") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539812")] @@ -2227,8 +2281,10 @@ void Bar() } } "; - await VerifyItemIsAbsentAsync(markup, "Hidden"); - await VerifyItemExistsAsync(markup, "Goo"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Absent("Hidden"), + CompletionTestExpectedResult.Exists("Goo") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539812")] @@ -2251,10 +2307,12 @@ void Bar() } } "; - await VerifyItemIsAbsentAsync(markup, "HiddenStatic"); - await VerifyItemExistsAsync(markup, "GooStatic"); - await VerifyItemIsAbsentAsync(markup, "HiddenInstance"); - await VerifyItemExistsAsync(markup, "GooInstance"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Absent("HiddenStatic"), + CompletionTestExpectedResult.Exists("GooStatic"), + CompletionTestExpectedResult.Absent("HiddenInstance"), + CompletionTestExpectedResult.Exists("GooInstance") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540155")] @@ -2422,8 +2480,10 @@ void M(String parameter) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Absent("parameter") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540212")] @@ -2439,8 +2499,10 @@ void M(String parameter) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Absent("parameter") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/24326")] @@ -2456,8 +2518,10 @@ void M(String parameter) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Absent("parameter") + ]); } [Fact] @@ -2473,8 +2537,10 @@ void M(ref $$) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "field"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Absent("field") + ]); } [Fact] @@ -2490,8 +2556,10 @@ void M(out $$) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "field"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Absent("field") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/24326")] @@ -2507,8 +2575,10 @@ void M(in $$) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "field"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Absent("field") + ]); } [Fact] @@ -2524,8 +2594,10 @@ void M(ref String parameter) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemExistsAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("parameter") + ]); } [Fact] @@ -2541,8 +2613,10 @@ void M(out String parameter) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemExistsAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("parameter") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/24326")] @@ -2558,8 +2632,10 @@ void M(in String parameter) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemExistsAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("parameter") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25569")] @@ -2575,8 +2651,10 @@ void M(String parameter) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemExistsAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Exists("parameter") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25569")] @@ -2592,8 +2670,10 @@ void M(String parameter) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Absent("parameter") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25569")] @@ -2609,8 +2689,10 @@ ref readonly $$ } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Absent("parameter") + ]); } [Fact] @@ -2626,8 +2708,10 @@ void M(String parameter) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Absent("parameter") + ]); } [Fact] @@ -2643,8 +2727,10 @@ void M(String parameter) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Absent("parameter") + ]); } [Fact] @@ -2660,8 +2746,10 @@ void M(String parameter) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Absent("parameter") + ]); } [Fact] @@ -2677,8 +2765,10 @@ void M(String parameter) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("String"), + CompletionTestExpectedResult.Absent("parameter") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35178")] @@ -3622,8 +3712,10 @@ namespace Namespace3.Namespace4 { class CustomAttribute : System.Attribute { } } } [Namespace1.$$]"; - await VerifyItemIsAbsentAsync(markup, "Namespace2"); - await VerifyItemExistsAsync(markup, "Namespace3"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Absent("Namespace2"), + CompletionTestExpectedResult.Exists("Namespace3"), + ]); } [Fact] @@ -3670,10 +3762,12 @@ namespace Namespace3.Namespace4 { class CustomAttribute : System.Attribute { } } } [$$]"; - await VerifyItemExistsAsync(markup, "Namespace1Alias"); - await VerifyItemIsAbsentAsync(markup, "Namespace2Alias"); - await VerifyItemExistsAsync(markup, "Namespace3Alias"); - await VerifyItemExistsAsync(markup, "Namespace4Alias"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("Namespace1Alias"), + CompletionTestExpectedResult.Absent("Namespace2Alias"), + CompletionTestExpectedResult.Exists("Namespace3Alias"), + CompletionTestExpectedResult.Exists("Namespace4Alias"), + ]); } [Fact] @@ -4139,10 +4233,12 @@ void M() } "; - await VerifyItemExistsAsync(markup, "a"); - await VerifyItemExistsAsync(markup, "b"); - await VerifyItemExistsAsync(markup, "c"); - await VerifyItemIsAbsentAsync(markup, "Equals"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("a"), + CompletionTestExpectedResult.Exists("b"), + CompletionTestExpectedResult.Exists("c"), + CompletionTestExpectedResult.Absent("Equals"), + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543104")] @@ -4160,10 +4256,12 @@ void M() } "; - await VerifyItemIsAbsentAsync(markup, "a"); - await VerifyItemIsAbsentAsync(markup, "b"); - await VerifyItemIsAbsentAsync(markup, "c"); - await VerifyItemExistsAsync(markup, "Equals"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Absent("a"), + CompletionTestExpectedResult.Absent("b"), + CompletionTestExpectedResult.Absent("c"), + CompletionTestExpectedResult.Exists("Equals"), + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/529138")] @@ -7476,9 +7574,10 @@ static void Main() A.$$ } }"; - - await VerifyItemExistsAsync(markup, "Goo"); - await VerifyItemExistsAsync(markup, "Bar"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("Goo"), + CompletionTestExpectedResult.Exists("Bar"), + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545647")] @@ -7662,8 +7761,10 @@ class Program public async $$ }"; - await VerifyItemExistsAsync(markup, "Task"); - await VerifyItemIsAbsentAsync(markup, "Console"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("Task"), + CompletionTestExpectedResult.Absent("Console"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/60341")] @@ -7678,8 +7779,10 @@ public async $$ class Test {}"; - await VerifyItemExistsAsync(markup, "Task"); - await VerifyItemIsAbsentAsync(markup, "Test"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("Task"), + CompletionTestExpectedResult.Absent("Test"), + ]); } [Fact] @@ -7818,8 +7921,10 @@ class Request } }"; // Nothing should be found: no awaiter for request. - await VerifyItemIsAbsentAsync(markup, "Result"); - await VerifyItemIsAbsentAsync(markup, "ReadAsStreamAsync"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Absent("Result"), + CompletionTestExpectedResult.Absent("ReadAsStreamAsync"), + ]); } [Fact] @@ -7846,8 +7951,10 @@ class Request } }"; - await VerifyItemIsAbsentAsync(markup, "Result"); - await VerifyItemExistsAsync(markup, "ReadAsStreamAsync"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Absent("Result"), + CompletionTestExpectedResult.Exists("ReadAsStreamAsync"), + ]); } [Fact] @@ -8663,8 +8770,10 @@ public void goo() var q = a?.$$AB.BA.AB.BA; } }"; - await VerifyItemExistsAsync(markup, "AA"); - await VerifyItemExistsAsync(markup, "AB"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("AA"), + CompletionTestExpectedResult.Exists("AB"), + ]); } [Fact] @@ -8686,8 +8795,10 @@ public void goo() var q = a?.s?.$$; } }"; - await VerifyItemExistsAsync(markup, "i"); - await VerifyItemIsAbsentAsync(markup, "value"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("i"), + CompletionTestExpectedResult.Absent("Value"), + ]); } [Fact] @@ -8708,8 +8819,10 @@ public void goo() var q = s?.$$i?.ToString(); } }"; - await VerifyItemExistsAsync(markup, "i"); - await VerifyItemIsAbsentAsync(markup, "value"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("i"), + CompletionTestExpectedResult.Absent("Value"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/54361")] @@ -8724,8 +8837,10 @@ void M(System.DateTime? dt) } } "; - await VerifyItemExistsAsync(markup, "Day"); - await VerifyItemIsAbsentAsync(markup, "Value"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("Day"), + CompletionTestExpectedResult.Absent("Value"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/54361")] @@ -8740,8 +8855,10 @@ void M(System.DateTime? dt) } } "; - await VerifyItemExistsAsync(markup, "Value"); - await VerifyItemIsAbsentAsync(markup, "Day"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("Value"), + CompletionTestExpectedResult.Absent("Day"), + ]); } [Fact] @@ -9154,8 +9271,10 @@ void goo() } } "; - await VerifyItemExistsAsync(markup, "x"); - await VerifyItemExistsAsync(markup, "y"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("x"), + CompletionTestExpectedResult.Exists("y"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/1663")] @@ -9385,9 +9504,11 @@ static class D { } namespace M { } }"; - await VerifyItemIsAbsentAsync(markup, "A"); - await VerifyItemIsAbsentAsync(markup, "B"); - await VerifyItemExistsAsync(markup, "N"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Absent("A"), + CompletionTestExpectedResult.Absent("B"), + CompletionTestExpectedResult.Exists("N"), + ]); } [Fact] @@ -9407,9 +9528,11 @@ static class D { } namespace M { } }"; - await VerifyItemIsAbsentAsync(markup, "C"); - await VerifyItemIsAbsentAsync(markup, "D"); - await VerifyItemExistsAsync(markup, "M"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Absent("C"), + CompletionTestExpectedResult.Absent("D"), + CompletionTestExpectedResult.Exists("M"), + ]); } [Fact] @@ -9429,9 +9552,11 @@ static class D { } namespace M { } }"; - await VerifyItemExistsAsync(markup, "A"); - await VerifyItemExistsAsync(markup, "B"); - await VerifyItemExistsAsync(markup, "N"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("A"), + CompletionTestExpectedResult.Exists("B"), + CompletionTestExpectedResult.Exists("N"), + ]); } [Fact] @@ -9451,9 +9576,11 @@ static class D { } namespace M { } }"; - await VerifyItemExistsAsync(markup, "C"); - await VerifyItemExistsAsync(markup, "D"); - await VerifyItemExistsAsync(markup, "M"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("C"), + CompletionTestExpectedResult.Exists("D"), + CompletionTestExpectedResult.Exists("M"), + ]); } [Fact] @@ -9473,9 +9600,11 @@ static class D { } namespace M { } }"; - await VerifyItemExistsAsync(markup, "A"); - await VerifyItemExistsAsync(markup, "B"); - await VerifyItemExistsAsync(markup, "N"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("A"), + CompletionTestExpectedResult.Exists("B"), + CompletionTestExpectedResult.Exists("N"), + ]); } [Fact] @@ -9495,9 +9624,11 @@ static class D { } namespace M { } }"; - await VerifyItemExistsAsync(markup, "C"); - await VerifyItemExistsAsync(markup, "D"); - await VerifyItemExistsAsync(markup, "M"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("C"), + CompletionTestExpectedResult.Exists("D"), + CompletionTestExpectedResult.Exists("M"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67985")] @@ -9517,9 +9648,11 @@ static class D { } namespace M { } }"; - await VerifyItemExistsAsync(markup, "A"); - await VerifyItemExistsAsync(markup, "B"); - await VerifyItemExistsAsync(markup, "N"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("A"), + CompletionTestExpectedResult.Exists("B"), + CompletionTestExpectedResult.Exists("N"), + ]); } [Fact] @@ -9539,9 +9672,11 @@ static class D { } namespace M { } }"; - await VerifyItemExistsAsync(markup, "A"); - await VerifyItemIsAbsentAsync(markup, "B"); - await VerifyItemExistsAsync(markup, "N"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("A"), + CompletionTestExpectedResult.Absent("B"), + CompletionTestExpectedResult.Exists("N"), + ]); } [Fact] @@ -9561,9 +9696,11 @@ class C { } namespace M { } }"; - await VerifyItemExistsAsync(markup, "C"); - await VerifyItemIsAbsentAsync(markup, "D"); - await VerifyItemExistsAsync(markup, "M"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("C"), + CompletionTestExpectedResult.Absent("D"), + CompletionTestExpectedResult.Exists("M"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67985")] @@ -9583,9 +9720,11 @@ static class D { } namespace M { } }"; - await VerifyItemExistsAsync(markup, "A"); - await VerifyItemIsAbsentAsync(markup, "B"); - await VerifyItemExistsAsync(markup, "N"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("A"), + CompletionTestExpectedResult.Absent("B"), + CompletionTestExpectedResult.Exists("N"), + ]); } [Fact] @@ -9606,9 +9745,11 @@ interface I { } namespace M { } }"; - await VerifyItemExistsAsync(markup, "C"); - await VerifyItemExistsAsync(markup, "I"); - await VerifyItemExistsAsync(markup, "M"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("C"), + CompletionTestExpectedResult.Exists("I"), + CompletionTestExpectedResult.Exists("M"), + ]); } [Fact] @@ -9629,9 +9770,11 @@ static class D { } namespace M { } }"; - await VerifyItemExistsAsync(markup, "A"); - await VerifyItemExistsAsync(markup, "I"); - await VerifyItemExistsAsync(markup, "N"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("A"), + CompletionTestExpectedResult.Exists("I"), + CompletionTestExpectedResult.Exists("N"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67985")] @@ -9652,9 +9795,11 @@ static class D { } namespace M { } }"; - await VerifyItemExistsAsync(markup, "A"); - await VerifyItemExistsAsync(markup, "I"); - await VerifyItemExistsAsync(markup, "N"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("A"), + CompletionTestExpectedResult.Exists("I"), + CompletionTestExpectedResult.Exists("N"), + ]); } [Fact] @@ -9683,8 +9828,10 @@ void M() } "; - await VerifyItemIsAbsentAsync(markup, "Goo"); - await VerifyItemIsAbsentAsync(markup, "Bar"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Absent("Goo"), + CompletionTestExpectedResult.Absent("Bar"), + ]); } [Fact] @@ -9715,8 +9862,10 @@ void M() } "; - await VerifyItemIsAbsentAsync(markup, "Goo"); - await VerifyItemIsAbsentAsync(markup, "Bar"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Absent("Goo"), + CompletionTestExpectedResult.Absent("Bar"), + ]); } [Fact] @@ -9748,8 +9897,10 @@ void M() } "; - await VerifyItemExistsAsync(markup, "Goo"); - await VerifyItemExistsAsync(markup, "Bar"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("Goo"), + CompletionTestExpectedResult.Exists("Bar"), + ]); } [Fact] @@ -9782,8 +9933,10 @@ void M() } "; - await VerifyItemExistsAsync(markup, "Goo"); - await VerifyItemExistsAsync(markup, "Bar"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("Goo"), + CompletionTestExpectedResult.Exists("Bar"), + ]); } [Fact] @@ -9815,8 +9968,10 @@ void M() } "; - await VerifyItemExistsAsync(markup, "Goo"); - await VerifyItemIsAbsentAsync(markup, "Bar"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("Goo"), + CompletionTestExpectedResult.Absent("Bar"), + ]); } [Fact] @@ -9848,8 +10003,10 @@ void M() } "; - await VerifyItemIsAbsentAsync(markup, "Goo"); - await VerifyItemExistsAsync(markup, "Bar"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Absent("Goo"), + CompletionTestExpectedResult.Exists("Bar"), + ]); } [Fact] @@ -9882,8 +10039,10 @@ void M() } "; - await VerifyItemExistsAsync(markup, "Goo"); - await VerifyItemExistsAsync(markup, "Bar"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("Goo"), + CompletionTestExpectedResult.Exists("Bar"), + ]); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/7932")] @@ -10861,9 +11020,11 @@ void M(SomeCollection c) } }"; - await VerifyItemIsAbsentAsync(markup, "Substring"); - await VerifyItemExistsAsync(markup, "A"); - await VerifyItemExistsAsync(markup, "B"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Absent("Substring"), + CompletionTestExpectedResult.Exists("A"), + CompletionTestExpectedResult.Exists("B"), + ]); } [Fact, WorkItem("https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1056325")] @@ -10972,8 +11133,10 @@ static void Create(Dictionary list, Action expression) { } class Product1 { public void MyProperty1() { } } class Product2 { public void MyProperty2() { } }"; - await VerifyItemExistsAsync(markup, "MyProperty1"); - await VerifyItemExistsAsync(markup, "MyProperty2"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("MyProperty1"), + CompletionTestExpectedResult.Exists("MyProperty2"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42997")] @@ -10999,9 +11162,11 @@ class Product1 { public void MyProperty1() { } } class Product2 { public void MyProperty2() { } } class Product3 { public void MyProperty3() { } }"; - await VerifyItemExistsAsync(markup, "MyProperty1"); - await VerifyItemExistsAsync(markup, "MyProperty2"); - await VerifyItemExistsAsync(markup, "MyProperty3"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("MyProperty1"), + CompletionTestExpectedResult.Exists("MyProperty2"), + CompletionTestExpectedResult.Exists("MyProperty3") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42997")] @@ -11288,9 +11453,11 @@ class AnotherBuilder public int AnotherSomething { get; set; } }"; - await VerifyItemIsAbsentAsync(markup, "AnotherSomething"); - await VerifyItemIsAbsentAsync(markup, "FirstOrDefault"); - await VerifyItemExistsAsync(markup, "Something"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Absent("AnotherSomething"), + CompletionTestExpectedResult.Absent("FirstOrDefault"), + CompletionTestExpectedResult.Exists("Something") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36029")] @@ -11319,9 +11486,11 @@ class AnotherBuilder public int AnotherSomething { get; set; } }"; - await VerifyItemIsAbsentAsync(markup, "Something"); - await VerifyItemIsAbsentAsync(markup, "FirstOrDefault"); - await VerifyItemExistsAsync(markup, "AnotherSomething"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Absent("Something"), + CompletionTestExpectedResult.Absent("FirstOrDefault"), + CompletionTestExpectedResult.Exists("AnotherSomething") + ]); } [Fact, Trait(Traits.Feature, Traits.Features.TargetTypedCompletion)] @@ -11425,9 +11594,14 @@ public void M(string s) } }"; - await VerifyItemExistsAsync(markup, "M"); - await VerifyItemExistsAsync(markup, "Equals"); - await VerifyItemIsAbsentAsync(markup, "DoSomething", displayTextSuffix: "<>"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("M"), + CompletionTestExpectedResult.Exists("Equals"), + CompletionTestExpectedResult.Absent("DoSomething") with + { + DisplayTextSuffix = "<>" + }, + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38074")] @@ -11704,9 +11878,11 @@ void M(RankedMusicians m) } }"; // VerifyItemExistsAsync also tests with the item typed. - await VerifyItemExistsAsync(markup, "BillyJoel"); - await VerifyItemExistsAsync(markup, "EveryoneElse"); - await VerifyItemIsAbsentAsync(markup, "Equals"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("BillyJoel"), + CompletionTestExpectedResult.Exists("EveryoneElse"), + CompletionTestExpectedResult.Absent("Equals"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/49072")] @@ -11732,9 +11908,11 @@ void M(RankedMusicians m) } }"; // VerifyItemExistsAsync also tests with the item typed. - await VerifyItemExistsAsync(markup, "BillyJoel"); - await VerifyItemExistsAsync(markup, "EveryoneElse"); - await VerifyItemIsAbsentAsync(markup, "Equals"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("BillyJoel"), + CompletionTestExpectedResult.Exists("EveryoneElse"), + CompletionTestExpectedResult.Absent("Equals"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/49072")] @@ -11760,9 +11938,11 @@ void M(C m) } }"; // VerifyItemExistsAsync also tests with the item typed. - await VerifyItemExistsAsync(markup, "BillyJoel"); - await VerifyItemExistsAsync(markup, "EveryoneElse"); - await VerifyItemIsAbsentAsync(markup, "Equals"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("BillyJoel"), + CompletionTestExpectedResult.Exists("EveryoneElse"), + CompletionTestExpectedResult.Absent("Equals"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/49072")] @@ -11807,9 +11987,11 @@ void M(RankedMusicians m) } }"; // VerifyItemExistsAsync also tests with the item typed. - await VerifyItemExistsAsync(markup, "BillyJoel"); - await VerifyItemExistsAsync(markup, "EveryoneElse"); - await VerifyItemIsAbsentAsync(markup, "Equals"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("BillyJoel"), + CompletionTestExpectedResult.Exists("EveryoneElse"), + CompletionTestExpectedResult.Absent("Equals"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/49072")] @@ -11835,9 +12017,11 @@ void M(RankedMusicians m) } }"; // VerifyItemExistsAsync also tests with the item typed. - await VerifyItemExistsAsync(markup, "BillyJoel"); - await VerifyItemExistsAsync(markup, "EveryoneElse"); - await VerifyItemIsAbsentAsync(markup, "Equals"); + await VerifyExpectedItemsAsync(markup, [ + CompletionTestExpectedResult.Exists("BillyJoel"), + CompletionTestExpectedResult.Exists("EveryoneElse"), + CompletionTestExpectedResult.Absent("Equals"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/49609")] @@ -13416,8 +13600,10 @@ public enum Status Closed, } """; - await VerifyItemExistsAsync(source, "Undisclosed"); - await VerifyItemIsAbsentAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("Undisclosed"), + CompletionTestExpectedResult.Absent("ToString"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75350")] @@ -13446,8 +13632,10 @@ public enum Status Closed, } """; - await VerifyItemExistsAsync(source, "Undisclosed"); - await VerifyItemIsAbsentAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("Undisclosed"), + CompletionTestExpectedResult.Absent("ToString"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75350")] @@ -13478,8 +13666,10 @@ public enum StatusEn Closed, } """; - await VerifyItemExistsAsync(source, "StatusEn"); - await VerifyItemIsAbsentAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("StatusEn"), + CompletionTestExpectedResult.Absent("ToString"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75350")] @@ -13510,8 +13700,10 @@ public enum StatusEn Closed, } """; - await VerifyItemExistsAsync(source, "Undisclosed"); - await VerifyItemIsAbsentAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("Undisclosed"), + CompletionTestExpectedResult.Absent("ToString"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75350")] @@ -13543,8 +13735,10 @@ public enum StatusEn Closed, } """; - await VerifyItemExistsAsync(source, "Undisclosed"); - await VerifyItemIsAbsentAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("Undisclosed"), + CompletionTestExpectedResult.Absent("ToString"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75350")] @@ -13571,8 +13765,10 @@ public enum Status Closed, } """; - await VerifyItemExistsAsync(source, "Undisclosed"); - await VerifyItemIsAbsentAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("Undisclosed"), + CompletionTestExpectedResult.Absent("ToString"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75350")] @@ -13599,8 +13795,10 @@ public enum Status Closed, } """; - await VerifyItemExistsAsync(source, "Undisclosed"); - await VerifyItemIsAbsentAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("Undisclosed"), + CompletionTestExpectedResult.Absent("ToString"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75350")] @@ -13629,8 +13827,10 @@ public enum StatusEn Closed, } """; - await VerifyItemExistsAsync(source, "StatusEn"); - await VerifyItemIsAbsentAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("StatusEn"), + CompletionTestExpectedResult.Absent("ToString"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75350")] @@ -13659,8 +13859,10 @@ public enum StatusEn Closed, } """; - await VerifyItemExistsAsync(source, "Undisclosed"); - await VerifyItemIsAbsentAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("Undisclosed"), + CompletionTestExpectedResult.Absent("ToString"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75350")] @@ -13690,8 +13892,10 @@ public enum StatusEn Closed, } """; - await VerifyItemExistsAsync(source, "Undisclosed"); - await VerifyItemIsAbsentAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("Undisclosed"), + CompletionTestExpectedResult.Absent("ToString"), + ]); } #region Collection expressions From ef5cbd40b6dda2bfdc439a7e0ea271d439c09c5a Mon Sep 17 00:00:00 2001 From: Rekkonnect <8298332+Rekkonnect@users.noreply.github.com> Date: Sat, 12 Oct 2024 21:29:41 +0300 Subject: [PATCH 095/508] Fix formatting --- .../TestUtilities/Completion/AbstractCompletionProviderTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs b/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs index 65f2e0fca4b2a..8d39306021a5c 100644 --- a/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs +++ b/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs @@ -355,7 +355,7 @@ private async Task VerifyAsync( NonCompletionOptions?.SetGlobalOptions(workspace.GlobalOptions); await VerifyWorkerAsync( - code, position, usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionModeItem, + code, position, usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionModeItem, sourceKind, results, matchingFilters, flags, options, skipSpeculation); } } From 95803354a7a20952ba233a83b1ebd622ada8e45f Mon Sep 17 00:00:00 2001 From: Rekkonnect <8298332+Rekkonnect@users.noreply.github.com> Date: Sat, 12 Oct 2024 23:43:47 +0300 Subject: [PATCH 096/508] Suggestions and oversight fixes --- .../SymbolCompletionProviderTests.cs | 9 +++++---- .../Completion/AbstractCompletionProviderTests.cs | 13 +++++-------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs index 6122be59c133a..73f74fbaee2fe 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs @@ -263,7 +263,7 @@ await VerifyExpectedItemsAsync(code, [ [Fact] public async Task OpenStringLiteralInDirective() { - var code = "#r \"$$\""; + var code = "#r \"$$"; await VerifyExpectedItemsAsync( code, [ CompletionTestExpectedResult.Absent("String"), @@ -318,9 +318,10 @@ await VerifyExpectedItemsAsync(code, [ public async Task AssemblyAttribute2() { var code = @"[assembly: $$]"; - await VerifyExpectedItemsAsync(code, [ - CompletionTestExpectedResult.Absent("String"), - CompletionTestExpectedResult.Exists("System") + var source = AddUsingDirectives("using System;", code); + await VerifyExpectedItemsAsync(source, [ + CompletionTestExpectedResult.Exists("System"), + CompletionTestExpectedResult.Exists("AttributeUsage") ]); } diff --git a/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs b/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs index 8d39306021a5c..c9b30ef21e871 100644 --- a/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs +++ b/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs @@ -160,9 +160,8 @@ private protected async Task CheckResultsAsync( InlineDescription: inlineDescription, IsComplexTextEdit: isComplexTextEdit); - var expectedResultArray = new[] { expectedResult }; await CheckResultsAsync(document, position, usePreviousCharAsTrigger, deletedCharTrigger, - hasSuggestionModeItem, expectedResultArray, matchingFilters, flags, options); + hasSuggestionModeItem, [expectedResult], matchingFilters, flags, options); } private protected async Task CheckResultsAsync( @@ -198,7 +197,7 @@ private protected async Task CheckResultsAsync( Assert.Equal(hasSuggestionModeItem.Value, completionList.SuggestionModeItem != null); } - if (expectedResults.Length is 0) + if (expectedResults.Length == 0) { Assert.Equal(items.Count, 0); } @@ -316,7 +315,7 @@ private async Task VerifyAsync( string displayTextPrefix, string inlineDescription, bool? isComplexTextEdit, List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) { - IReadOnlyList evaluatedSourceCodeKinds = sourceCodeKind.HasValue ? [sourceCodeKind.Value] : [SourceCodeKind.Regular, SourceCodeKind.Script]; + SourceCodeKind[] evaluatedSourceCodeKinds = sourceCodeKind.HasValue ? [sourceCodeKind.Value] : [SourceCodeKind.Regular, SourceCodeKind.Script]; foreach (var sourceKind in evaluatedSourceCodeKinds) { using var workspaceFixture = GetOrCreateWorkspaceFixture(); @@ -342,7 +341,7 @@ private async Task VerifyAsync( CompletionTestExpectedResult[] results, bool? hasSuggestionModeItem, List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) { - IReadOnlyList evaluatedSourceCodeKinds = sourceCodeKind.HasValue ? [sourceCodeKind.Value] : [SourceCodeKind.Regular, SourceCodeKind.Script]; + SourceCodeKind[] evaluatedSourceCodeKinds = sourceCodeKind.HasValue ? [sourceCodeKind.Value] : [SourceCodeKind.Regular, SourceCodeKind.Script]; foreach (var sourceKind in evaluatedSourceCodeKinds) { using var workspaceFixture = GetOrCreateWorkspaceFixture(); @@ -524,11 +523,9 @@ private protected virtual async Task VerifyWorkerAsync( InlineDescription: inlineDescription, IsComplexTextEdit: isComplexTextEdit); - var expectedResultArray = new[] { expectedResult }; - await VerifyWorkerCoreAsync( code, position, usePreviousCharAsTrigger, deletedCharTrigger, - hasSuggestionModeItem, sourceCodeKind, expectedResultArray, + hasSuggestionModeItem, sourceCodeKind, [expectedResult], matchingFilters, flags, options, skipSpeculation); } From 31ea0b25d9b5ea88b27024f886e1a10d261a9acf Mon Sep 17 00:00:00 2001 From: Rekkonnect <8298332+Rekkonnect@users.noreply.github.com> Date: Sat, 12 Oct 2024 23:47:04 +0300 Subject: [PATCH 097/508] Shorten record to ItemExpectation --- .../AbstractCSharpCompletionProviderTests.cs | 2 +- .../SymbolCompletionProviderTests.cs | 1386 ++++++++--------- .../AbstractCompletionProviderTests.cs | 52 +- ...tractVisualBasicCompletionProviderTests.vb | 4 +- 4 files changed, 722 insertions(+), 722 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractCSharpCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractCSharpCompletionProviderTests.cs index 965832f98cb9c..1f5e19957748a 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractCSharpCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractCSharpCompletionProviderTests.cs @@ -64,7 +64,7 @@ private protected override Task BaseVerifyWorkerAsync( private protected override Task BaseVerifyWorkerAsync( string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, - SourceCodeKind sourceCodeKind, CompletionTestExpectedResult[] expectedResults, + SourceCodeKind sourceCodeKind, ItemExpectation[] expectedResults, List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) { return base.VerifyWorkerAsync( diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs index 73f74fbaee2fe..c98b9e085ab6b 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs @@ -255,8 +255,8 @@ public async Task OpenStringLiteral() { var code = AddUsingDirectives("using System;", AddInsideMethod("string s = \"$$")); await VerifyExpectedItemsAsync(code, [ - CompletionTestExpectedResult.Absent("String"), - CompletionTestExpectedResult.Absent("System") + ItemExpectation.Absent("String"), + ItemExpectation.Absent("System") ]); } @@ -266,8 +266,8 @@ public async Task OpenStringLiteralInDirective() var code = "#r \"$$"; await VerifyExpectedItemsAsync( code, [ - CompletionTestExpectedResult.Absent("String"), - CompletionTestExpectedResult.Absent("System") + ItemExpectation.Absent("String"), + ItemExpectation.Absent("System") ], sourceCodeKind: SourceCodeKind.Script); } @@ -277,8 +277,8 @@ public async Task StringLiteral() { var code = AddUsingDirectives("using System;", AddInsideMethod("string s = \"$$\";")); await VerifyExpectedItemsAsync(code, [ - CompletionTestExpectedResult.Absent("String"), - CompletionTestExpectedResult.Absent("System") + ItemExpectation.Absent("String"), + ItemExpectation.Absent("System") ]); } @@ -288,8 +288,8 @@ public async Task StringLiteralInDirective() var code = "#r \"$$\""; await VerifyExpectedItemsAsync( code, [ - CompletionTestExpectedResult.Absent("String"), - CompletionTestExpectedResult.Absent("System") + ItemExpectation.Absent("String"), + ItemExpectation.Absent("System") ], sourceCodeKind: SourceCodeKind.Script); } @@ -299,8 +299,8 @@ public async Task OpenCharLiteral() { var code = AddUsingDirectives("using System;", AddInsideMethod("char c = '$$")); await VerifyExpectedItemsAsync(code, [ - CompletionTestExpectedResult.Absent("String"), - CompletionTestExpectedResult.Absent("System") + ItemExpectation.Absent("String"), + ItemExpectation.Absent("System") ]); } @@ -309,8 +309,8 @@ public async Task AssemblyAttribute1() { var code = @"[assembly: $$]"; await VerifyExpectedItemsAsync(code, [ - CompletionTestExpectedResult.Absent("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Absent("String"), + ItemExpectation.Exists("System") ]); } @@ -320,8 +320,8 @@ public async Task AssemblyAttribute2() var code = @"[assembly: $$]"; var source = AddUsingDirectives("using System;", code); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("System"), - CompletionTestExpectedResult.Exists("AttributeUsage") + ItemExpectation.Exists("System"), + ItemExpectation.Exists("AttributeUsage") ]); } @@ -349,8 +349,8 @@ public async Task TypeParamAttribute() { var code = AddUsingDirectives("using System;", @"class CL<[A$$]T> {}"); await VerifyExpectedItemsAsync(code, [ - CompletionTestExpectedResult.Exists("AttributeUsage"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("AttributeUsage"), + ItemExpectation.Exists("System") ]); } @@ -363,8 +363,8 @@ void Method() {} }"; var code = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(code, [ - CompletionTestExpectedResult.Exists("AttributeUsage"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("AttributeUsage"), + ItemExpectation.Exists("System") ]); } @@ -376,8 +376,8 @@ public async Task MethodTypeParamAttribute() }"; var code = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(code, [ - CompletionTestExpectedResult.Exists("AttributeUsage"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("AttributeUsage"), + ItemExpectation.Exists("System") ]); } @@ -389,8 +389,8 @@ void Method ([$$]int i) {} }"; var code = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(code, [ - CompletionTestExpectedResult.Exists("AttributeUsage"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("AttributeUsage"), + ItemExpectation.Exists("System") ]); } @@ -423,8 +423,8 @@ namespace $$"; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("System"), - CompletionTestExpectedResult.Absent("String") + ItemExpectation.Exists("System"), + ItemExpectation.Absent("String") ], sourceCodeKind: SourceCodeKind.Regular); } @@ -438,8 +438,8 @@ namespace $$;"; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("System"), - CompletionTestExpectedResult.Absent("String") + ItemExpectation.Exists("System"), + ItemExpectation.Absent("String") ], sourceCodeKind: SourceCodeKind.Regular); } @@ -480,8 +480,8 @@ namespace $$ await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Absent("A"), - CompletionTestExpectedResult.Exists("B") + ItemExpectation.Absent("A"), + ItemExpectation.Exists("B") ], sourceCodeKind: SourceCodeKind.Regular); } @@ -508,8 +508,8 @@ namespace B { } await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Absent("A"), - CompletionTestExpectedResult.Absent("B") + ItemExpectation.Absent("A"), + ItemExpectation.Absent("B") ], sourceCodeKind: SourceCodeKind.Regular); } @@ -530,8 +530,8 @@ namespace B { } await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Absent("A"), - CompletionTestExpectedResult.Exists("B") + ItemExpectation.Absent("A"), + ItemExpectation.Exists("B") ], sourceCodeKind: SourceCodeKind.Regular); } @@ -543,8 +543,8 @@ public async Task NamespaceName_Unqualified_InnerCompletionPosition() await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("System"), - CompletionTestExpectedResult.Absent("Runtime") + ItemExpectation.Exists("System"), + ItemExpectation.Absent("Runtime") ], sourceCodeKind: SourceCodeKind.Regular); } @@ -575,14 +575,14 @@ await VerifyExpectedItemsAsync( // C3 => A.A.B.C3 // // ...none of which are found by the current algorithm. - CompletionTestExpectedResult.Absent("C1"), - CompletionTestExpectedResult.Absent("C2"), - CompletionTestExpectedResult.Absent("C3"), - CompletionTestExpectedResult.Absent("A"), + ItemExpectation.Absent("C1"), + ItemExpectation.Absent("C2"), + ItemExpectation.Absent("C3"), + ItemExpectation.Absent("A"), // Because of the above, B does end up in the completion list // since A.B.B appears to be a peer of the new declaration - CompletionTestExpectedResult.Exists("B") + ItemExpectation.Exists("B") ], SourceCodeKind.Regular); } @@ -630,9 +630,9 @@ namespace B.$$ await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Absent("A"), - CompletionTestExpectedResult.Absent("B"), - CompletionTestExpectedResult.Exists("C") + ItemExpectation.Absent("A"), + ItemExpectation.Absent("B"), + ItemExpectation.Exists("C") ], sourceCodeKind: SourceCodeKind.Regular); } @@ -649,8 +649,8 @@ namespace B { } await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Absent("A"), - CompletionTestExpectedResult.Absent("B") + ItemExpectation.Absent("A"), + ItemExpectation.Absent("B") ], sourceCodeKind: SourceCodeKind.Regular); } @@ -669,8 +669,8 @@ namespace B { } await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Absent("A"), - CompletionTestExpectedResult.Exists("B") + ItemExpectation.Absent("A"), + ItemExpectation.Exists("B") ], sourceCodeKind: SourceCodeKind.Regular); } @@ -682,8 +682,8 @@ public async Task NamespaceName_Qualified_InnerCompletionPosition() await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("System"), - CompletionTestExpectedResult.Absent("Runtime") + ItemExpectation.Exists("System"), + ItemExpectation.Absent("Runtime") ], sourceCodeKind: SourceCodeKind.Regular); } @@ -707,8 +707,8 @@ namespace System await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Absent("System"), - CompletionTestExpectedResult.Absent("Runtime") + ItemExpectation.Absent("System"), + ItemExpectation.Absent("Runtime") ], sourceCodeKind: SourceCodeKind.Regular); } @@ -733,9 +733,9 @@ namespace A.B.C.D3 { }"; await VerifyExpectedItemsAsync( source, [ - CompletionTestExpectedResult.Absent("A"), - CompletionTestExpectedResult.Absent("B"), - CompletionTestExpectedResult.Absent("C"), + ItemExpectation.Absent("A"), + ItemExpectation.Absent("B"), + ItemExpectation.Absent("C"), // Ideally, all the D* namespaces would be recommended but, because of how the parser // recovers from the missing braces, they end up with the following qualified names... @@ -745,9 +745,9 @@ await VerifyExpectedItemsAsync( // D3 => A.A.B.C.D3 // // ...none of which are found by the current algorithm. - CompletionTestExpectedResult.Absent("D1"), - CompletionTestExpectedResult.Absent("D2"), - CompletionTestExpectedResult.Absent("D3") + ItemExpectation.Absent("D1"), + ItemExpectation.Absent("D2"), + ItemExpectation.Absent("D3") ], SourceCodeKind.Regular); } @@ -757,8 +757,8 @@ public async Task UnderNamespace() { var source = @"namespace NS { $$"; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Absent("String"), - CompletionTestExpectedResult.Absent("System") + ItemExpectation.Absent("String"), + ItemExpectation.Absent("System") ]); } @@ -769,8 +769,8 @@ public async Task OutsideOfType1() class CL {} $$"; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Absent("String"), - CompletionTestExpectedResult.Absent("System") + ItemExpectation.Absent("String"), + ItemExpectation.Absent("System") ]); } @@ -782,8 +782,8 @@ class CL {} $$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Absent("String"), - CompletionTestExpectedResult.Absent("System") + ItemExpectation.Absent("String"), + ItemExpectation.Absent("System") ]); } @@ -799,8 +799,8 @@ public string Name { name = $$"; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("value"), - CompletionTestExpectedResult.Exists("C") + ItemExpectation.Exists("value"), + ItemExpectation.Exists("C") ]); } @@ -809,8 +809,8 @@ public async Task AfterDot() { var source = AddUsingDirectives("using System;", @"[assembly: A.$$"); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Absent("String"), - CompletionTestExpectedResult.Absent("System") + ItemExpectation.Absent("String"), + ItemExpectation.Absent("System") ]); } @@ -819,8 +819,8 @@ public async Task UsingAlias() { var source = AddUsingDirectives("using System;", @"using MyType = $$"); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Absent("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Absent("String"), + ItemExpectation.Exists("System") ]); } @@ -832,8 +832,8 @@ public async Task IncompleteMember() "; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -845,8 +845,8 @@ public async Task IncompleteMemberAccessibility() "; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -855,8 +855,8 @@ public async Task BadStatement() { var source = AddUsingDirectives("using System;", AddInsideMethod(@"var t = $$)c")); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -865,8 +865,8 @@ public async Task TypeTypeParameter() { var source = AddUsingDirectives("using System;", @"class CL<$$"); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Absent("String"), - CompletionTestExpectedResult.Absent("System") + ItemExpectation.Absent("String"), + ItemExpectation.Absent("System") ]); } @@ -875,8 +875,8 @@ public async Task TypeTypeParameterList() { var source = AddUsingDirectives("using System;", @"class CL10;$$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1384,8 +1384,8 @@ public async Task DoStatementConditionPart() var content = @"do {} while($$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1395,8 +1395,8 @@ public async Task WhileStatementConditionPart() var content = @"while($$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1406,8 +1406,8 @@ public async Task ArrayRankSpecifierSizesPart() var content = @"int [$$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1417,8 +1417,8 @@ public async Task PrefixUnaryExpression() var content = @"+$$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1428,8 +1428,8 @@ public async Task PostfixUnaryExpression() var content = @"$$++"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1439,8 +1439,8 @@ public async Task BinaryExpressionLeftPart() var content = @"$$ + 1"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1450,8 +1450,8 @@ public async Task BinaryExpressionRightPart() var content = @"1 + $$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1461,8 +1461,8 @@ public async Task AssignmentExpressionLeftPart() var content = @"$$ = 1"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1472,8 +1472,8 @@ public async Task AssignmentExpressionRightPart() var content = @"1 = $$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1483,8 +1483,8 @@ public async Task ConditionalExpressionConditionPart() var content = @"$$? 1:"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1494,8 +1494,8 @@ public async Task ConditionalExpressionWhenTruePart() var content = @"true? $$:"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1505,8 +1505,8 @@ public async Task ConditionalExpressionWhenFalsePart() var content = @"true? 1:$$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1516,8 +1516,8 @@ public async Task JoinClauseInExpressionPart() var content = @"var t = from c in C join p in $$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1527,8 +1527,8 @@ public async Task JoinClauseLeftExpressionPart() var content = @"var t = from c in C join p in P on $$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1538,8 +1538,8 @@ public async Task JoinClauseRightExpressionPart() var content = @"var t = from c in C join p in P on id equals $$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1549,8 +1549,8 @@ public async Task WhereClauseConditionPart() var content = @"var t = from c in C where $$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1560,8 +1560,8 @@ public async Task GroupClauseGroupExpressionPart() var content = @"var t = from c in C group $$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1571,8 +1571,8 @@ public async Task GroupClauseByExpressionPart() var content = @"var t = from c in C group g by $$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1582,8 +1582,8 @@ public async Task IfStatement() var content = @"if ($$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1593,8 +1593,8 @@ public async Task SwitchStatement() var content = @"switch($$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1604,8 +1604,8 @@ public async Task SwitchLabelCase() var content = @"switch(i) { case $$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1615,8 +1615,8 @@ public async Task SwitchPatternLabelCase() var content = @"switch(i) { case $$ when"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1626,8 +1626,8 @@ public async Task SwitchExpressionFirstBranch() var content = @"i switch { $$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1637,8 +1637,8 @@ public async Task SwitchExpressionSecondBranch() var content = @"i switch { 1 => true, $$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1648,8 +1648,8 @@ public async Task PositionalPatternFirstPosition() var content = @"i is ($$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1659,8 +1659,8 @@ public async Task PositionalPatternSecondPosition() var content = @"i is (1, $$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1670,8 +1670,8 @@ public async Task PropertyPatternFirstPosition() var content = @"i is { P: $$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1681,8 +1681,8 @@ public async Task PropertyPatternSecondPosition() var content = @"i is { P1: 1, P2: $$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1692,8 +1692,8 @@ public async Task InitializerExpression() var content = @"var t = new [] { $$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1745,8 +1745,8 @@ public async Task TypeParameterConstraintClause_StillShowStaticAndSealedTypesNot var content = @"class CL where T : IList<$$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1798,8 +1798,8 @@ public async Task TypeParameterConstraintClauseList_StillShowStaticAndSealedType var content = @"class CL where T : A, IList<$$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1809,8 +1809,8 @@ public async Task TypeParameterConstraintClauseAnotherWhere() var content = @"class CL where T : A where$$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Absent("String"), - CompletionTestExpectedResult.Absent("System") + ItemExpectation.Absent("String"), + ItemExpectation.Absent("System") ]); } @@ -1842,8 +1842,8 @@ public async Task BaseList1() var content = @"class CL : $$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1853,8 +1853,8 @@ public async Task BaseList2() var content = @"class CL : B, $$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1864,8 +1864,8 @@ public async Task BaseListWhere() var content = @"class CL : B where$$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Absent("String"), - CompletionTestExpectedResult.Absent("System") + ItemExpectation.Absent("String"), + ItemExpectation.Absent("System") ]); } @@ -1875,8 +1875,8 @@ public async Task AliasedName() var content = @"global::$$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Absent("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Absent("String"), + ItemExpectation.Exists("System") ]); } @@ -1894,8 +1894,8 @@ public async Task ConstructorInitializer() var content = @"class C { C() : $$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Absent("String"), - CompletionTestExpectedResult.Absent("System") + ItemExpectation.Absent("String"), + ItemExpectation.Absent("System") ]); } @@ -1905,8 +1905,8 @@ public async Task Typeof1() var content = @"typeof($$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1920,8 +1920,8 @@ public async Task Sizeof1() var content = @"sizeof($$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -1935,8 +1935,8 @@ public async Task Default1() var content = @"default($$"; var source = AddUsingDirectives("using System;", content); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("System") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") ]); } @@ -2167,8 +2167,8 @@ void Bar() } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Absent("Hidden"), - CompletionTestExpectedResult.Exists("Goo") + ItemExpectation.Absent("Hidden"), + ItemExpectation.Exists("Goo") ]); } @@ -2190,8 +2190,8 @@ void Bar() } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Absent("Hidden"), - CompletionTestExpectedResult.Exists("Goo") + ItemExpectation.Absent("Hidden"), + ItemExpectation.Exists("Goo") ]); } @@ -2213,9 +2213,9 @@ void Bar() } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Absent("Hidden"), - CompletionTestExpectedResult.Exists("Goo"), - CompletionTestExpectedResult.Absent("Bar"), + ItemExpectation.Absent("Hidden"), + ItemExpectation.Exists("Goo"), + ItemExpectation.Absent("Bar"), ]); } @@ -2237,8 +2237,8 @@ void Bar() } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Absent("Hidden"), - CompletionTestExpectedResult.Exists("Goo") + ItemExpectation.Absent("Hidden"), + ItemExpectation.Exists("Goo") ]); } @@ -2260,8 +2260,8 @@ void Bar() } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Absent("Hidden"), - CompletionTestExpectedResult.Exists("Goo") + ItemExpectation.Absent("Hidden"), + ItemExpectation.Exists("Goo") ]); } @@ -2283,8 +2283,8 @@ void Bar() } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Absent("Hidden"), - CompletionTestExpectedResult.Exists("Goo") + ItemExpectation.Absent("Hidden"), + ItemExpectation.Exists("Goo") ]); } @@ -2309,10 +2309,10 @@ void Bar() } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Absent("HiddenStatic"), - CompletionTestExpectedResult.Exists("GooStatic"), - CompletionTestExpectedResult.Absent("HiddenInstance"), - CompletionTestExpectedResult.Exists("GooInstance") + ItemExpectation.Absent("HiddenStatic"), + ItemExpectation.Exists("GooStatic"), + ItemExpectation.Absent("HiddenInstance"), + ItemExpectation.Exists("GooInstance") ]); } @@ -2482,8 +2482,8 @@ void M(String parameter) } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Absent("parameter") + ItemExpectation.Exists("String"), + ItemExpectation.Absent("parameter") ]); } @@ -2501,8 +2501,8 @@ void M(String parameter) } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Absent("parameter") + ItemExpectation.Exists("String"), + ItemExpectation.Absent("parameter") ]); } @@ -2520,8 +2520,8 @@ void M(String parameter) } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Absent("parameter") + ItemExpectation.Exists("String"), + ItemExpectation.Absent("parameter") ]); } @@ -2539,8 +2539,8 @@ void M(ref $$) } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Absent("field") + ItemExpectation.Exists("String"), + ItemExpectation.Absent("field") ]); } @@ -2558,8 +2558,8 @@ void M(out $$) } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Absent("field") + ItemExpectation.Exists("String"), + ItemExpectation.Absent("field") ]); } @@ -2577,8 +2577,8 @@ void M(in $$) } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Absent("field") + ItemExpectation.Exists("String"), + ItemExpectation.Absent("field") ]); } @@ -2596,8 +2596,8 @@ void M(ref String parameter) } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("parameter") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("parameter") ]); } @@ -2615,8 +2615,8 @@ void M(out String parameter) } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("parameter") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("parameter") ]); } @@ -2634,8 +2634,8 @@ void M(in String parameter) } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("parameter") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("parameter") ]); } @@ -2653,8 +2653,8 @@ void M(String parameter) } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Exists("parameter") + ItemExpectation.Exists("String"), + ItemExpectation.Exists("parameter") ]); } @@ -2672,8 +2672,8 @@ void M(String parameter) } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Absent("parameter") + ItemExpectation.Exists("String"), + ItemExpectation.Absent("parameter") ]); } @@ -2691,8 +2691,8 @@ ref readonly $$ } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Absent("parameter") + ItemExpectation.Exists("String"), + ItemExpectation.Absent("parameter") ]); } @@ -2710,8 +2710,8 @@ void M(String parameter) } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Absent("parameter") + ItemExpectation.Exists("String"), + ItemExpectation.Absent("parameter") ]); } @@ -2729,8 +2729,8 @@ void M(String parameter) } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Absent("parameter") + ItemExpectation.Exists("String"), + ItemExpectation.Absent("parameter") ]); } @@ -2748,8 +2748,8 @@ void M(String parameter) } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Absent("parameter") + ItemExpectation.Exists("String"), + ItemExpectation.Absent("parameter") ]); } @@ -2767,8 +2767,8 @@ void M(String parameter) } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Absent("parameter") + ItemExpectation.Exists("String"), + ItemExpectation.Absent("parameter") ]); } @@ -2807,10 +2807,10 @@ void M() } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("ToString"), - CompletionTestExpectedResult.Absent("GetType"), - CompletionTestExpectedResult.Absent("Equals"), - CompletionTestExpectedResult.Absent("GetHashCode") + ItemExpectation.Exists("ToString"), + ItemExpectation.Absent("GetType"), + ItemExpectation.Absent("Equals"), + ItemExpectation.Absent("GetHashCode") ]); } @@ -2829,10 +2829,10 @@ void M() } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("ToString"), - CompletionTestExpectedResult.Exists("GetType"), - CompletionTestExpectedResult.Exists("Equals"), - CompletionTestExpectedResult.Exists("GetHashCode") + ItemExpectation.Exists("ToString"), + ItemExpectation.Exists("GetType"), + ItemExpectation.Exists("Equals"), + ItemExpectation.Exists("GetHashCode") ]); } @@ -2850,8 +2850,8 @@ void M(String parameter) } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Absent("parameter") + ItemExpectation.Exists("String"), + ItemExpectation.Absent("parameter") ]); } @@ -2878,8 +2878,8 @@ void M(String parameter) }} "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Absent("parameter") + ItemExpectation.Exists("String"), + ItemExpectation.Absent("parameter") ]); } @@ -2904,8 +2904,8 @@ void M(String parameter) }} "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Absent("String"), - CompletionTestExpectedResult.Absent("parameter") + ItemExpectation.Absent("String"), + ItemExpectation.Absent("parameter") ]); } @@ -2923,8 +2923,8 @@ async async $$ } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Absent("String"), - CompletionTestExpectedResult.Absent("parameter") + ItemExpectation.Absent("String"), + ItemExpectation.Absent("parameter") ]); } @@ -2949,8 +2949,8 @@ void M(String parameter) }} "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Absent("String"), - CompletionTestExpectedResult.Absent("parameter") + ItemExpectation.Absent("String"), + ItemExpectation.Absent("parameter") ]); } @@ -2966,8 +2966,8 @@ class C } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Absent("field") + ItemExpectation.Exists("String"), + ItemExpectation.Absent("field") ]); } @@ -2983,8 +2983,8 @@ ref readonly $$ } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("String"), - CompletionTestExpectedResult.Absent("field") + ItemExpectation.Exists("String"), + ItemExpectation.Absent("field") ]); } @@ -3002,8 +3002,8 @@ class R } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("Q"), - CompletionTestExpectedResult.Exists("R") + ItemExpectation.Exists("Q"), + ItemExpectation.Exists("R") ]); } @@ -3020,8 +3020,8 @@ class R } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("Q"), - CompletionTestExpectedResult.Exists("R") + ItemExpectation.Exists("Q"), + ItemExpectation.Exists("R") ]); } @@ -3038,8 +3038,8 @@ class R } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("Q"), - CompletionTestExpectedResult.Exists("R") + ItemExpectation.Exists("Q"), + ItemExpectation.Exists("R") ]); } @@ -3091,8 +3091,8 @@ class R } $$"; // At EOF await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("Q"), - CompletionTestExpectedResult.Exists("R") + ItemExpectation.Exists("Q"), + ItemExpectation.Exists("R") ]); } @@ -3106,8 +3106,8 @@ class R { $$"; // At EOF await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("Q"), - CompletionTestExpectedResult.Exists("R") + ItemExpectation.Exists("Q"), + ItemExpectation.Exists("R") ]); } @@ -3239,8 +3239,8 @@ public void M() "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("ToString"), - CompletionTestExpectedResult.Exists("Invoke") + ItemExpectation.Exists("ToString"), + ItemExpectation.Exists("Invoke") ]); } @@ -3366,8 +3366,8 @@ public async Task AttributeName() [$$"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("CLSCompliant"), - CompletionTestExpectedResult.Absent("CLSCompliantAttribute") + ItemExpectation.Exists("CLSCompliant"), + ItemExpectation.Absent("CLSCompliantAttribute") ]); } @@ -3380,8 +3380,8 @@ public async Task AttributeNameAfterSpecifier() "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("CLSCompliant"), - CompletionTestExpectedResult.Absent("CLSCompliantAttribute") + ItemExpectation.Exists("CLSCompliant"), + ItemExpectation.Absent("CLSCompliantAttribute") ]); } @@ -3393,8 +3393,8 @@ public async Task AttributeNameInAttributeList() [CLSCompliant, $$"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("CLSCompliant"), - CompletionTestExpectedResult.Absent("CLSCompliantAttribute") + ItemExpectation.Exists("CLSCompliant"), + ItemExpectation.Absent("CLSCompliantAttribute") ]); } @@ -3407,8 +3407,8 @@ public async Task AttributeNameBeforeClass() class C { }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("CLSCompliant"), - CompletionTestExpectedResult.Absent("CLSCompliantAttribute") + ItemExpectation.Exists("CLSCompliant"), + ItemExpectation.Absent("CLSCompliantAttribute") ]); } @@ -3421,8 +3421,8 @@ public async Task AttributeNameAfterSpecifierBeforeClass() class C { }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("CLSCompliant"), - CompletionTestExpectedResult.Absent("CLSCompliantAttribute") + ItemExpectation.Exists("CLSCompliant"), + ItemExpectation.Absent("CLSCompliantAttribute") ]); } @@ -3435,8 +3435,8 @@ public async Task AttributeNameInAttributeArgumentList() class C { }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("CLSCompliantAttribute"), - CompletionTestExpectedResult.Absent("CLSCompliant") + ItemExpectation.Exists("CLSCompliantAttribute"), + ItemExpectation.Absent("CLSCompliant") ]); } @@ -3448,8 +3448,8 @@ public async Task AttributeNameInsideClass() class C { $$ }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("CLSCompliantAttribute"), - CompletionTestExpectedResult.Absent("CLSCompliant") + ItemExpectation.Exists("CLSCompliantAttribute"), + ItemExpectation.Absent("CLSCompliant") ]); } @@ -3504,8 +3504,8 @@ class MyAttribute : System.Attribute { } class Program { } }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("My"), - CompletionTestExpectedResult.Absent("MyAttribute") + ItemExpectation.Exists("My"), + ItemExpectation.Absent("MyAttribute") ]); } @@ -3523,8 +3523,8 @@ class Program { } } }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("My"), - CompletionTestExpectedResult.Absent("MyAttribute") + ItemExpectation.Exists("My"), + ItemExpectation.Absent("MyAttribute") ]); } @@ -3539,9 +3539,9 @@ class namespaceAttribute : System.Attribute { } class Program { } }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("namespaceAttribute"), - CompletionTestExpectedResult.Absent("namespace"), - CompletionTestExpectedResult.Absent("@namespace") + ItemExpectation.Exists("namespaceAttribute"), + ItemExpectation.Absent("namespace"), + ItemExpectation.Absent("@namespace") ]); } @@ -3556,9 +3556,9 @@ class namespaceAttribute : System.Attribute { } class Program { } }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("namespaceAttribute"), - CompletionTestExpectedResult.Absent("namespace"), - CompletionTestExpectedResult.Absent("@namespace") + ItemExpectation.Exists("namespaceAttribute"), + ItemExpectation.Absent("namespace"), + ItemExpectation.Absent("@namespace") ]); } @@ -3579,16 +3579,16 @@ void M() await VerifyExpectedItemsAsync(markup, [ // preprocessor keyword - CompletionTestExpectedResult.Exists("error"), - CompletionTestExpectedResult.Absent("@error"), + ItemExpectation.Exists("error"), + ItemExpectation.Absent("@error"), // contextual keyword - CompletionTestExpectedResult.Exists("method"), - CompletionTestExpectedResult.Absent("@method"), + ItemExpectation.Exists("method"), + ItemExpectation.Absent("@method"), // full keyword - CompletionTestExpectedResult.Exists("@int"), - CompletionTestExpectedResult.Absent("int") + ItemExpectation.Exists("@int"), + ItemExpectation.Absent("int") ]); } @@ -3606,8 +3606,8 @@ void M() }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("@from"), - CompletionTestExpectedResult.Absent("from") + ItemExpectation.Exists("@from"), + ItemExpectation.Absent("from") ]); } @@ -3627,10 +3627,10 @@ void M() }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("@from"), - CompletionTestExpectedResult.Absent("from"), - CompletionTestExpectedResult.Exists("@where"), - CompletionTestExpectedResult.Absent("where") + ItemExpectation.Exists("@from"), + ItemExpectation.Absent("from"), + ItemExpectation.Exists("@where"), + ItemExpectation.Absent("where") ]); } @@ -3650,10 +3650,10 @@ void M() }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("@from"), - CompletionTestExpectedResult.Absent("from"), - CompletionTestExpectedResult.Exists("@where"), - CompletionTestExpectedResult.Absent("where") + ItemExpectation.Exists("@from"), + ItemExpectation.Absent("from"), + ItemExpectation.Exists("@where"), + ItemExpectation.Absent("where") ]); } @@ -3666,8 +3666,8 @@ class MyAttribute : System.Attribute { } class Program { }"; await VerifyExpectedItemsAsync( markup, [ - CompletionTestExpectedResult.Exists("My"), - CompletionTestExpectedResult.Absent("MyAttribute") + ItemExpectation.Exists("My"), + ItemExpectation.Absent("MyAttribute") ], SourceCodeKind.Regular); } @@ -3681,9 +3681,9 @@ class namespaceAttribute : System.Attribute { } class Program { }"; await VerifyExpectedItemsAsync( markup, [ - CompletionTestExpectedResult.Exists("namespaceAttribute"), - CompletionTestExpectedResult.Absent("namespace"), - CompletionTestExpectedResult.Absent("@namespace") + ItemExpectation.Exists("namespaceAttribute"), + ItemExpectation.Absent("namespace"), + ItemExpectation.Absent("@namespace") ], SourceCodeKind.Regular); } @@ -3714,8 +3714,8 @@ namespace Namespace3.Namespace4 { class CustomAttribute : System.Attribute { } } [Namespace1.$$]"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Absent("Namespace2"), - CompletionTestExpectedResult.Exists("Namespace3"), + ItemExpectation.Absent("Namespace2"), + ItemExpectation.Exists("Namespace3"), ]); } @@ -3764,10 +3764,10 @@ namespace Namespace3.Namespace4 { class CustomAttribute : System.Attribute { } } [$$]"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("Namespace1Alias"), - CompletionTestExpectedResult.Absent("Namespace2Alias"), - CompletionTestExpectedResult.Exists("Namespace3Alias"), - CompletionTestExpectedResult.Exists("Namespace4Alias"), + ItemExpectation.Exists("Namespace1Alias"), + ItemExpectation.Absent("Namespace2Alias"), + ItemExpectation.Exists("Namespace3Alias"), + ItemExpectation.Exists("Namespace4Alias"), ]); } @@ -4235,10 +4235,10 @@ void M() "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("a"), - CompletionTestExpectedResult.Exists("b"), - CompletionTestExpectedResult.Exists("c"), - CompletionTestExpectedResult.Absent("Equals"), + ItemExpectation.Exists("a"), + ItemExpectation.Exists("b"), + ItemExpectation.Exists("c"), + ItemExpectation.Absent("Equals"), ]); } @@ -4258,10 +4258,10 @@ void M() "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Absent("a"), - CompletionTestExpectedResult.Absent("b"), - CompletionTestExpectedResult.Absent("c"), - CompletionTestExpectedResult.Exists("Equals"), + ItemExpectation.Absent("a"), + ItemExpectation.Absent("b"), + ItemExpectation.Absent("c"), + ItemExpectation.Exists("Equals"), ]); } @@ -7576,8 +7576,8 @@ static void Main() } }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("Goo"), - CompletionTestExpectedResult.Exists("Bar"), + ItemExpectation.Exists("Goo"), + ItemExpectation.Exists("Bar"), ]); } @@ -7763,8 +7763,8 @@ public async $$ }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("Task"), - CompletionTestExpectedResult.Absent("Console"), + ItemExpectation.Exists("Task"), + ItemExpectation.Absent("Console"), ]); } @@ -7781,8 +7781,8 @@ public async $$ class Test {}"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("Task"), - CompletionTestExpectedResult.Absent("Test"), + ItemExpectation.Exists("Task"), + ItemExpectation.Absent("Test"), ]); } @@ -7923,8 +7923,8 @@ class Request }"; // Nothing should be found: no awaiter for request. await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Absent("Result"), - CompletionTestExpectedResult.Absent("ReadAsStreamAsync"), + ItemExpectation.Absent("Result"), + ItemExpectation.Absent("ReadAsStreamAsync"), ]); } @@ -7953,8 +7953,8 @@ class Request }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Absent("Result"), - CompletionTestExpectedResult.Exists("ReadAsStreamAsync"), + ItemExpectation.Absent("Result"), + ItemExpectation.Exists("ReadAsStreamAsync"), ]); } @@ -8772,8 +8772,8 @@ public void goo() } }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("AA"), - CompletionTestExpectedResult.Exists("AB"), + ItemExpectation.Exists("AA"), + ItemExpectation.Exists("AB"), ]); } @@ -8797,8 +8797,8 @@ public void goo() } }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("i"), - CompletionTestExpectedResult.Absent("Value"), + ItemExpectation.Exists("i"), + ItemExpectation.Absent("Value"), ]); } @@ -8821,8 +8821,8 @@ public void goo() } }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("i"), - CompletionTestExpectedResult.Absent("Value"), + ItemExpectation.Exists("i"), + ItemExpectation.Absent("Value"), ]); } @@ -8839,8 +8839,8 @@ void M(System.DateTime? dt) } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("Day"), - CompletionTestExpectedResult.Absent("Value"), + ItemExpectation.Exists("Day"), + ItemExpectation.Absent("Value"), ]); } @@ -8857,8 +8857,8 @@ void M(System.DateTime? dt) } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("Value"), - CompletionTestExpectedResult.Absent("Day"), + ItemExpectation.Exists("Value"), + ItemExpectation.Absent("Day"), ]); } @@ -9273,8 +9273,8 @@ void goo() } "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("x"), - CompletionTestExpectedResult.Exists("y"), + ItemExpectation.Exists("x"), + ItemExpectation.Exists("y"), ]); } @@ -9506,9 +9506,9 @@ namespace M { } }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Absent("A"), - CompletionTestExpectedResult.Absent("B"), - CompletionTestExpectedResult.Exists("N"), + ItemExpectation.Absent("A"), + ItemExpectation.Absent("B"), + ItemExpectation.Exists("N"), ]); } @@ -9530,9 +9530,9 @@ namespace M { } }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Absent("C"), - CompletionTestExpectedResult.Absent("D"), - CompletionTestExpectedResult.Exists("M"), + ItemExpectation.Absent("C"), + ItemExpectation.Absent("D"), + ItemExpectation.Exists("M"), ]); } @@ -9554,9 +9554,9 @@ namespace M { } }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("A"), - CompletionTestExpectedResult.Exists("B"), - CompletionTestExpectedResult.Exists("N"), + ItemExpectation.Exists("A"), + ItemExpectation.Exists("B"), + ItemExpectation.Exists("N"), ]); } @@ -9578,9 +9578,9 @@ namespace M { } }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("C"), - CompletionTestExpectedResult.Exists("D"), - CompletionTestExpectedResult.Exists("M"), + ItemExpectation.Exists("C"), + ItemExpectation.Exists("D"), + ItemExpectation.Exists("M"), ]); } @@ -9602,9 +9602,9 @@ namespace M { } }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("A"), - CompletionTestExpectedResult.Exists("B"), - CompletionTestExpectedResult.Exists("N"), + ItemExpectation.Exists("A"), + ItemExpectation.Exists("B"), + ItemExpectation.Exists("N"), ]); } @@ -9626,9 +9626,9 @@ namespace M { } }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("C"), - CompletionTestExpectedResult.Exists("D"), - CompletionTestExpectedResult.Exists("M"), + ItemExpectation.Exists("C"), + ItemExpectation.Exists("D"), + ItemExpectation.Exists("M"), ]); } @@ -9650,9 +9650,9 @@ namespace M { } }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("A"), - CompletionTestExpectedResult.Exists("B"), - CompletionTestExpectedResult.Exists("N"), + ItemExpectation.Exists("A"), + ItemExpectation.Exists("B"), + ItemExpectation.Exists("N"), ]); } @@ -9674,9 +9674,9 @@ namespace M { } }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("A"), - CompletionTestExpectedResult.Absent("B"), - CompletionTestExpectedResult.Exists("N"), + ItemExpectation.Exists("A"), + ItemExpectation.Absent("B"), + ItemExpectation.Exists("N"), ]); } @@ -9698,9 +9698,9 @@ namespace M { } }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("C"), - CompletionTestExpectedResult.Absent("D"), - CompletionTestExpectedResult.Exists("M"), + ItemExpectation.Exists("C"), + ItemExpectation.Absent("D"), + ItemExpectation.Exists("M"), ]); } @@ -9722,9 +9722,9 @@ namespace M { } }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("A"), - CompletionTestExpectedResult.Absent("B"), - CompletionTestExpectedResult.Exists("N"), + ItemExpectation.Exists("A"), + ItemExpectation.Absent("B"), + ItemExpectation.Exists("N"), ]); } @@ -9747,9 +9747,9 @@ namespace M { } }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("C"), - CompletionTestExpectedResult.Exists("I"), - CompletionTestExpectedResult.Exists("M"), + ItemExpectation.Exists("C"), + ItemExpectation.Exists("I"), + ItemExpectation.Exists("M"), ]); } @@ -9772,9 +9772,9 @@ namespace M { } }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("A"), - CompletionTestExpectedResult.Exists("I"), - CompletionTestExpectedResult.Exists("N"), + ItemExpectation.Exists("A"), + ItemExpectation.Exists("I"), + ItemExpectation.Exists("N"), ]); } @@ -9797,9 +9797,9 @@ namespace M { } }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("A"), - CompletionTestExpectedResult.Exists("I"), - CompletionTestExpectedResult.Exists("N"), + ItemExpectation.Exists("A"), + ItemExpectation.Exists("I"), + ItemExpectation.Exists("N"), ]); } @@ -9830,8 +9830,8 @@ void M() "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Absent("Goo"), - CompletionTestExpectedResult.Absent("Bar"), + ItemExpectation.Absent("Goo"), + ItemExpectation.Absent("Bar"), ]); } @@ -9864,8 +9864,8 @@ void M() "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Absent("Goo"), - CompletionTestExpectedResult.Absent("Bar"), + ItemExpectation.Absent("Goo"), + ItemExpectation.Absent("Bar"), ]); } @@ -9899,8 +9899,8 @@ void M() "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("Goo"), - CompletionTestExpectedResult.Exists("Bar"), + ItemExpectation.Exists("Goo"), + ItemExpectation.Exists("Bar"), ]); } @@ -9935,8 +9935,8 @@ void M() "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("Goo"), - CompletionTestExpectedResult.Exists("Bar"), + ItemExpectation.Exists("Goo"), + ItemExpectation.Exists("Bar"), ]); } @@ -9970,8 +9970,8 @@ void M() "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("Goo"), - CompletionTestExpectedResult.Absent("Bar"), + ItemExpectation.Exists("Goo"), + ItemExpectation.Absent("Bar"), ]); } @@ -10005,8 +10005,8 @@ void M() "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Absent("Goo"), - CompletionTestExpectedResult.Exists("Bar"), + ItemExpectation.Absent("Goo"), + ItemExpectation.Exists("Bar"), ]); } @@ -10041,8 +10041,8 @@ void M() "; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("Goo"), - CompletionTestExpectedResult.Exists("Bar"), + ItemExpectation.Exists("Goo"), + ItemExpectation.Exists("Bar"), ]); } @@ -10439,25 +10439,25 @@ void goo() }" + TestResources.NetFX.ValueTuple.tuplelib_cs; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("Alice"), - CompletionTestExpectedResult.Exists("Bob"), - CompletionTestExpectedResult.Exists("CompareTo"), - CompletionTestExpectedResult.Exists("Equals"), - CompletionTestExpectedResult.Exists("GetHashCode"), - CompletionTestExpectedResult.Exists("GetType"), - CompletionTestExpectedResult.Exists("Item2"), - CompletionTestExpectedResult.Exists("ITEM3"), - CompletionTestExpectedResult.Exists("Item4"), - CompletionTestExpectedResult.Exists("Item5"), - CompletionTestExpectedResult.Exists("Item6"), - CompletionTestExpectedResult.Exists("Item7"), - CompletionTestExpectedResult.Exists("Item8"), - CompletionTestExpectedResult.Exists("ToString"), - - CompletionTestExpectedResult.Absent("Item1"), - CompletionTestExpectedResult.Absent("Item9"), - CompletionTestExpectedResult.Absent("Rest"), - CompletionTestExpectedResult.Absent("Item3") + ItemExpectation.Exists("Alice"), + ItemExpectation.Exists("Bob"), + ItemExpectation.Exists("CompareTo"), + ItemExpectation.Exists("Equals"), + ItemExpectation.Exists("GetHashCode"), + ItemExpectation.Exists("GetType"), + ItemExpectation.Exists("Item2"), + ItemExpectation.Exists("ITEM3"), + ItemExpectation.Exists("Item4"), + ItemExpectation.Exists("Item5"), + ItemExpectation.Exists("Item6"), + ItemExpectation.Exists("Item7"), + ItemExpectation.Exists("Item8"), + ItemExpectation.Exists("ToString"), + + ItemExpectation.Absent("Item1"), + ItemExpectation.Absent("Item9"), + ItemExpectation.Absent("Rest"), + ItemExpectation.Absent("Item3") ]); } @@ -11022,9 +11022,9 @@ void M(SomeCollection c) }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Absent("Substring"), - CompletionTestExpectedResult.Exists("A"), - CompletionTestExpectedResult.Exists("B"), + ItemExpectation.Absent("Substring"), + ItemExpectation.Exists("A"), + ItemExpectation.Exists("B"), ]); } @@ -11135,8 +11135,8 @@ class Product1 { public void MyProperty1() { } } class Product2 { public void MyProperty2() { } }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("MyProperty1"), - CompletionTestExpectedResult.Exists("MyProperty2"), + ItemExpectation.Exists("MyProperty1"), + ItemExpectation.Exists("MyProperty2"), ]); } @@ -11164,9 +11164,9 @@ class Product2 { public void MyProperty2() { } } class Product3 { public void MyProperty3() { } }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("MyProperty1"), - CompletionTestExpectedResult.Exists("MyProperty2"), - CompletionTestExpectedResult.Exists("MyProperty3") + ItemExpectation.Exists("MyProperty1"), + ItemExpectation.Exists("MyProperty2"), + ItemExpectation.Exists("MyProperty3") ]); } @@ -11344,11 +11344,11 @@ class Builder }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("Something"), - CompletionTestExpectedResult.Absent("BeginInvoke"), - CompletionTestExpectedResult.Absent("Clone"), - CompletionTestExpectedResult.Absent("Method"), - CompletionTestExpectedResult.Absent("Target") + ItemExpectation.Exists("Something"), + ItemExpectation.Absent("BeginInvoke"), + ItemExpectation.Absent("Clone"), + ItemExpectation.Absent("Method"), + ItemExpectation.Absent("Target") ]); } @@ -11374,18 +11374,18 @@ public void Test() await VerifyExpectedItemsAsync(markup, [ // Guid - CompletionTestExpectedResult.Exists("ToByteArray"), + ItemExpectation.Exists("ToByteArray"), // Uri - CompletionTestExpectedResult.Exists("AbsoluteUri"), - CompletionTestExpectedResult.Exists("Fragment"), - CompletionTestExpectedResult.Exists("Query"), + ItemExpectation.Exists("AbsoluteUri"), + ItemExpectation.Exists("Fragment"), + ItemExpectation.Exists("Query"), // Should not appear for Delegate - CompletionTestExpectedResult.Absent("BeginInvoke"), - CompletionTestExpectedResult.Absent("Clone"), - CompletionTestExpectedResult.Absent("Method"), - CompletionTestExpectedResult.Absent("Target") + ItemExpectation.Absent("BeginInvoke"), + ItemExpectation.Absent("Clone"), + ItemExpectation.Absent("Method"), + ItemExpectation.Absent("Target") ]); } @@ -11410,18 +11410,18 @@ public void Test() }"; await VerifyExpectedItemsAsync(markup, [ // Guid - CompletionTestExpectedResult.Exists("ToByteArray"), + ItemExpectation.Exists("ToByteArray"), // Should not appear for Uri - CompletionTestExpectedResult.Absent("AbsoluteUri"), - CompletionTestExpectedResult.Absent("Fragment"), - CompletionTestExpectedResult.Absent("Query"), + ItemExpectation.Absent("AbsoluteUri"), + ItemExpectation.Absent("Fragment"), + ItemExpectation.Absent("Query"), // Should not appear for Delegate - CompletionTestExpectedResult.Absent("BeginInvoke"), - CompletionTestExpectedResult.Absent("Clone"), - CompletionTestExpectedResult.Absent("Method"), - CompletionTestExpectedResult.Absent("Target") + ItemExpectation.Absent("BeginInvoke"), + ItemExpectation.Absent("Clone"), + ItemExpectation.Absent("Method"), + ItemExpectation.Absent("Target") ]); } @@ -11455,9 +11455,9 @@ class AnotherBuilder }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Absent("AnotherSomething"), - CompletionTestExpectedResult.Absent("FirstOrDefault"), - CompletionTestExpectedResult.Exists("Something") + ItemExpectation.Absent("AnotherSomething"), + ItemExpectation.Absent("FirstOrDefault"), + ItemExpectation.Exists("Something") ]); } @@ -11488,9 +11488,9 @@ class AnotherBuilder }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Absent("Something"), - CompletionTestExpectedResult.Absent("FirstOrDefault"), - CompletionTestExpectedResult.Exists("AnotherSomething") + ItemExpectation.Absent("Something"), + ItemExpectation.Absent("FirstOrDefault"), + ItemExpectation.Exists("AnotherSomething") ]); } @@ -11596,9 +11596,9 @@ public void M(string s) }"; await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("M"), - CompletionTestExpectedResult.Exists("Equals"), - CompletionTestExpectedResult.Absent("DoSomething") with + ItemExpectation.Exists("M"), + ItemExpectation.Exists("Equals"), + ItemExpectation.Absent("DoSomething") with { DisplayTextSuffix = "<>" }, @@ -11880,9 +11880,9 @@ void M(RankedMusicians m) }"; // VerifyItemExistsAsync also tests with the item typed. await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("BillyJoel"), - CompletionTestExpectedResult.Exists("EveryoneElse"), - CompletionTestExpectedResult.Absent("Equals"), + ItemExpectation.Exists("BillyJoel"), + ItemExpectation.Exists("EveryoneElse"), + ItemExpectation.Absent("Equals"), ]); } @@ -11910,9 +11910,9 @@ void M(RankedMusicians m) }"; // VerifyItemExistsAsync also tests with the item typed. await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("BillyJoel"), - CompletionTestExpectedResult.Exists("EveryoneElse"), - CompletionTestExpectedResult.Absent("Equals"), + ItemExpectation.Exists("BillyJoel"), + ItemExpectation.Exists("EveryoneElse"), + ItemExpectation.Absent("Equals"), ]); } @@ -11940,9 +11940,9 @@ void M(C m) }"; // VerifyItemExistsAsync also tests with the item typed. await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("BillyJoel"), - CompletionTestExpectedResult.Exists("EveryoneElse"), - CompletionTestExpectedResult.Absent("Equals"), + ItemExpectation.Exists("BillyJoel"), + ItemExpectation.Exists("EveryoneElse"), + ItemExpectation.Absent("Equals"), ]); } @@ -11989,9 +11989,9 @@ void M(RankedMusicians m) }"; // VerifyItemExistsAsync also tests with the item typed. await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("BillyJoel"), - CompletionTestExpectedResult.Exists("EveryoneElse"), - CompletionTestExpectedResult.Absent("Equals"), + ItemExpectation.Exists("BillyJoel"), + ItemExpectation.Exists("EveryoneElse"), + ItemExpectation.Absent("Equals"), ]); } @@ -12019,9 +12019,9 @@ void M(RankedMusicians m) }"; // VerifyItemExistsAsync also tests with the item typed. await VerifyExpectedItemsAsync(markup, [ - CompletionTestExpectedResult.Exists("BillyJoel"), - CompletionTestExpectedResult.Exists("EveryoneElse"), - CompletionTestExpectedResult.Absent("Equals"), + ItemExpectation.Exists("BillyJoel"), + ItemExpectation.Exists("EveryoneElse"), + ItemExpectation.Absent("Equals"), ]); } @@ -12190,13 +12190,13 @@ void M(T x) where T : I1, I2 } "; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Absent("M0"), + ItemExpectation.Absent("M0"), - CompletionTestExpectedResult.Exists("M1"), - CompletionTestExpectedResult.Exists("M2"), - CompletionTestExpectedResult.Exists("M3"), - CompletionTestExpectedResult.Exists("P1"), - CompletionTestExpectedResult.Exists("E1") + ItemExpectation.Exists("M1"), + ItemExpectation.Exists("M2"), + ItemExpectation.Exists("M3"), + ItemExpectation.Exists("P1"), + ItemExpectation.Exists("E1") ]); } @@ -12220,10 +12220,10 @@ void TestMethod(TestStruct* a) } "; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("X"), - CompletionTestExpectedResult.Exists("Y"), - CompletionTestExpectedResult.Exists("Method"), - CompletionTestExpectedResult.Exists("ToString") + ItemExpectation.Exists("X"), + ItemExpectation.Exists("Y"), + ItemExpectation.Exists("Method"), + ItemExpectation.Exists("ToString") ]); } @@ -12247,10 +12247,10 @@ await a->$$ } "; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("X"), - CompletionTestExpectedResult.Exists("Y"), - CompletionTestExpectedResult.Exists("Method"), - CompletionTestExpectedResult.Exists("ToString") + ItemExpectation.Exists("X"), + ItemExpectation.Exists("Y"), + ItemExpectation.Exists("Method"), + ItemExpectation.Exists("ToString") ]); } @@ -12276,10 +12276,10 @@ TestLambda TestMethod() } "; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("X"), - CompletionTestExpectedResult.Exists("Y"), - CompletionTestExpectedResult.Exists("Method"), - CompletionTestExpectedResult.Exists("ToString") + ItemExpectation.Exists("X"), + ItemExpectation.Exists("Y"), + ItemExpectation.Exists("Method"), + ItemExpectation.Exists("ToString") ]); } @@ -12316,8 +12316,8 @@ void TestMethod() } "; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("X"), - CompletionTestExpectedResult.Exists("Y") + ItemExpectation.Exists("X"), + ItemExpectation.Exists("Y") ]); } @@ -12354,8 +12354,8 @@ void TestMethod() } "; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("X"), - CompletionTestExpectedResult.Absent("Y") + ItemExpectation.Exists("X"), + ItemExpectation.Absent("Y") ]); } @@ -12414,8 +12414,8 @@ void TestMethod() } "; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Absent("X"), - CompletionTestExpectedResult.Absent("Y") + ItemExpectation.Absent("X"), + ItemExpectation.Absent("Y") ]); } @@ -12452,8 +12452,8 @@ void TestMethod() } "; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Absent("X"), - CompletionTestExpectedResult.Absent("Y") + ItemExpectation.Absent("X"), + ItemExpectation.Absent("Y") ]); } @@ -12770,10 +12770,10 @@ public async Task EnumBaseList1(string underlyingType) var source = "enum E : $$"; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("System"), + ItemExpectation.Exists("System"), // Not accessible in the given context - CompletionTestExpectedResult.Absent(underlyingType), + ItemExpectation.Absent(underlyingType), ]); } @@ -12805,14 +12805,14 @@ enum E : $$ """; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("System"), + ItemExpectation.Exists("System"), - CompletionTestExpectedResult.Exists(underlyingType), + ItemExpectation.Exists(underlyingType), // Verify that other things from `System` namespace are not present - CompletionTestExpectedResult.Absent("Console"), - CompletionTestExpectedResult.Absent("Action"), - CompletionTestExpectedResult.Absent("DateTime") + ItemExpectation.Absent("Console"), + ItemExpectation.Absent("Action"), + ItemExpectation.Absent("DateTime") ]); } @@ -12828,13 +12828,13 @@ enum E : global::$$ """; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Absent("E"), + ItemExpectation.Absent("E"), - CompletionTestExpectedResult.Exists("System"), - CompletionTestExpectedResult.Absent("MyNamespace"), + ItemExpectation.Exists("System"), + ItemExpectation.Absent("MyNamespace"), // Not accessible in the given context - CompletionTestExpectedResult.Absent(underlyingType) + ItemExpectation.Absent(underlyingType) ]); } @@ -12844,14 +12844,14 @@ public async Task EnumBaseList5(string underlyingType) var source = "enum E : System.$$"; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Absent("System"), + ItemExpectation.Absent("System"), - CompletionTestExpectedResult.Exists(underlyingType), + ItemExpectation.Exists(underlyingType), // Verify that other things from `System` namespace are not present - CompletionTestExpectedResult.Absent("Console"), - CompletionTestExpectedResult.Absent("Action"), - CompletionTestExpectedResult.Absent("DateTime") + ItemExpectation.Absent("Console"), + ItemExpectation.Absent("Action"), + ItemExpectation.Absent("DateTime") ]); } @@ -12861,14 +12861,14 @@ public async Task EnumBaseList6(string underlyingType) var source = "enum E : global::System.$$"; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Absent("System"), + ItemExpectation.Absent("System"), - CompletionTestExpectedResult.Exists(underlyingType), + ItemExpectation.Exists(underlyingType), // Verify that other things from `System` namespace are not present - CompletionTestExpectedResult.Absent("Console"), - CompletionTestExpectedResult.Absent("Action"), - CompletionTestExpectedResult.Absent("DateTime") + ItemExpectation.Absent("Console"), + ItemExpectation.Absent("Action"), + ItemExpectation.Absent("DateTime") ]); } @@ -12937,15 +12937,15 @@ enum E : MySystem.$$ """; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Absent("System"), - CompletionTestExpectedResult.Absent("MySystem"), + ItemExpectation.Absent("System"), + ItemExpectation.Absent("MySystem"), - CompletionTestExpectedResult.Exists(underlyingType), + ItemExpectation.Exists(underlyingType), // Verify that other things from `System` namespace are not present - CompletionTestExpectedResult.Absent("Console"), - CompletionTestExpectedResult.Absent("Action"), - CompletionTestExpectedResult.Absent("DateTime") + ItemExpectation.Absent("Console"), + ItemExpectation.Absent("Action"), + ItemExpectation.Absent("DateTime") ]); } @@ -13028,9 +13028,9 @@ void M(string s) """; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("endIndex"), - CompletionTestExpectedResult.Exists("Test"), - CompletionTestExpectedResult.Exists("C"), + ItemExpectation.Exists("endIndex"), + ItemExpectation.Exists("Test"), + ItemExpectation.Exists("C"), ]); } @@ -13051,9 +13051,9 @@ void M(string s) """; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("endIndex"), - CompletionTestExpectedResult.Exists("Test"), - CompletionTestExpectedResult.Exists("C"), + ItemExpectation.Exists("endIndex"), + ItemExpectation.Exists("Test"), + ItemExpectation.Exists("C"), ]); } @@ -13078,10 +13078,10 @@ void M() """; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("foo"), - CompletionTestExpectedResult.Exists("M"), - CompletionTestExpectedResult.Exists("System"), - CompletionTestExpectedResult.Absent("Int32"), + ItemExpectation.Exists("foo"), + ItemExpectation.Exists("M"), + ItemExpectation.Exists("System"), + ItemExpectation.Absent("Int32"), ]); } @@ -13104,9 +13104,9 @@ void A() { } """; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("System"), - CompletionTestExpectedResult.Exists("C"), - CompletionTestExpectedResult.Absent("other"), + ItemExpectation.Exists("System"), + ItemExpectation.Exists("C"), + ItemExpectation.Absent("other"), ]); } @@ -13141,12 +13141,12 @@ public async Task PatternMatching_01(string precedingPattern) var source = WrapPatternMatchingSource(expression); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("A"), - CompletionTestExpectedResult.Exists("B"), - CompletionTestExpectedResult.Exists("C"), - CompletionTestExpectedResult.Absent("D"), - CompletionTestExpectedResult.Absent("M"), - CompletionTestExpectedResult.Exists("R"), + ItemExpectation.Exists("A"), + ItemExpectation.Exists("B"), + ItemExpectation.Exists("C"), + ItemExpectation.Absent("D"), + ItemExpectation.Absent("M"), + ItemExpectation.Exists("R"), ]); } @@ -13158,12 +13158,12 @@ public async Task PatternMatching_02(string precedingPattern) var source = WrapPatternMatchingSource(expression); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("A"), - CompletionTestExpectedResult.Exists("B"), - CompletionTestExpectedResult.Absent("C"), - CompletionTestExpectedResult.Absent("D"), - CompletionTestExpectedResult.Absent("M"), - CompletionTestExpectedResult.Absent("R"), + ItemExpectation.Exists("A"), + ItemExpectation.Exists("B"), + ItemExpectation.Absent("C"), + ItemExpectation.Absent("D"), + ItemExpectation.Absent("M"), + ItemExpectation.Absent("R"), ]); } @@ -13175,9 +13175,9 @@ public async Task PatternMatching_03(string precedingPattern) var source = WrapPatternMatchingSource(expression); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("C"), - CompletionTestExpectedResult.Exists("Constants"), - CompletionTestExpectedResult.Exists("System"), + ItemExpectation.Exists("C"), + ItemExpectation.Exists("Constants"), + ItemExpectation.Exists("System"), ]); } @@ -13191,8 +13191,8 @@ public async Task PatternMatching_04(string precedingPattern) // In scripts, we also get a Script class containing our defined types await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("C"), - CompletionTestExpectedResult.Exists("Constants"), + ItemExpectation.Exists("C"), + ItemExpectation.Exists("Constants"), ], sourceCodeKind: SourceCodeKind.Regular); await VerifyItemExistsAsync(source, "System"); @@ -13205,10 +13205,10 @@ public async Task PatternMatching_05() var source = WrapPatternMatchingSource(expression); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("input"), - CompletionTestExpectedResult.Exists("Constants"), - CompletionTestExpectedResult.Exists("C"), - CompletionTestExpectedResult.Exists("M"), + ItemExpectation.Exists("input"), + ItemExpectation.Exists("Constants"), + ItemExpectation.Exists("C"), + ItemExpectation.Exists("M"), ]); } @@ -13220,12 +13220,12 @@ public async Task PatternMatching_06(string precedingPattern) var source = WrapPatternMatchingSource(expression); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("A"), - CompletionTestExpectedResult.Exists("B"), - CompletionTestExpectedResult.Exists("C"), - CompletionTestExpectedResult.Absent("D"), - CompletionTestExpectedResult.Absent("M"), - CompletionTestExpectedResult.Exists("R"), + ItemExpectation.Exists("A"), + ItemExpectation.Exists("B"), + ItemExpectation.Exists("C"), + ItemExpectation.Absent("D"), + ItemExpectation.Absent("M"), + ItemExpectation.Exists("R"), ]); } @@ -13237,12 +13237,12 @@ public async Task PatternMatching_07(string precedingPattern) var source = WrapPatternMatchingSource(expression); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("A"), - CompletionTestExpectedResult.Exists("B"), - CompletionTestExpectedResult.Exists("C"), - CompletionTestExpectedResult.Exists("D"), - CompletionTestExpectedResult.Absent("M"), - CompletionTestExpectedResult.Absent("R"), + ItemExpectation.Exists("A"), + ItemExpectation.Exists("B"), + ItemExpectation.Exists("C"), + ItemExpectation.Exists("D"), + ItemExpectation.Absent("M"), + ItemExpectation.Absent("R"), ]); } @@ -13254,14 +13254,14 @@ public async Task PatternMatching_08(string precedingPattern) var source = WrapPatternMatchingSource(expression); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("A"), - CompletionTestExpectedResult.Exists("B"), - CompletionTestExpectedResult.Exists("C"), - CompletionTestExpectedResult.Exists("D"), - CompletionTestExpectedResult.Exists("E"), - CompletionTestExpectedResult.Exists("M"), - CompletionTestExpectedResult.Exists("R"), - CompletionTestExpectedResult.Exists("ToString"), + ItemExpectation.Exists("A"), + ItemExpectation.Exists("B"), + ItemExpectation.Exists("C"), + ItemExpectation.Exists("D"), + ItemExpectation.Exists("E"), + ItemExpectation.Exists("M"), + ItemExpectation.Exists("R"), + ItemExpectation.Exists("ToString"), ]); } @@ -13273,11 +13273,11 @@ public async Task PatternMatching_09(string precedingPattern) var source = WrapPatternMatchingSource(expression); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("A"), - CompletionTestExpectedResult.Exists("B"), - CompletionTestExpectedResult.Exists("D"), - CompletionTestExpectedResult.Exists("E"), - CompletionTestExpectedResult.Exists("M"), + ItemExpectation.Exists("A"), + ItemExpectation.Exists("B"), + ItemExpectation.Exists("D"), + ItemExpectation.Exists("E"), + ItemExpectation.Exists("M"), ]); } @@ -13289,13 +13289,13 @@ public async Task PatternMatching_10(string precedingPattern) var source = WrapPatternMatchingSource(expression); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("A"), - CompletionTestExpectedResult.Exists("B"), - CompletionTestExpectedResult.Exists("C"), - CompletionTestExpectedResult.Exists("D"), - CompletionTestExpectedResult.Exists("E"), - CompletionTestExpectedResult.Exists("M"), - CompletionTestExpectedResult.Exists("R"), + ItemExpectation.Exists("A"), + ItemExpectation.Exists("B"), + ItemExpectation.Exists("C"), + ItemExpectation.Exists("D"), + ItemExpectation.Exists("E"), + ItemExpectation.Exists("M"), + ItemExpectation.Exists("R"), ]); } @@ -13307,12 +13307,12 @@ public async Task PatternMatching_11(string precedingPattern) var source = WrapPatternMatchingSource(expression); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("A"), - CompletionTestExpectedResult.Exists("B"), - CompletionTestExpectedResult.Exists("C"), - CompletionTestExpectedResult.Absent("D"), - CompletionTestExpectedResult.Absent("M"), - CompletionTestExpectedResult.Exists("R"), + ItemExpectation.Exists("A"), + ItemExpectation.Exists("B"), + ItemExpectation.Exists("C"), + ItemExpectation.Absent("D"), + ItemExpectation.Absent("M"), + ItemExpectation.Exists("R"), ]); } @@ -13324,12 +13324,12 @@ public async Task PatternMatching_12(string precedingPattern) var source = WrapPatternMatchingSource(expression); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("A"), - CompletionTestExpectedResult.Exists("B"), - CompletionTestExpectedResult.Exists("C"), - CompletionTestExpectedResult.Absent("D"), - CompletionTestExpectedResult.Absent("M"), - CompletionTestExpectedResult.Exists("R"), + ItemExpectation.Exists("A"), + ItemExpectation.Exists("B"), + ItemExpectation.Exists("C"), + ItemExpectation.Absent("D"), + ItemExpectation.Absent("M"), + ItemExpectation.Exists("R"), ]); } @@ -13341,8 +13341,8 @@ public async Task PatternMatching_13(string precedingPattern) var source = WrapPatternMatchingSource(expression); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("Length"), - CompletionTestExpectedResult.Absent("Constants"), + ItemExpectation.Exists("Length"), + ItemExpectation.Absent("Constants"), ]); } @@ -13354,9 +13354,9 @@ public async Task PatternMatching_14(string precedingPattern) var source = WrapPatternMatchingSource(expression); await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("Constants"), - CompletionTestExpectedResult.Absent("InstanceProperty"), - CompletionTestExpectedResult.Absent("P"), + ItemExpectation.Exists("Constants"), + ItemExpectation.Absent("InstanceProperty"), + ItemExpectation.Absent("P"), ]); } @@ -13602,8 +13602,8 @@ public enum Status } """; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("Undisclosed"), - CompletionTestExpectedResult.Absent("ToString"), + ItemExpectation.Exists("Undisclosed"), + ItemExpectation.Absent("ToString"), ]); } @@ -13634,8 +13634,8 @@ public enum Status } """; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("Undisclosed"), - CompletionTestExpectedResult.Absent("ToString"), + ItemExpectation.Exists("Undisclosed"), + ItemExpectation.Absent("ToString"), ]); } @@ -13668,8 +13668,8 @@ public enum StatusEn } """; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("StatusEn"), - CompletionTestExpectedResult.Absent("ToString"), + ItemExpectation.Exists("StatusEn"), + ItemExpectation.Absent("ToString"), ]); } @@ -13702,8 +13702,8 @@ public enum StatusEn } """; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("Undisclosed"), - CompletionTestExpectedResult.Absent("ToString"), + ItemExpectation.Exists("Undisclosed"), + ItemExpectation.Absent("ToString"), ]); } @@ -13737,8 +13737,8 @@ public enum StatusEn } """; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("Undisclosed"), - CompletionTestExpectedResult.Absent("ToString"), + ItemExpectation.Exists("Undisclosed"), + ItemExpectation.Absent("ToString"), ]); } @@ -13767,8 +13767,8 @@ public enum Status } """; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("Undisclosed"), - CompletionTestExpectedResult.Absent("ToString"), + ItemExpectation.Exists("Undisclosed"), + ItemExpectation.Absent("ToString"), ]); } @@ -13797,8 +13797,8 @@ public enum Status } """; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("Undisclosed"), - CompletionTestExpectedResult.Absent("ToString"), + ItemExpectation.Exists("Undisclosed"), + ItemExpectation.Absent("ToString"), ]); } @@ -13829,8 +13829,8 @@ public enum StatusEn } """; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("StatusEn"), - CompletionTestExpectedResult.Absent("ToString"), + ItemExpectation.Exists("StatusEn"), + ItemExpectation.Absent("ToString"), ]); } @@ -13861,8 +13861,8 @@ public enum StatusEn } """; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("Undisclosed"), - CompletionTestExpectedResult.Absent("ToString"), + ItemExpectation.Exists("Undisclosed"), + ItemExpectation.Absent("ToString"), ]); } @@ -13894,8 +13894,8 @@ public enum StatusEn } """; await VerifyExpectedItemsAsync(source, [ - CompletionTestExpectedResult.Exists("Undisclosed"), - CompletionTestExpectedResult.Absent("ToString"), + ItemExpectation.Exists("Undisclosed"), + ItemExpectation.Absent("ToString"), ]); } diff --git a/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs b/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs index c9b30ef21e871..3725fdab236d6 100644 --- a/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs +++ b/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs @@ -126,7 +126,7 @@ private protected abstract Task BaseVerifyWorkerAsync( private protected abstract Task BaseVerifyWorkerAsync( string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, - SourceCodeKind sourceCodeKind, CompletionTestExpectedResult[] expectedResults, + SourceCodeKind sourceCodeKind, ItemExpectation[] expectedResults, List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false); internal Task GetCompletionListAsync( @@ -149,7 +149,7 @@ private protected async Task CheckResultsAsync( CompletionItemFlags? flags, CompletionOptions options) { - var expectedResult = new CompletionTestExpectedResult( + var expectedResult = new ItemExpectation( Name: expectedItemOrNull, IsAbsent: checkForAbsence, ExpectedDescription: expectedDescriptionOrNull, @@ -167,7 +167,7 @@ await CheckResultsAsync(document, position, usePreviousCharAsTrigger, deletedCha private protected async Task CheckResultsAsync( Document document, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionModeItem, - CompletionTestExpectedResult[] expectedResults, + ItemExpectation[] expectedResults, List matchingFilters, CompletionItemFlags? flags, CompletionOptions options) @@ -269,7 +269,7 @@ bool Predicate(RoslynCompletion.CompletionItem c) } } - private protected record CompletionTestExpectedResult( + private protected record ItemExpectation( string Name, bool IsAbsent, string ExpectedDescription = null, @@ -281,21 +281,21 @@ private protected record CompletionTestExpectedResult( bool? IsComplexTextEdit = null, SourceCodeKind? SourceCodeKind = null) { - public static CompletionTestExpectedResult[] None = CreateGeneralMatchingArray(true); - public static CompletionTestExpectedResult[] Any = CreateGeneralMatchingArray(false); + public static ItemExpectation[] None = CreateGeneralMatchingArray(true); + public static ItemExpectation[] Any = CreateGeneralMatchingArray(false); - private static CompletionTestExpectedResult[] CreateGeneralMatchingArray(bool absent) + private static ItemExpectation[] CreateGeneralMatchingArray(bool absent) { - return [new CompletionTestExpectedResult(Name: null, IsAbsent: absent)]; + return [new ItemExpectation(Name: null, IsAbsent: absent)]; } - public static CompletionTestExpectedResult Exists(string name) + public static ItemExpectation Exists(string name) { - return new CompletionTestExpectedResult(name, false); + return new ItemExpectation(name, false); } - public static CompletionTestExpectedResult Absent(string name) + public static ItemExpectation Absent(string name) { - return new CompletionTestExpectedResult(name, true); + return new ItemExpectation(name, true); } } @@ -338,7 +338,7 @@ await VerifyWorkerAsync( private async Task VerifyAsync( string markup, SourceCodeKind? sourceCodeKind, char? deletedCharTrigger, bool usePreviousCharAsTrigger, - CompletionTestExpectedResult[] results, bool? hasSuggestionModeItem, + ItemExpectation[] results, bool? hasSuggestionModeItem, List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) { SourceCodeKind[] evaluatedSourceCodeKinds = sourceCodeKind.HasValue ? [sourceCodeKind.Value] : [SourceCodeKind.Regular, SourceCodeKind.Script]; @@ -463,7 +463,7 @@ private protected async Task VerifyAnyItemExistsAsync( bool? hasSuggestionModeItem = null, CompletionOptions options = null) { await VerifyExpectedItemsAsync( - markup, results: CompletionTestExpectedResult.Any, sourceCodeKind, usePreviousCharAsTrigger: usePreviousCharAsTrigger, + markup, results: ItemExpectation.Any, sourceCodeKind, usePreviousCharAsTrigger: usePreviousCharAsTrigger, hasSuggestionModeItem: hasSuggestionModeItem, options: options); } @@ -473,12 +473,12 @@ private protected async Task VerifyNoItemsExistAsync( CompletionOptions options = null) { await VerifyExpectedItemsAsync( - markup, results: CompletionTestExpectedResult.None, sourceCodeKind, usePreviousCharAsTrigger: usePreviousCharAsTrigger, + markup, results: ItemExpectation.None, sourceCodeKind, usePreviousCharAsTrigger: usePreviousCharAsTrigger, hasSuggestionModeItem: hasSuggestionModeItem, options: options); } private protected async Task VerifyExpectedItemsAsync( - string markup, CompletionTestExpectedResult[] results, + string markup, ItemExpectation[] results, SourceCodeKind? sourceCodeKind = null, char? deletedCharTrigger = null, bool usePreviousCharAsTrigger = false, @@ -512,7 +512,7 @@ private protected virtual async Task VerifyWorkerAsync( CompletionOptions options, bool skipSpeculation = false) { - var expectedResult = new CompletionTestExpectedResult( + var expectedResult = new ItemExpectation( Name: expectedItemOrNull, IsAbsent: checkForAbsence, ExpectedDescription: expectedDescriptionOrNull, @@ -538,7 +538,7 @@ await VerifyWorkerCoreAsync( private protected async Task VerifyWorkerCoreAsync( string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionModeItem, SourceCodeKind sourceCodeKind, - CompletionTestExpectedResult[] expectedResults, + ItemExpectation[] expectedResults, List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, @@ -572,7 +572,7 @@ await CheckResultsAsync(document2, position, usePreviousCharAsTrigger, private protected virtual Task VerifyWorkerAsync( string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionModeItem, SourceCodeKind sourceCodeKind, - CompletionTestExpectedResult[] expectedResults, + ItemExpectation[] expectedResults, List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, @@ -1060,7 +1060,7 @@ protected async Task VerifyItemInLinkedFilesAsync(string xmlString, string expec private protected async Task VerifyAtPositionAsync( string code, int position, string insertText, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, SourceCodeKind sourceCodeKind, - CompletionTestExpectedResult[] expectedResults, + ItemExpectation[] expectedResults, List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) { code = code[..position] + insertText + code[position..]; @@ -1073,7 +1073,7 @@ await BaseVerifyWorkerAsync(code, position, usePreviousCharAsTrigger, private protected async Task VerifyAtPositionAsync( string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, - SourceCodeKind sourceCodeKind, CompletionTestExpectedResult[] expectedResults, + SourceCodeKind sourceCodeKind, ItemExpectation[] expectedResults, List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) { await VerifyAtPositionAsync(code, position, string.Empty, usePreviousCharAsTrigger, @@ -1083,7 +1083,7 @@ await VerifyAtPositionAsync(code, position, string.Empty, usePreviousCharAsTrigg private protected async Task VerifyAtPosition_ItemPartiallyWrittenAsync( string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, - SourceCodeKind sourceCodeKind, CompletionTestExpectedResult[] expectedResults, string partialItem, + SourceCodeKind sourceCodeKind, ItemExpectation[] expectedResults, string partialItem, List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) { await VerifyAtPositionAsync(code, position, ItemPartiallyWritten(partialItem), @@ -1143,7 +1143,7 @@ await VerifyAtPositionAsync( private protected async Task VerifyAtEndOfFileAsync( string code, int position, string insertText, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, - SourceCodeKind sourceCodeKind, CompletionTestExpectedResult[] expectedResults, + SourceCodeKind sourceCodeKind, ItemExpectation[] expectedResults, List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) { // only do this if the placeholder was at the end of the text. @@ -1162,7 +1162,7 @@ await BaseVerifyWorkerAsync(code, position, private protected async Task VerifyAtEndOfFileAsync( string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, - SourceCodeKind sourceCodeKind, CompletionTestExpectedResult[] expectedResults, + SourceCodeKind sourceCodeKind, ItemExpectation[] expectedResults, List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) { await VerifyAtEndOfFileAsync(code, position, string.Empty, @@ -1172,7 +1172,7 @@ await VerifyAtEndOfFileAsync(code, position, string.Empty, private protected async Task VerifyAtEndOfFileAsync( string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, - SourceCodeKind sourceCodeKind, CompletionTestExpectedResult[] expectedResults, + SourceCodeKind sourceCodeKind, ItemExpectation[] expectedResults, List matchingFilters, CompletionItemFlags? flags, CompletionOptions options) { await VerifyAtEndOfFileAsync(code, position, string.Empty, @@ -1222,7 +1222,7 @@ await VerifyAtEndOfFileAsync(code, position, string.Empty, usePreviousCharAsTrig private protected async Task VerifyAtEndOfFile_ItemPartiallyWrittenAsync( string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, - SourceCodeKind sourceCodeKind, CompletionTestExpectedResult[] expectedResults, string partialItem, + SourceCodeKind sourceCodeKind, ItemExpectation[] expectedResults, string partialItem, List matchingFilters, CompletionItemFlags? flags, CompletionOptions options) { await VerifyAtEndOfFileAsync( diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AbstractVisualBasicCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AbstractVisualBasicCompletionProviderTests.vb index 3145c09bbed08..e34ac4a375f62 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AbstractVisualBasicCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AbstractVisualBasicCompletionProviderTests.vb @@ -39,7 +39,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet Private Protected Overrides Function BaseVerifyWorkerAsync( code As String, position As Integer, usePreviousCharAsTrigger As Boolean, deletedCharTrigger As Char?, - hasSuggestionItem As Boolean?, sourceCodeKind As SourceCodeKind, expectedResults() As CompletionTestExpectedResult, + hasSuggestionItem As Boolean?, sourceCodeKind As SourceCodeKind, expectedResults() As ItemExpectation, matchingFilters As List(Of CompletionFilter), flags As CompletionItemFlags?, options As CompletionOptions, Optional skipSpeculation As Boolean = False) As Task Return MyBase.VerifyWorkerAsync( code, position, usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionItem, sourceCodeKind, @@ -87,7 +87,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet Private Protected Overrides Async Function VerifyWorkerAsync( code As String, position As Integer, usePreviousCharAsTrigger As Boolean, deletedCharTrigger As Char?, hasSuggestionModeItem As Boolean?, sourceCodeKind As SourceCodeKind, - expectedResults() As CompletionTestExpectedResult, matchingFilters As List(Of CompletionFilter), + expectedResults() As ItemExpectation, matchingFilters As List(Of CompletionFilter), flags As CompletionItemFlags?, options As CompletionOptions, Optional skipSpeculation As Boolean = False) As Task ' Script/interactive support removed for now. From 6c3d8f755c896c795085d1f39a22dc7537823bc7 Mon Sep 17 00:00:00 2001 From: Rekkonnect <8298332+Rekkonnect@users.noreply.github.com> Date: Sat, 19 Oct 2024 12:43:53 +0300 Subject: [PATCH 098/508] Remove unnecessary method --- .../Portable/Compilation/SemanticModel.vb | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb b/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb index 7e83b7f901559..3290a0c168f6d 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb @@ -2919,28 +2919,6 @@ _Default: Friend MustOverride Function GetAwaitExpressionInfoWorker(awaitExpression As AwaitExpressionSyntax, Optional cancellationToken As CancellationToken = Nothing) As AwaitExpressionInfo - ''' - ''' If the given token is within a preprocessing directive, gets the preprocessing symbol info for it. - ''' - ''' Preprocessing symbol syntax token. - Public Shadows Function GetPreprocessingSymbolInfo(token As SyntaxToken) As VisualBasicPreprocessingSymbolInfo - Dim parent = DirectCast(token.Parent, VisualBasicSyntaxNode) - CheckSyntaxNode(parent) - - If parent.Kind() = SyntaxKind.ConstDirectiveTrivia Then - Dim symbolInfo As VisualBasicPreprocessingSymbolInfo = token.SyntaxTree.GetPreprocessingSymbolInfo(token) - - Return RetrieveOrConstructPreprocessingSymbolInfo(symbolInfo, token) - End If - - Dim identifierParent = TryCast(parent, IdentifierNameSyntax) - If identifierParent IsNot Nothing Then - Return GetPreprocessingSymbolInfo(identifierParent) - End If - - Return VisualBasicPreprocessingSymbolInfo.None - End Function - ''' ''' If the given node is within a preprocessing directive, gets the preprocessing symbol info for it. ''' From 1031e703ebc179c258d9349fdca8b2cc5ad68999 Mon Sep 17 00:00:00 2001 From: Rekkonnect <8298332+Rekkonnect@users.noreply.github.com> Date: Sat, 19 Oct 2024 12:51:54 +0300 Subject: [PATCH 099/508] Improve help for preprocessing symbols --- .../CSharpHelpContextService.cs | 17 +++-- .../CSharp/Test/F1Help/F1HelpTests.cs | 67 ++++++++++++++++--- 2 files changed, 69 insertions(+), 15 deletions(-) diff --git a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs index c215fde4fa093..18746e61cb3a6 100644 --- a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs +++ b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs @@ -208,6 +208,11 @@ private bool TryGetTextForSymbol( return true; } + if (symbol is IPreprocessingSymbol) + { + Debug.Fail("We should have handled that in the preprocessor directive."); + } + text = FormatSymbol(symbol); return text != null; } @@ -357,7 +362,13 @@ private static bool TryGetTextForPreProcessor(SyntaxToken token, [NotNullWhen(tr return true; } - if (token.IsKind(SyntaxKind.EndOfDirectiveToken)) + if (directive.IsKind(SyntaxKind.PragmaWarningDirectiveTrivia)) + { + text = token.Text; + return true; + } + + if (token.Kind() is SyntaxKind.IdentifierToken or SyntaxKind.EndOfDirectiveToken) { text = $"#{directive.HashToken.GetNextToken(includeDirectives: true).Text}"; return true; @@ -556,10 +567,6 @@ private static string FormatNamespaceOrTypeSymbol(INamespaceOrTypeSymbol symbol) if (symbol is null) return null; - // Currently consider not finding help for a preprocessing symbol - if (symbol is IPreprocessingSymbol) - return null; - if (symbol is ITypeSymbol or INamespaceSymbol) { return FormatNamespaceOrTypeSymbol((INamespaceOrTypeSymbol)symbol); diff --git a/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs b/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs index ff10035ea1e84..3134613333f3d 100644 --- a/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs +++ b/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs @@ -1634,16 +1634,6 @@ void M() }", "discard"); } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] - public async Task TestNotFound_PreprocessingSymbol() - { - await TestAsync( -@" -#if ANY[||]THING -#endif -", "vs.texteditor"); - } - [Fact] public async Task TestChecked_01() { @@ -1984,6 +1974,16 @@ await TestAsync( ", "#if"); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] + public async Task TestPreprocessorIf3() + { + await TestAsync( +@" +#if ANY[||]THING +#endif +", "#if"); + } + [Fact] public async Task TestPreprocessorEndIf() { @@ -2036,5 +2036,52 @@ await TestAsync( #endif ", "#elif"); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] + public async Task TestPreprocessorElIf2() + { + await TestAsync( +@" +#if ANY +#elif S[||]OME +#endif +", "#elif"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] + public async Task TestPreprocessorPragmaWarning1() + { + await TestAsync( +@" +#pragma warning disable CS[||]0312 +", "CS0312"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] + public async Task TestPreprocessorPragmaWarning2() + { + await TestAsync( +@" +#pragm[||]a warning disable CS0312 +", "#pragma"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] + public async Task TestPreprocessorPragmaWarning3() + { + await TestAsync( +@" +#pragma warni[||]ng disable CS0312 +", "#warning"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] + public async Task TestPreprocessorPragmaWarning4() + { + await TestAsync( +@" +#pragma warning dis[||]able CS0312 +", "#disable"); + } } } From 94b68482c513269cad4863832af3fc813b3a1575 Mon Sep 17 00:00:00 2001 From: Rekkonnect <8298332+Rekkonnect@users.noreply.github.com> Date: Sat, 19 Oct 2024 14:02:26 +0300 Subject: [PATCH 100/508] Test semantic info for pp symbols --- .../Compilation/GetSemanticInfoTests.cs | 81 +++++++++++++++++++ .../Utilities/CSharp/SemanticModelTestBase.cs | 4 +- 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs index 6ad935d3702cc..03607449769d0 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs @@ -5388,6 +5388,87 @@ public void Bug720566_22() Assert.False(symbolInfo.IsDefined, "must not be defined"); } + [Fact, WorkItem(66009, "https://github.com/dotnet/roslyn/issues/66009")] + public void GetPreprocessingSymbolInfoInDirectives_01() + { + string sourceCode = @" +#if false +#else + #define Z //bind +#endif +"; + PreprocessingSymbolInfo symbolInfo = GetPreprocessingSymbolInfoForTest(sourceCode, "Z //bind"); + Assert.Equal("Z", symbolInfo.Symbol.Name); + Assert.True(symbolInfo.IsDefined, "must be defined"); + } + + [Fact, WorkItem(66009, "https://github.com/dotnet/roslyn/issues/66009")] + public void GetPreprocessingSymbolInfoInDirectives_02() + { + string sourceCode = @" +#if true + #pragma warning disable CS0123 //bind +#endif +"; + PreprocessingSymbolInfo symbolInfo = GetPreprocessingSymbolInfoForTest(sourceCode, "CS0123 //bind"); + Assert.Null(symbolInfo.Symbol); + } + + [Fact, WorkItem(66009, "https://github.com/dotnet/roslyn/issues/66009")] + public void GetPreprocessingSymbolInfoInDirectives_03() + { + string sourceCode = @" +#define Y + +#if X //bind +#elif Y //bind +#endif +"; + PreprocessingSymbolInfo symbolInfo = GetPreprocessingSymbolInfoForTest(sourceCode, "X //bind"); + Assert.Equal("X", symbolInfo.Symbol.Name); + Assert.False(symbolInfo.IsDefined, "must not be defined"); + + symbolInfo = GetPreprocessingSymbolInfoForTest(sourceCode, "Y //bind"); + Assert.Equal("Y", symbolInfo.Symbol.Name); + Assert.True(symbolInfo.IsDefined, "must be defined"); + } + + [Fact, WorkItem(66009, "https://github.com/dotnet/roslyn/issues/66009")] + public void GetPreprocessingSymbolInfoInDirectives_04() + { + string sourceCode = @" +#define X +#define Y + +#if X //bind +#elif Y //bind +#endif +"; + PreprocessingSymbolInfo symbolInfo = GetPreprocessingSymbolInfoForTest(sourceCode, "X //bind"); + Assert.Equal("X", symbolInfo.Symbol.Name); + Assert.True(symbolInfo.IsDefined, "must be defined"); + + symbolInfo = GetPreprocessingSymbolInfoForTest(sourceCode, "Y //bind"); + Assert.Equal("Y", symbolInfo.Symbol.Name); + Assert.True(symbolInfo.IsDefined, "must be defined"); + } + + [Fact, WorkItem(66009, "https://github.com/dotnet/roslyn/issues/66009")] + public void GetPreprocessingSymbolInfoInDirectives_05() + { + string sourceCode = @" +#undef X //bind +#define Y //bind +"; + PreprocessingSymbolInfo symbolInfo = GetPreprocessingSymbolInfoForTest(sourceCode, "X //bind"); + Assert.Equal("X", symbolInfo.Symbol.Name); + Assert.False(symbolInfo.IsDefined, "must not be defined"); + + symbolInfo = GetPreprocessingSymbolInfoForTest(sourceCode, "Y //bind"); + Assert.Equal("Y", symbolInfo.Symbol.Name); + Assert.True(symbolInfo.IsDefined, "must be defined"); + } + [WorkItem(835391, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/835391")] [Fact] public void ConstructedErrorTypeValidation() diff --git a/src/Compilers/Test/Utilities/CSharp/SemanticModelTestBase.cs b/src/Compilers/Test/Utilities/CSharp/SemanticModelTestBase.cs index 7faf243c35585..725d13af7bb56 100644 --- a/src/Compilers/Test/Utilities/CSharp/SemanticModelTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/SemanticModelTestBase.cs @@ -138,9 +138,9 @@ internal PreprocessingSymbolInfo GetPreprocessingSymbolInfoForTest(string testSr var tree = compilation.SyntaxTrees[0]; var model = compilation.GetSemanticModel(tree); var position = testSrc.IndexOf(subStrForPreprocessNameIndex, StringComparison.Ordinal); - var nameSyntaxToBind = tree.GetRoot().FindToken(position, findInsideTrivia: true).Parent as IdentifierNameSyntax; + var nodeToBind = tree.GetRoot().FindToken(position, findInsideTrivia: true).Parent; - return model.GetPreprocessingSymbolInfo(nameSyntaxToBind); + return model.GetPreprocessingSymbolInfo(nodeToBind); } internal AliasSymbol GetAliasInfoForTest(string testSrc) From 4fde24ef8e41c20f30197b736b7e90097434d9c4 Mon Sep 17 00:00:00 2001 From: Rekkonnect <8298332+Rekkonnect@users.noreply.github.com> Date: Sat, 19 Oct 2024 14:04:48 +0300 Subject: [PATCH 101/508] Revert style change --- src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor`2.vb | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor`2.vb b/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor`2.vb index b9e7c75dc800a..4f8a6fcb94fd5 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor`2.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/SymbolVisitor`2.vb @@ -94,5 +94,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Public Overridable Function VisitEvent(symbol As EventSymbol, arg As TArgument) As TResult Return DefaultVisit(symbol, arg) End Function + End Class End Namespace From a79133337adabe7f233cac7b75ba34703c8c071d Mon Sep 17 00:00:00 2001 From: Rekkonnect <8298332+Rekkonnect@users.noreply.github.com> Date: Sat, 19 Oct 2024 23:29:39 +0300 Subject: [PATCH 102/508] Rework implementation avoiding compiler changes --- .../Compilation/CSharpSemanticModel.cs | 42 ++-------- .../Compilation/GetSemanticInfoTests.cs | 81 ------------------- .../Utilities/CSharp/SemanticModelTestBase.cs | 4 +- .../Portable/Compilation/SemanticModel.vb | 34 ++------ ...alBasicSyntaxTree.ConditionalSymbolsMap.vb | 9 --- .../Portable/Syntax/VisualBasicSyntaxTree.vb | 6 +- .../Portable/VisualBasicExtensions.vb | 4 +- .../CSharpHelpContextService.cs | 2 +- .../PreprocessingSymbolReferenceFinder.cs | 2 +- .../Extensions/SemanticModelExtensions.cs | 25 ++++-- .../Shared/Extensions/TokenSemanticInfo.cs | 5 +- .../SemanticFacts/CSharpSemanticFacts.cs | 40 ++++++++- .../Services/SemanticFacts/ISemanticFacts.cs | 11 +++ .../SemanticFacts/VisualBasicSemanticFacts.vb | 40 +++++++++ .../AbstractSemanticFactsService.cs | 3 + 15 files changed, 135 insertions(+), 173 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index 6172ee6f2b77b..c3405df8f8be7 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -4842,39 +4842,13 @@ public PreprocessingSymbolInfo GetPreprocessingSymbolInfo(IdentifierNameSyntax n { CheckSyntaxNode(node); - if (node.Ancestors().Any(n => isPreprocessorDirectiveAcceptingPreprocessingSymbols(n.Kind()))) + if (node.Ancestors().Any(n => SyntaxFacts.IsPreprocessorDirective(n.Kind()))) { - return CreatePreprocessingSymbolInfo(node.Identifier); + bool isDefined = this.SyntaxTree.IsPreprocessorSymbolDefined(node.Identifier.ValueText, node.Identifier.SpanStart); + return new PreprocessingSymbolInfo(new Symbols.PublicModel.PreprocessingSymbol(node.Identifier.ValueText), isDefined); } return PreprocessingSymbolInfo.None; - - static bool isPreprocessorDirectiveAcceptingPreprocessingSymbols(SyntaxKind kind) - { - switch (kind) - { - case SyntaxKind.IfDirectiveTrivia: - case SyntaxKind.ElifDirectiveTrivia: - case SyntaxKind.DefineDirectiveTrivia: - case SyntaxKind.UndefDirectiveTrivia: - return true; - default: - return false; - } - } - } - - private PreprocessingSymbolInfo GetPreprocessingSymbolInfo(DirectiveTriviaSyntax node, SyntaxToken name) - { - CheckSyntaxNode(node); - return CreatePreprocessingSymbolInfo(name); - } - - private PreprocessingSymbolInfo CreatePreprocessingSymbolInfo(SyntaxToken identifier) - { - bool isDefined = SyntaxTree.IsPreprocessorSymbolDefined(identifier.ValueText, identifier.SpanStart); - var preprocessingSymbol = new Symbols.PublicModel.PreprocessingSymbol(identifier.ValueText); - return new PreprocessingSymbolInfo(preprocessingSymbol, isDefined); } /// @@ -5087,13 +5061,9 @@ protected sealed override IAliasSymbol GetAliasInfoCore(SyntaxNode node, Cancell protected sealed override PreprocessingSymbolInfo GetPreprocessingSymbolInfoCore(SyntaxNode node) { - return node switch - { - IdentifierNameSyntax nameSyntax => GetPreprocessingSymbolInfo(nameSyntax), - DefineDirectiveTriviaSyntax defineSyntax => GetPreprocessingSymbolInfo(defineSyntax, defineSyntax.Name), - UndefDirectiveTriviaSyntax undefSyntax => GetPreprocessingSymbolInfo(undefSyntax, undefSyntax.Name), - _ => PreprocessingSymbolInfo.None - }; + return node is IdentifierNameSyntax nameSyntax + ? GetPreprocessingSymbolInfo(nameSyntax) + : PreprocessingSymbolInfo.None; } protected sealed override ISymbol GetDeclaredSymbolCore(SyntaxNode node, CancellationToken cancellationToken) diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs index 03607449769d0..6ad935d3702cc 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs @@ -5388,87 +5388,6 @@ public void Bug720566_22() Assert.False(symbolInfo.IsDefined, "must not be defined"); } - [Fact, WorkItem(66009, "https://github.com/dotnet/roslyn/issues/66009")] - public void GetPreprocessingSymbolInfoInDirectives_01() - { - string sourceCode = @" -#if false -#else - #define Z //bind -#endif -"; - PreprocessingSymbolInfo symbolInfo = GetPreprocessingSymbolInfoForTest(sourceCode, "Z //bind"); - Assert.Equal("Z", symbolInfo.Symbol.Name); - Assert.True(symbolInfo.IsDefined, "must be defined"); - } - - [Fact, WorkItem(66009, "https://github.com/dotnet/roslyn/issues/66009")] - public void GetPreprocessingSymbolInfoInDirectives_02() - { - string sourceCode = @" -#if true - #pragma warning disable CS0123 //bind -#endif -"; - PreprocessingSymbolInfo symbolInfo = GetPreprocessingSymbolInfoForTest(sourceCode, "CS0123 //bind"); - Assert.Null(symbolInfo.Symbol); - } - - [Fact, WorkItem(66009, "https://github.com/dotnet/roslyn/issues/66009")] - public void GetPreprocessingSymbolInfoInDirectives_03() - { - string sourceCode = @" -#define Y - -#if X //bind -#elif Y //bind -#endif -"; - PreprocessingSymbolInfo symbolInfo = GetPreprocessingSymbolInfoForTest(sourceCode, "X //bind"); - Assert.Equal("X", symbolInfo.Symbol.Name); - Assert.False(symbolInfo.IsDefined, "must not be defined"); - - symbolInfo = GetPreprocessingSymbolInfoForTest(sourceCode, "Y //bind"); - Assert.Equal("Y", symbolInfo.Symbol.Name); - Assert.True(symbolInfo.IsDefined, "must be defined"); - } - - [Fact, WorkItem(66009, "https://github.com/dotnet/roslyn/issues/66009")] - public void GetPreprocessingSymbolInfoInDirectives_04() - { - string sourceCode = @" -#define X -#define Y - -#if X //bind -#elif Y //bind -#endif -"; - PreprocessingSymbolInfo symbolInfo = GetPreprocessingSymbolInfoForTest(sourceCode, "X //bind"); - Assert.Equal("X", symbolInfo.Symbol.Name); - Assert.True(symbolInfo.IsDefined, "must be defined"); - - symbolInfo = GetPreprocessingSymbolInfoForTest(sourceCode, "Y //bind"); - Assert.Equal("Y", symbolInfo.Symbol.Name); - Assert.True(symbolInfo.IsDefined, "must be defined"); - } - - [Fact, WorkItem(66009, "https://github.com/dotnet/roslyn/issues/66009")] - public void GetPreprocessingSymbolInfoInDirectives_05() - { - string sourceCode = @" -#undef X //bind -#define Y //bind -"; - PreprocessingSymbolInfo symbolInfo = GetPreprocessingSymbolInfoForTest(sourceCode, "X //bind"); - Assert.Equal("X", symbolInfo.Symbol.Name); - Assert.False(symbolInfo.IsDefined, "must not be defined"); - - symbolInfo = GetPreprocessingSymbolInfoForTest(sourceCode, "Y //bind"); - Assert.Equal("Y", symbolInfo.Symbol.Name); - Assert.True(symbolInfo.IsDefined, "must be defined"); - } - [WorkItem(835391, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/835391")] [Fact] public void ConstructedErrorTypeValidation() diff --git a/src/Compilers/Test/Utilities/CSharp/SemanticModelTestBase.cs b/src/Compilers/Test/Utilities/CSharp/SemanticModelTestBase.cs index 725d13af7bb56..7faf243c35585 100644 --- a/src/Compilers/Test/Utilities/CSharp/SemanticModelTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/SemanticModelTestBase.cs @@ -138,9 +138,9 @@ internal PreprocessingSymbolInfo GetPreprocessingSymbolInfoForTest(string testSr var tree = compilation.SyntaxTrees[0]; var model = compilation.GetSemanticModel(tree); var position = testSrc.IndexOf(subStrForPreprocessNameIndex, StringComparison.Ordinal); - var nodeToBind = tree.GetRoot().FindToken(position, findInsideTrivia: true).Parent; + var nameSyntaxToBind = tree.GetRoot().FindToken(position, findInsideTrivia: true).Parent as IdentifierNameSyntax; - return model.GetPreprocessingSymbolInfo(nodeToBind); + return model.GetPreprocessingSymbolInfo(nameSyntaxToBind); } internal AliasSymbol GetAliasInfoForTest(string testSrc) diff --git a/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb b/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb index 3290a0c168f6d..32e09ae72b7f2 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/SemanticModel.vb @@ -2927,32 +2927,17 @@ _Default: CheckSyntaxNode(node) If SyntaxFacts.IsWithinPreprocessorConditionalExpression(node) Then - Dim symbolInfo As VisualBasicPreprocessingSymbolInfo = node.SyntaxTree.GetPreprocessingSymbolInfo(node.Identifier) - Return RetrieveOrConstructPreprocessingSymbolInfo(symbolInfo, node.Identifier) - End If - - Return VisualBasicPreprocessingSymbolInfo.None - End Function - - ''' - ''' Gets the preprocessing symbol info for the preprocessing symbol defined in the #Const directive. - ''' - ''' A #Const directive trivia node. - Public Shadows Function GetPreprocessingSymbolInfo(node As ConstDirectiveTriviaSyntax) As VisualBasicPreprocessingSymbolInfo - CheckSyntaxNode(node) - Dim symbolInfo As VisualBasicPreprocessingSymbolInfo = node.SyntaxTree.GetPreprocessingSymbolInfo(node.Name) - Return RetrieveOrConstructPreprocessingSymbolInfo(symbolInfo, node.Name) - End Function + Dim symbolInfo As VisualBasicPreprocessingSymbolInfo = node.SyntaxTree.GetPreprocessingSymbolInfo(node) - Private Function RetrieveOrConstructPreprocessingSymbolInfo(symbolInfo As VisualBasicPreprocessingSymbolInfo, token As SyntaxToken) As VisualBasicPreprocessingSymbolInfo + If symbolInfo.Symbol IsNot Nothing Then + Debug.Assert(CaseInsensitiveComparison.Equals(symbolInfo.Symbol.Name, node.Identifier.ValueText)) + Return symbolInfo + End If - If symbolInfo.Symbol IsNot Nothing Then - Debug.Assert(CaseInsensitiveComparison.Equals(symbolInfo.Symbol.Name, token.ValueText)) - Return symbolInfo + Return New VisualBasicPreprocessingSymbolInfo(New PreprocessingSymbol(node.Identifier.ValueText), constantValueOpt:=Nothing, isDefined:=False) End If - Return New VisualBasicPreprocessingSymbolInfo(New PreprocessingSymbol(token.ValueText), constantValueOpt:=Nothing, isDefined:=False) - + Return VisualBasicPreprocessingSymbolInfo.None End Function ''' @@ -3221,11 +3206,6 @@ _Default: Return GetPreprocessingSymbolInfo(nameSyntax) End If - Dim constSyntax = TryCast(node, ConstDirectiveTriviaSyntax) - If constSyntax IsNot Nothing Then - Return GetPreprocessingSymbolInfo(constSyntax) - End If - Return Nothing End Function diff --git a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.ConditionalSymbolsMap.vb b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.ConditionalSymbolsMap.vb index d70462d8a934f..a5caea689051e 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.ConditionalSymbolsMap.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.ConditionalSymbolsMap.vb @@ -139,17 +139,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic #End Region - Friend Function GetPreprocessingSymbolInfo(conditionalSymbolName As String, node As SyntaxToken) As VisualBasicPreprocessingSymbolInfo - Dim constValue = GetPreprocessorSymbolValue(conditionalSymbolName, node) - Return GetPreprocessingSymbolInfo(conditionalSymbolName, constValue) - End Function - Friend Function GetPreprocessingSymbolInfo(conditionalSymbolName As String, node As IdentifierNameSyntax) As VisualBasicPreprocessingSymbolInfo Dim constValue As InternalSyntax.CConst = GetPreprocessorSymbolValue(conditionalSymbolName, node) - Return GetPreprocessingSymbolInfo(conditionalSymbolName, constValue) - End Function - - Private Function GetPreprocessingSymbolInfo(conditionalSymbolName As String, constValue As InternalSyntax.CConst) As VisualBasicPreprocessingSymbolInfo If constValue Is Nothing Then Return VisualBasicPreprocessingSymbolInfo.None End If diff --git a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.vb b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.vb index 360125edf55f5..ad16c66e37b07 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxTree.vb @@ -597,11 +597,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return False End Function - Friend Function GetPreprocessingSymbolInfo(token As SyntaxToken) As VisualBasicPreprocessingSymbolInfo - Dim conditionalSymbolName = token.ValueText + Friend Function GetPreprocessingSymbolInfo(identifierNode As IdentifierNameSyntax) As VisualBasicPreprocessingSymbolInfo + Dim conditionalSymbolName As String = identifierNode.Identifier.ValueText Dim conditionalSymbols As ConditionalSymbolsMap = Me.ConditionalSymbols - Return If(conditionalSymbols Is Nothing, VisualBasicPreprocessingSymbolInfo.None, conditionalSymbols.GetPreprocessingSymbolInfo(conditionalSymbolName, token)) + Return If(conditionalSymbols Is Nothing, VisualBasicPreprocessingSymbolInfo.None, conditionalSymbols.GetPreprocessingSymbolInfo(conditionalSymbolName, identifierNode)) End Function #End Region diff --git a/src/Compilers/VisualBasic/Portable/VisualBasicExtensions.vb b/src/Compilers/VisualBasic/Portable/VisualBasicExtensions.vb index 3a01c14107e49..f650e9b00560f 100644 --- a/src/Compilers/VisualBasic/Portable/VisualBasicExtensions.vb +++ b/src/Compilers/VisualBasic/Portable/VisualBasicExtensions.vb @@ -261,9 +261,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function - Friend Function GetPreprocessingSymbolInfo(syntaxTree As SyntaxTree, token As SyntaxToken) As VisualBasicPreprocessingSymbolInfo + Friend Function GetPreprocessingSymbolInfo(syntaxTree As SyntaxTree, identifierNode As IdentifierNameSyntax) As VisualBasicPreprocessingSymbolInfo Dim vbTree = DirectCast(syntaxTree, VisualBasicSyntaxTree) - Return vbTree.GetPreprocessingSymbolInfo(token) + Return vbTree.GetPreprocessingSymbolInfo(identifierNode) End Function diff --git a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs index 18746e61cb3a6..543cb08faf6e8 100644 --- a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs +++ b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs @@ -564,7 +564,7 @@ private static string FormatNamespaceOrTypeSymbol(INamespaceOrTypeSymbol symbol) public override string? FormatSymbol(ISymbol? symbol) { - if (symbol is null) + if (symbol == null) return null; if (symbol is ITypeSymbol or INamespaceSymbol) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs index fcf44e61c5427..dd6bfbbb4b0df 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs @@ -49,7 +49,7 @@ protected override void FindReferencesInDocument( { cancellationToken.ThrowIfCancellationRequested(); - var targetSymbol = state.SemanticModel.GetPreprocessingSymbolInfo(token.GetRequiredParent()).Symbol; + var targetSymbol = state.SemanticFacts.GetPreprocessingSymbol(state.SemanticModel, token.GetRequiredParent()); var matched = SymbolFinder.OriginalSymbolsMatch(state.Solution, symbol, targetSymbol); if (matched) diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs index 0866fcf4877c4..27f36de1e2fdb 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Immutable; +using System.Diagnostics; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.Host; @@ -86,13 +87,15 @@ public static TokenSemanticInfo GetSemanticInfo( ITypeSymbol? type = null; ITypeSymbol? convertedType = null; ISymbol? declaredSymbol = null; + IPreprocessingSymbol? preprocessingSymbol = null; var allSymbols = ImmutableArray.Empty; + var tokenParent = token.Parent; if (token.RawKind == syntaxKinds.UsingKeyword && - (token.Parent?.RawKind == syntaxKinds.UsingStatement || token.Parent?.RawKind == syntaxKinds.LocalDeclarationStatement)) + (tokenParent?.RawKind == syntaxKinds.UsingStatement || tokenParent?.RawKind == syntaxKinds.LocalDeclarationStatement)) { var usingStatement = token.Parent; - declaredSymbol = semanticFacts.TryGetDisposeMethod(semanticModel, token.Parent, cancellationToken); + declaredSymbol = semanticFacts.TryGetDisposeMethod(semanticModel, tokenParent, cancellationToken); } else if (overriddingIdentifier.HasValue) { @@ -104,20 +107,26 @@ public static TokenSemanticInfo GetSemanticInfo( } else { - aliasSymbol = semanticModel.GetAliasInfo(token.Parent!, cancellationToken); + Debug.Assert(tokenParent is not null); + aliasSymbol = semanticModel.GetAliasInfo(tokenParent, cancellationToken); var bindableParent = syntaxFacts.TryGetBindableParent(token); var typeInfo = bindableParent != null ? semanticModel.GetTypeInfo(bindableParent, cancellationToken) : default; type = typeInfo.Type; convertedType = typeInfo.ConvertedType; declaredSymbol = MapSymbol(semanticFacts.GetDeclaredSymbol(semanticModel, token, cancellationToken), type); + preprocessingSymbol = semanticFacts.GetPreprocessingSymbol(semanticModel, tokenParent); - var skipSymbolInfoLookup = declaredSymbol.IsKind(SymbolKind.RangeVariable); - allSymbols = skipSymbolInfoLookup - ? [] - : semanticFacts + if (preprocessingSymbol != null) + { + allSymbols = [preprocessingSymbol]; + } + else if (!declaredSymbol.IsKind(SymbolKind.RangeVariable)) + { + allSymbols = semanticFacts .GetBestOrAllSymbols(semanticModel, bindableParent, token, cancellationToken) .WhereAsArray(s => s != null && !s.Equals(declaredSymbol)) .SelectAsArray(s => MapSymbol(s, type)); + } } // NOTE(cyrusn): This is a workaround to how the semantic model binds and returns @@ -150,6 +159,6 @@ public static TokenSemanticInfo GetSemanticInfo( convertedType = null; } - return new TokenSemanticInfo(declaredSymbol, aliasSymbol, allSymbols, type, convertedType, token.Span); + return new TokenSemanticInfo(declaredSymbol, preprocessingSymbol, aliasSymbol, allSymbols, type, convertedType, token.Span); } } diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/TokenSemanticInfo.cs b/src/Workspaces/Core/Portable/Shared/Extensions/TokenSemanticInfo.cs index 223e26c2860ee..554583066d42a 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/TokenSemanticInfo.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/TokenSemanticInfo.cs @@ -13,6 +13,7 @@ namespace Microsoft.CodeAnalysis.Shared.Extensions; internal readonly struct TokenSemanticInfo( ISymbol declaredSymbol, + IPreprocessingSymbol preprocessingSymbol, IAliasSymbol aliasSymbol, ImmutableArray referencedSymbols, ITypeSymbol type, @@ -20,9 +21,10 @@ internal readonly struct TokenSemanticInfo( TextSpan span) { public static readonly TokenSemanticInfo Empty = new( - null, null, [], null, null, default); + null, null, null, [], null, null, default); public readonly ISymbol DeclaredSymbol = declaredSymbol; + public readonly IPreprocessingSymbol PreprocessingSymbol = preprocessingSymbol; public readonly IAliasSymbol AliasSymbol = aliasSymbol; public readonly ImmutableArray ReferencedSymbols = referencedSymbols; public readonly ITypeSymbol Type = type; @@ -33,6 +35,7 @@ public ImmutableArray GetSymbols(bool includeType) { var result = ArrayBuilder.GetInstance(); result.AddIfNotNull(DeclaredSymbol); + result.AddIfNotNull(PreprocessingSymbol); result.AddIfNotNull(AliasSymbol); result.AddRange(ReferencedSymbols); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs index 622dfa587c3e9..4a2c0f70c3940 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs @@ -306,7 +306,7 @@ static ImmutableArray GetCallingConventionSymbols(SemanticModel model, /// heuristics to provide a better result for tokens that users conceptually think bind to things, but which the /// compiler does not necessarily return results for. /// - private static ImmutableArray GetSymbolInfo(SemanticModel semanticModel, SyntaxNode node, SyntaxToken token, CancellationToken cancellationToken) + private ImmutableArray GetSymbolInfo(SemanticModel semanticModel, SyntaxNode node, SyntaxToken token, CancellationToken cancellationToken) { switch (node) { @@ -372,7 +372,7 @@ private static ImmutableArray GetSymbolInfo(SemanticModel semanticModel } } - var preprocessingSymbol = semanticModel.GetPreprocessingSymbolInfo(node).Symbol; + var preprocessingSymbol = GetPreprocessingSymbol(semanticModel, node); return preprocessingSymbol != null ? ImmutableArray.Create(preprocessingSymbol) : semanticModel.GetSymbolInfo(node, cancellationToken).GetBestOrAllSymbols(); @@ -408,6 +408,42 @@ public bool IsInExpressionTree(SemanticModel semanticModel, SyntaxNode node, [No public string GenerateNameForExpression(SemanticModel semanticModel, SyntaxNode expression, bool capitalize, CancellationToken cancellationToken) => semanticModel.GenerateNameForExpression((ExpressionSyntax)expression, capitalize, cancellationToken); + public IPreprocessingSymbol? GetPreprocessingSymbol(SemanticModel semanticModel, SyntaxNode node) + { + return node switch + { + IdentifierNameSyntax nameSyntax + when IsInPreprocessingSymbolContext(nameSyntax) => CreatePreprocessingSymbol(semanticModel, nameSyntax.Identifier), + DefineDirectiveTriviaSyntax defineSyntax => CreatePreprocessingSymbol(semanticModel, defineSyntax.Name), + UndefDirectiveTriviaSyntax undefSyntax => CreatePreprocessingSymbol(semanticModel, undefSyntax.Name), + _ => null, + }; + } + + private static IPreprocessingSymbol? CreatePreprocessingSymbol(SemanticModel model, SyntaxToken identifier) + { + return model.Compilation.CreatePreprocessingSymbol(identifier.ValueText); + } + + private static bool IsInPreprocessingSymbolContext(SyntaxNode node) + { + if (node.Ancestors().Any(n => IsPreprocessorDirectiveAcceptingPreprocessingSymbols(n.Kind()))) + { + return true; + } + + return false; + + static bool IsPreprocessorDirectiveAcceptingPreprocessingSymbols(SyntaxKind kind) + { + return kind + is SyntaxKind.IfDirectiveTrivia + or SyntaxKind.ElifDirectiveTrivia + or SyntaxKind.DefineDirectiveTrivia + or SyntaxKind.UndefDirectiveTrivia; + } + } + #if !CODE_STYLE public async Task GetInterceptorSymbolAsync(Document document, int position, CancellationToken cancellationToken) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFacts.cs index 1a12537d7fbdc..7f05d5afec83f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFacts.cs @@ -110,6 +110,17 @@ internal partial interface ISemanticFacts string GenerateNameForExpression(SemanticModel semanticModel, SyntaxNode expression, bool capitalize, CancellationToken cancellationToken); + /// + /// Gets the that the given node involves. + /// The node's kind must match any of the following kinds: + /// + /// , + /// , or + /// . + /// + /// + IPreprocessingSymbol? GetPreprocessingSymbol(SemanticModel semanticModel, SyntaxNode node); + #if !CODE_STYLE /// diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb index b411f9946ef16..4a4790a665427 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb @@ -288,6 +288,46 @@ Namespace Microsoft.CodeAnalysis.VisualBasic DirectCast(expression, ExpressionSyntax), capitalize, cancellationToken) End Function + Public Function GetPreprocessingSymbol(model As SemanticModel, node As SyntaxNode) As IPreprocessingSymbol Implements ISemanticFacts.GetPreprocessingSymbol + Dim nameSyntax = TryCast(node, IdentifierNameSyntax) + If nameSyntax IsNot Nothing Then + If IsWithinPreprocessorConditionalExpression(nameSyntax) Then + Return CreatePreprocessingSymbol(model, nameSyntax.Identifier) + End If + End If + + Dim constSyntax = TryCast(node, ConstDirectiveTriviaSyntax) + If constSyntax IsNot Nothing Then + Return CreatePreprocessingSymbol(model, constSyntax.Name) + End If + + Return Nothing + End Function + + Private Shared Function CreatePreprocessingSymbol(model As SemanticModel, token As SyntaxToken) As IPreprocessingSymbol + Return model.Compilation.CreatePreprocessingSymbol(token.ValueText) + End Function + + Friend Shared Function IsWithinPreprocessorConditionalExpression(node As IdentifierNameSyntax) As Boolean + Debug.Assert(node IsNot Nothing) + Dim current As SyntaxNode = node + Dim parent As SyntaxNode = node.Parent + + While parent IsNot Nothing + Select Case parent.Kind() + Case SyntaxKind.IfDirectiveTrivia, SyntaxKind.ElseIfDirectiveTrivia + Return DirectCast(parent, IfDirectiveTriviaSyntax).Condition Is current + Case SyntaxKind.ConstDirectiveTrivia + Return DirectCast(parent, ConstDirectiveTriviaSyntax).Value Is current + Case Else + current = parent + parent = current.Parent + End Select + End While + + Return False + End Function + #If Not CODE_STYLE Then Public Function GetInterceptorSymbolAsync(document As Document, position As Integer, cancellationToken As CancellationToken) As Task(Of ISymbol) Implements ISemanticFacts.GetInterceptorSymbolAsync diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs index ee1453718264f..6bde62c6ead96 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs @@ -254,6 +254,9 @@ public bool IsInExpressionTree(SemanticModel semanticModel, SyntaxNode node, INa public string GenerateNameForExpression(SemanticModel semanticModel, SyntaxNode expression, bool capitalize, CancellationToken cancellationToken) => SemanticFacts.GenerateNameForExpression(semanticModel, expression, capitalize, cancellationToken); + public IPreprocessingSymbol GetPreprocessingSymbol(SemanticModel semanticModel, SyntaxNode node) + => SemanticFacts.GetPreprocessingSymbol(semanticModel, node); + #if !CODE_STYLE public Task GetInterceptorSymbolAsync(Document document, int position, CancellationToken cancellationToken) From 76235f41911534c4817751f914d1ea59a10df684 Mon Sep 17 00:00:00 2001 From: Rekkonnect <8298332+Rekkonnect@users.noreply.github.com> Date: Sun, 20 Oct 2024 00:04:46 +0300 Subject: [PATCH 103/508] Remove handle to pragma warning This is outside the scope of this PR --- .../CSharp/Impl/LanguageService/CSharpHelpContextService.cs | 6 ------ src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs index 543cb08faf6e8..20e4dd353019b 100644 --- a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs +++ b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs @@ -362,12 +362,6 @@ private static bool TryGetTextForPreProcessor(SyntaxToken token, [NotNullWhen(tr return true; } - if (directive.IsKind(SyntaxKind.PragmaWarningDirectiveTrivia)) - { - text = token.Text; - return true; - } - if (token.Kind() is SyntaxKind.IdentifierToken or SyntaxKind.EndOfDirectiveToken) { text = $"#{directive.HashToken.GetNextToken(includeDirectives: true).Text}"; diff --git a/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs b/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs index 3134613333f3d..20734194f9488 100644 --- a/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs +++ b/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs @@ -2054,7 +2054,7 @@ public async Task TestPreprocessorPragmaWarning1() await TestAsync( @" #pragma warning disable CS[||]0312 -", "CS0312"); +", "#pragma"); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] From 15d2fb214e9ce310d6fac8291a6de136d52c1215 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 25 Oct 2024 11:50:57 -0700 Subject: [PATCH 104/508] Recover better when a user uses commas in a for-statement instead of semicolons --- .../CSharp/Portable/Parser/LanguageParser.cs | 85 ++-- .../Syntax/Parsing/ForStatementParsingTest.cs | 377 ++++++++++++++++++ 2 files changed, 435 insertions(+), 27 deletions(-) create mode 100644 src/Compilers/CSharp/Test/Syntax/Parsing/ForStatementParsingTest.cs diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index f3db0e485c25d..753c4243e005b 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -5029,6 +5029,19 @@ private void ParseVariableDeclarators( } else if (this.CurrentToken.Kind == SyntaxKind.CommaToken) { + // If we see `for (int i = 0, j < ...` then we do not want to consume j as the next declarator. + // + // Legal forms here are `for (int i = 0, j; ...` or `for (int i = 0, j = ...` or `for (int i = 0, + // j). Anything else we'll treat as as more likely to be the following + if (flags.HasFlag(VariableFlags.ForStatement)) + { + if (!IsTrueIdentifier(this.PeekToken(1)) || + this.PeekToken(2).Kind is not (SyntaxKind.SemicolonToken or SyntaxKind.EqualsToken or SyntaxKind.CloseParenToken)) + { + break; + } + } + variables.AddSeparator(this.EatToken(SyntaxKind.CommaToken)); variables.Add( this.ParseVariableDeclarator( @@ -5062,9 +5075,11 @@ private PostSkipAction SkipBadVariableListTokens(SeparatedSyntaxListBuilder var saveTerm = _termState; _termState |= TerminatorState.IsEndOfFixedStatement; - var decl = ParseParenthesizedVariableDeclaration(); + var decl = ParseParenthesizedVariableDeclaration(VariableFlags.None); _termState = saveTerm; return _syntaxFactory.FixedStatement( @@ -9159,9 +9174,8 @@ private ForStatementSyntax ParseForStatement(SyntaxList att var saveTerm = _termState; _termState |= TerminatorState.IsEndOfForStatementArgument; - var resetPoint = this.GetResetPoint(); + using var resetPoint = this.GetDisposableResetPoint(resetOnDispose: false); var initializers = default(SeparatedSyntaxList); - var incrementors = default(SeparatedSyntaxList); try { // Here can be either a declaration or an expression statement list. Scan @@ -9180,7 +9194,7 @@ private ForStatementSyntax ParseForStatement(SyntaxList att { this.EatToken(); isDeclaration = ScanType() != ScanTypeFlags.NotType && this.CurrentToken.Kind == SyntaxKind.IdentifierToken; - this.Reset(ref resetPoint); + resetPoint.Reset(); } haveScopedKeyword = isDeclaration; @@ -9195,8 +9209,7 @@ private ForStatementSyntax ParseForStatement(SyntaxList att isDeclaration = !this.IsQueryExpression(mayBeVariableDeclaration: true, mayBeMemberDeclaration: false) && this.ScanType() != ScanTypeFlags.NotType && this.IsTrueIdentifier(); - - this.Reset(ref resetPoint); + resetPoint.Reset(); } if (isDeclaration) @@ -9208,7 +9221,7 @@ private ForStatementSyntax ParseForStatement(SyntaxList att scopedKeyword = EatContextualToken(SyntaxKind.ScopedKeyword); } - decl = ParseParenthesizedVariableDeclaration(); + decl = ParseParenthesizedVariableDeclaration(VariableFlags.ForStatement); var declType = decl.Type; @@ -9228,18 +9241,17 @@ private ForStatementSyntax ParseForStatement(SyntaxList att initializers = this.ParseForStatementExpressionList(ref openParen); } - var semi = this.EatToken(SyntaxKind.SemicolonToken); - ExpressionSyntax condition = null; - if (this.CurrentToken.Kind != SyntaxKind.SemicolonToken) - { - condition = this.ParseExpressionCore(); - } + var semi1 = eatCommaOrSemicolon(); - var semi2 = this.EatToken(SyntaxKind.SemicolonToken); - if (this.CurrentToken.Kind != SyntaxKind.CloseParenToken) - { - incrementors = this.ParseForStatementExpressionList(ref semi2); - } + var condition = this.CurrentToken.Kind is not SyntaxKind.SemicolonToken and not SyntaxKind.CommaToken + ? this.ParseExpressionCore() + : null; + + var semi2 = eatCommaOrSemicolon(); + + var incrementors = this.CurrentToken.Kind != SyntaxKind.CloseParenToken + ? this.ParseForStatementExpressionList(ref semi2) + : default; return _syntaxFactory.ForStatement( attributes, @@ -9247,7 +9259,7 @@ private ForStatementSyntax ParseForStatement(SyntaxList att openParen, decl, initializers, - semi, + semi1, condition, semi2, incrementors, @@ -9257,7 +9269,23 @@ private ForStatementSyntax ParseForStatement(SyntaxList att finally { _termState = saveTerm; - this.Release(ref resetPoint); + } + + SyntaxToken eatCommaOrSemicolon() + { + if (this.CurrentToken.Kind is SyntaxKind.CommaToken) + { + // Still parse out a semicolon so we give an appropriate error that the comma is unexpected, and so + // we have the correct token kind. + var semicolon = this.EatToken(SyntaxKind.SemicolonToken); + + // Now skip past the comma + return AddTrailingSkippedSyntax(semicolon, this.EatToken()); + } + else + { + return this.EatToken(SyntaxKind.SemicolonToken); + } } } @@ -9902,7 +9930,7 @@ private void ParseUsingExpression(ref VariableDeclarationSyntax declaration, ref if (scopedKeyword != null) { - declaration = ParseParenthesizedVariableDeclaration(); + declaration = ParseParenthesizedVariableDeclaration(VariableFlags.None); declaration = declaration.Update(_syntaxFactory.ScopedType(scopedKeyword, declaration.Type), declaration.Variables); return; } @@ -9936,14 +9964,14 @@ private void ParseUsingExpression(ref VariableDeclarationSyntax declaration, ref case SyntaxKind.CommaToken: case SyntaxKind.CloseParenToken: this.Reset(ref resetPoint); - declaration = ParseParenthesizedVariableDeclaration(); + declaration = ParseParenthesizedVariableDeclaration(VariableFlags.None); break; case SyntaxKind.EqualsToken: // Parse it as a decl. If the next token is a : and only one variable was parsed, // convert the whole thing to ?: expression. this.Reset(ref resetPoint); - declaration = ParseParenthesizedVariableDeclaration(); + declaration = ParseParenthesizedVariableDeclaration(VariableFlags.None); // We may have non-nullable types in error scenarios. if (this.CurrentToken.Kind == SyntaxKind.ColonToken && @@ -9964,7 +9992,7 @@ private void ParseUsingExpression(ref VariableDeclarationSyntax declaration, ref else if (IsUsingStatementVariableDeclaration(st)) { this.Reset(ref resetPoint); - declaration = ParseParenthesizedVariableDeclaration(); + declaration = ParseParenthesizedVariableDeclaration(VariableFlags.None); } else { @@ -10055,6 +10083,7 @@ private StatementSyntax ParseLocalDeclarationStatement(SyntaxList - private VariableDeclarationSyntax ParseParenthesizedVariableDeclaration() + private VariableDeclarationSyntax ParseParenthesizedVariableDeclaration(VariableFlags initialFlags) { var variables = _pool.AllocateSeparated(); ParseLocalDeclaration( @@ -10231,6 +10260,7 @@ private VariableDeclarationSyntax ParseParenthesizedVariableDeclaration() stopOnCloseParen: true, attributes: default, mods: default, + initialFlags, out var type, out var localFunction); Debug.Assert(localFunction == null); @@ -10245,12 +10275,13 @@ private void ParseLocalDeclaration( bool stopOnCloseParen, SyntaxList attributes, SyntaxList mods, + VariableFlags initialFlags, out TypeSyntax type, out LocalFunctionStatementSyntax localFunction) { type = allowLocalFunctions ? ParseReturnType() : this.ParseType(); - VariableFlags flags = VariableFlags.LocalOrField; + VariableFlags flags = initialFlags | VariableFlags.LocalOrField; if (mods.Any((int)SyntaxKind.ConstKeyword)) { flags |= VariableFlags.Const; diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ForStatementParsingTest.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ForStatementParsingTest.cs new file mode 100644 index 0000000000000..a2feede844549 --- /dev/null +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ForStatementParsingTest.cs @@ -0,0 +1,377 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Roslyn.Test.Utilities; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Parsing; + +public sealed class ForStatementParsingTest(ITestOutputHelper output) : ParsingTests(output) +{ + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66522")] + public void TestCommaSeparators1() + { + UsingStatement("for (int i = 0, j = 0; i < 10; i++) ;"); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "i"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "j"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "i"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "10"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.PostIncrementExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "i"); + } + N(SyntaxKind.PlusPlusToken); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66522")] + public void TestCommaSeparators2() + { + UsingStatement("for (int i = 0, i < 10; i++) ;", + // (1,15): error CS1002: ; expected + // for (int i = 0, i < 10; i++) ; + Diagnostic(ErrorCode.ERR_SemicolonExpected, ",").WithLocation(1, 15)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "i"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + } + M(SyntaxKind.SemicolonToken); + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "i"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "10"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.PostIncrementExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "i"); + } + N(SyntaxKind.PlusPlusToken); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66522")] + public void TestCommaSeparators3() + { + UsingStatement("for (int i = 0, i < 10, i++) ;", + // (1,15): error CS1002: ; expected + // for (int i = 0, i < 10, i++) ; + Diagnostic(ErrorCode.ERR_SemicolonExpected, ",").WithLocation(1, 15), + // (1,23): error CS1002: ; expected + // for (int i = 0, i < 10, i++) ; + Diagnostic(ErrorCode.ERR_SemicolonExpected, ",").WithLocation(1, 23)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "i"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + } + M(SyntaxKind.SemicolonToken); + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "i"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "10"); + } + } + M(SyntaxKind.SemicolonToken); + N(SyntaxKind.PostIncrementExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "i"); + } + N(SyntaxKind.PlusPlusToken); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66522")] + public void TestCommaSeparators4() + { + UsingStatement("for (int i = 0, i) ;", + // (1,18): error CS1002: ; expected + // for (int i = 0, i) ; + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(1, 18), + // (1,18): error CS1525: Invalid expression term ')' + // for (int i = 0, i) ; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(1, 18), + // (1,18): error CS1002: ; expected + // for (int i = 0, i) ; + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(1, 18)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "i"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "i"); + } + } + M(SyntaxKind.SemicolonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66522")] + public void TestCommaSeparators5() + { + UsingStatement("for (int i = 0,,) ;", + // (1,15): error CS1002: ; expected + // for (int i = 0,,) ; + Diagnostic(ErrorCode.ERR_SemicolonExpected, ",").WithLocation(1, 15), + // (1,16): error CS1002: ; expected + // for (int i = 0,,) ; + Diagnostic(ErrorCode.ERR_SemicolonExpected, ",").WithLocation(1, 16)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "i"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + } + M(SyntaxKind.SemicolonToken); + M(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66522")] + public void TestCommaSeparators6() + { + UsingStatement("for (int i = 0, j; i < 10; i++) ;"); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "i"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "j"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "i"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "10"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.PostIncrementExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "i"); + } + N(SyntaxKind.PlusPlusToken); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } +} From 2f1c5c9421c512c485f9059772e51bb4ff76d4a8 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 25 Oct 2024 11:57:50 -0700 Subject: [PATCH 105/508] Simplify --- .../CSharp/Portable/Parser/LanguageParser.cs | 77 ++++++++++--------- 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 91c4ba39698a0..73b19e8aeb4d7 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -9136,19 +9136,45 @@ private ForStatementSyntax ParseForStatement(SyntaxList att { Debug.Assert(this.CurrentToken.Kind == SyntaxKind.ForKeyword); + var saveTerm = _termState; + _termState |= TerminatorState.IsEndOfForStatementArgument; + var forToken = this.EatToken(SyntaxKind.ForKeyword); var openParen = this.EatToken(SyntaxKind.OpenParenToken); - var saveTerm = _termState; - _termState |= TerminatorState.IsEndOfForStatementArgument; + var (decl, initializers) = eatVariableDeclarationOrInitializers(); - using var resetPoint = this.GetDisposableResetPoint(resetOnDispose: false); - var initializers = default(SeparatedSyntaxList); - try + var firstSemicolonToken = eatCommaOrSemicolon(); + var condition = this.CurrentToken.Kind is not SyntaxKind.SemicolonToken and not SyntaxKind.CommaToken + ? this.ParseExpressionCore() + : null; + var secondSemicolonToken = eatCommaOrSemicolon(); + + var forStatement = _syntaxFactory.ForStatement( + attributes, + forToken, + openParen, + decl, + initializers, + firstSemicolonToken, + condition, + secondSemicolonToken, + incrementors: this.CurrentToken.Kind != SyntaxKind.CloseParenToken + ? this.ParseForStatementExpressionList(ref secondSemicolonToken) + : default, + this.EatToken(SyntaxKind.CloseParenToken), + ParseEmbeddedStatement()); + + _termState = saveTerm; + + return forStatement; + + (VariableDeclarationSyntax variableDeclaration, SeparatedSyntaxList) eatVariableDeclarationOrInitializers() { + using var resetPoint = this.GetDisposableResetPoint(resetOnDispose: false); + // Here can be either a declaration or an expression statement list. Scan // for a declaration first. - VariableDeclarationSyntax decl = null; bool isDeclaration = false; bool haveScopedKeyword = false; @@ -9189,7 +9215,7 @@ private ForStatementSyntax ParseForStatement(SyntaxList att scopedKeyword = EatContextualToken(SyntaxKind.ScopedKeyword); } - decl = ParseParenthesizedVariableDeclaration(VariableFlags.ForStatement); + var decl = ParseParenthesizedVariableDeclaration(VariableFlags.ForStatement); var declType = decl.Type; @@ -9202,41 +9228,18 @@ private ForStatementSyntax ParseForStatement(SyntaxList att { decl = decl.Update(declType, decl.Variables); } + + return (decl, default); } else if (this.CurrentToken.Kind != SyntaxKind.SemicolonToken) { // Not a type followed by an identifier, so it must be an expression list. - initializers = this.ParseForStatementExpressionList(ref openParen); + return (null, this.ParseForStatementExpressionList(ref openParen)); + } + else + { + return default; } - - var semi1 = eatCommaOrSemicolon(); - - var condition = this.CurrentToken.Kind is not SyntaxKind.SemicolonToken and not SyntaxKind.CommaToken - ? this.ParseExpressionCore() - : null; - - var semi2 = eatCommaOrSemicolon(); - - var incrementors = this.CurrentToken.Kind != SyntaxKind.CloseParenToken - ? this.ParseForStatementExpressionList(ref semi2) - : default; - - return _syntaxFactory.ForStatement( - attributes, - forToken, - openParen, - decl, - initializers, - semi1, - condition, - semi2, - incrementors, - this.EatToken(SyntaxKind.CloseParenToken), - ParseEmbeddedStatement()); - } - finally - { - _termState = saveTerm; } SyntaxToken eatCommaOrSemicolon() From d333f07bbe7e767df18ab9498491747ad20541b5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 25 Oct 2024 12:01:42 -0700 Subject: [PATCH 106/508] Simplify --- .../CSharp/Portable/Parser/LanguageParser.cs | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 73b19e8aeb4d7..d137c7f522e2c 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -9141,24 +9141,22 @@ private ForStatementSyntax ParseForStatement(SyntaxList att var forToken = this.EatToken(SyntaxKind.ForKeyword); var openParen = this.EatToken(SyntaxKind.OpenParenToken); + var (variableDeclaration, initializers) = eatVariableDeclarationOrInitializers(); - var (decl, initializers) = eatVariableDeclarationOrInitializers(); - - var firstSemicolonToken = eatCommaOrSemicolon(); - var condition = this.CurrentToken.Kind is not SyntaxKind.SemicolonToken and not SyntaxKind.CommaToken - ? this.ParseExpressionCore() - : null; - var secondSemicolonToken = eatCommaOrSemicolon(); + // Pulled out as we need to track this when parsing incrementors to place skipped tokens. + SyntaxToken secondSemicolonToken; var forStatement = _syntaxFactory.ForStatement( attributes, forToken, openParen, - decl, + variableDeclaration, initializers, - firstSemicolonToken, - condition, - secondSemicolonToken, + firstSemicolonToken: eatCommaOrSemicolon(), + condition: this.CurrentToken.Kind is not SyntaxKind.SemicolonToken and not SyntaxKind.CommaToken + ? this.ParseExpressionCore() + : null, + secondSemicolonToken = eatCommaOrSemicolon(), incrementors: this.CurrentToken.Kind != SyntaxKind.CloseParenToken ? this.ParseForStatementExpressionList(ref secondSemicolonToken) : default, From 9be9f492cd814be11e40ff1c8a3da9c8f42b8d10 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 26 Oct 2024 11:31:59 -0700 Subject: [PATCH 107/508] Fix 'use conditional expression' in top level statements --- ...ConditionalExpressionForAssignmentTests.cs | 45 ++++++++++++++++++- ...lExpressionForAssignmentCodeFixProvider.cs | 2 +- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForAssignmentTests.cs b/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForAssignmentTests.cs index b390f823c05cb..548e764d0a344 100644 --- a/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForAssignmentTests.cs +++ b/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForAssignmentTests.cs @@ -19,7 +19,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseConditionalExpressio CSharpUseConditionalExpressionForAssignmentCodeFixProvider>; [Trait(Traits.Feature, Traits.Features.CodeActionsUseConditionalExpression)] -public partial class UseConditionalExpressionForAssignmentTests +public sealed partial class UseConditionalExpressionForAssignmentTests { private static async Task TestMissingAsync( string testCode, @@ -2102,4 +2102,47 @@ public static void Test(object obj) } """); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71403")] + public async Task TestGlobalStatements() + { + await new VerifyCS.Test + { + TestCode = """ + #nullable enable + + using System; + + object? x = null; + object? y = null; + object? z; + + [|if|] (x != null) + { + z = x; + } + else + { + z = y; + } + + Console.WriteLine($"{x}{y}{z}"); + """, + FixedCode = """ + #nullable enable + + using System; + + object? x = null; + object? y = null; + object? z = x != null ? x : y; + + Console.WriteLine($"{x}{y}{z}"); + """, + LanguageVersion = LanguageVersion.CSharp9, + TestState = { + OutputKind = OutputKind.ConsoleApplication, + } + }.RunAsync(); + } } diff --git a/src/Analyzers/Core/CodeFixes/UseConditionalExpression/ForAssignment/AbstractUseConditionalExpressionForAssignmentCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseConditionalExpression/ForAssignment/AbstractUseConditionalExpressionForAssignmentCodeFixProvider.cs index 0c096ac320694..b26a1934237d3 100644 --- a/src/Analyzers/Core/CodeFixes/UseConditionalExpression/ForAssignment/AbstractUseConditionalExpressionForAssignmentCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseConditionalExpression/ForAssignment/AbstractUseConditionalExpressionForAssignmentCodeFixProvider.cs @@ -60,7 +60,7 @@ protected override async Task FixOneAsync( SyntaxEditor editor, SyntaxFormattingOptions formattingOptions, CancellationToken cancellationToken) { var syntaxFacts = document.GetRequiredLanguageService(); - var ifStatement = diagnostic.AdditionalLocations[0].FindNode(cancellationToken); + var ifStatement = diagnostic.AdditionalLocations[0].FindNode(getInnermostNodeForTie: true, cancellationToken); var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var ifOperation = (IConditionalOperation)semanticModel.GetOperation(ifStatement, cancellationToken)!; From 8da2ec415c697a997d3421e6c2b5ee42d1baade9 Mon Sep 17 00:00:00 2001 From: Rekkonnect <8298332+Rekkonnect@users.noreply.github.com> Date: Sun, 27 Oct 2024 12:59:10 +0200 Subject: [PATCH 108/508] Handle enter in single-line raw strings --- .../RawStringLiteralCommandHandler_Return.cs | 159 +++++++++++++++++- .../RawStringLiteralCommandHandlerTests.cs | 66 ++++++++ 2 files changed, 222 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs index 0c66d57a8fa48..5ee443c2c09aa 100644 --- a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs +++ b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Indentation; @@ -50,12 +51,164 @@ public bool ExecuteCommand(ReturnKeyCommandArgs args, CommandExecutionContext co if (position >= currentSnapshot.Length) return false; - if (currentSnapshot[position] != '"') + if (currentSnapshot[position] == '"') + { + return HandleCaretOnQuote(textView, subjectBuffer, span, position, context.OperationContext.UserCancellationToken); + } + + return HandleCaretNotOnQuote(textView, subjectBuffer, span, position, context.OperationContext.UserCancellationToken); + } + + private bool HandleCaretNotOnQuote(ITextView textView, ITextBuffer subjectBuffer, SnapshotSpan span, int position, CancellationToken cancellationToken) + { + // If the caret is not on a quote, we need to find whether we are within the contents of a single-line raw string literal + // but not inside an interpolation + // If we are inside a raw string literal and the caret is not on top of a quote, it is part of the literal's text + // Here we try to ensure that the literal's closing quotes are properly placed in their own line + // We could reach this point after pressing enter on a single-line raw string + + var currentSnapshot = subjectBuffer.CurrentSnapshot; + var document = currentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); + if (document == null) return false; + var parsedDocument = ParsedDocument.CreateSynchronously(document, cancellationToken); + + var token = parsedDocument.Root.FindToken(position); + switch (token.Kind()) + { + case SyntaxKind.SingleLineRawStringLiteralToken: + break; + + case SyntaxKind.InterpolatedStringTextToken + when token.Parent?.Parent is InterpolatedStringExpressionSyntax interpolated && + interpolated.StringStartToken.Kind() is SyntaxKind.InterpolatedSingleLineRawStringStartToken: + break; + + default: + return false; + } + + var indentationOptions = subjectBuffer.GetIndentationOptions(_editorOptionsService, document.Project.GetFallbackAnalyzerOptions(), document.Project.Services, explicitFormat: false); + var indentation = token.GetPreferredIndentation(parsedDocument, indentationOptions, cancellationToken); + + var newLine = indentationOptions.FormattingOptions.NewLine; + + using var transaction = CaretPreservingEditTransaction.TryCreate( + CSharpEditorResources.Split_raw_string, textView, _undoHistoryRegistry, _editorOperationsFactoryService); + + var edit = subjectBuffer.CreateEdit(); + + // Add a newline at the position of the end literal + var closingStart = GetStartPositionOfClosingDelimiter(token); + var newLineAndIndentation = newLine + indentation; + var insertedLines = 1; + edit.Insert(closingStart, newLineAndIndentation); + // Add a newline at the requested position + edit.Insert(position, newLineAndIndentation); + // Also add a newline at the start of the text, only if there is text before the requested position + var openingEnd = GetEndPositionOfOpeningDelimiter(token); + if (openingEnd != position) + { + insertedLines++; + edit.Insert(openingEnd, newLineAndIndentation); + } + var snapshot = edit.Apply(); + + // move caret: + var lineInNewSnapshot = snapshot.GetLineFromPosition(openingEnd); + var nextLine = snapshot.GetLineFromLineNumber(lineInNewSnapshot.LineNumber + insertedLines); + textView.Caret.MoveTo(new VirtualSnapshotPoint(nextLine, indentation.Length)); + + transaction?.Complete(); + return true; + } + + private static int GetEndPositionOfOpeningDelimiter(SyntaxToken currentStringLiteralToken) + { + switch (currentStringLiteralToken.Kind()) + { + case SyntaxKind.SingleLineRawStringLiteralToken: + case SyntaxKind.MultiLineRawStringLiteralToken: + { + var text = currentStringLiteralToken.Text; + var tokenSpan = currentStringLiteralToken.Span; + var tokenStart = tokenSpan.Start; + var length = tokenSpan.Length; + var index = 0; + while (index < length) + { + var c = text[index]; + if (c != '"') + return tokenStart + index; + index++; + } + + Contract.Fail("This should only be triggered by raw string literals that contain text aside from the double quotes"); + return -1; + } + + case SyntaxKind.InterpolatedStringTextToken: + var tokenParent = currentStringLiteralToken.Parent?.Parent; + if (tokenParent is not InterpolatedStringExpressionSyntax interpolatedStringExpression) + { + Contract.Fail("This token should only be contained in an interpolated string text syntax"); + return -1; + } + + return interpolatedStringExpression.StringStartToken.Span.End; + + default: + Contract.Fail("This should only be triggered on a known raw string literal kind"); + return -1; + } + } + + private static int GetStartPositionOfClosingDelimiter(SyntaxToken currentStringLiteralToken) + { + switch (currentStringLiteralToken.Kind()) + { + case SyntaxKind.SingleLineRawStringLiteralToken: + case SyntaxKind.MultiLineRawStringLiteralToken: + { + var text = currentStringLiteralToken.Text; + var tokenSpan = currentStringLiteralToken.Span; + var tokenStart = tokenSpan.Start; + var index = tokenSpan.Length - 1; + while (index > 0) + { + var c = text[index]; + if (c != '"') + return tokenStart + index + 1; + index--; + } + + Contract.Fail("This should only be triggered by raw string literals that contain text aside from the double quotes"); + return -1; + } + + case SyntaxKind.InterpolatedStringTextToken: + var tokenParent = currentStringLiteralToken.Parent?.Parent; + if (tokenParent is not InterpolatedStringExpressionSyntax interpolatedStringExpression) + { + Contract.Fail("This token should only be contained in an interpolated string text syntax"); + return -1; + } + + return interpolatedStringExpression.StringEndToken.SpanStart; + + default: + Contract.Fail("This should only be triggered on a known raw string literal kind"); + return -1; + } + } + + private bool HandleCaretOnQuote(ITextView textView, ITextBuffer subjectBuffer, SnapshotSpan span, int position, CancellationToken cancellationToken) + { var quotesBefore = 0; var quotesAfter = 0; + var currentSnapshot = subjectBuffer.CurrentSnapshot; for (int i = position, n = currentSnapshot.Length; i < n; i++) { if (currentSnapshot[i] != '"') @@ -78,7 +231,7 @@ public bool ExecuteCommand(ReturnKeyCommandArgs args, CommandExecutionContext co if (quotesAfter < 3) return false; - return SplitRawString(textView, subjectBuffer, span.Start.Position, CancellationToken.None); + return SplitRawString(textView, subjectBuffer, span.Start.Position, cancellationToken); } private bool SplitRawString(ITextView textView, ITextBuffer subjectBuffer, int position, CancellationToken cancellationToken) @@ -104,7 +257,7 @@ SyntaxKind.InterpolatedSingleLineRawStringStartToken or var newLine = indentationOptions.FormattingOptions.NewLine; using var transaction = CaretPreservingEditTransaction.TryCreate( - CSharpEditorResources.Split_string, textView, _undoHistoryRegistry, _editorOperationsFactoryService); + CSharpEditorResources.Split_raw_string, textView, _undoHistoryRegistry, _editorOperationsFactoryService); var edit = subjectBuffer.CreateEdit(); diff --git a/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs b/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs index 20c4c56035d2f..aeae78df6a839 100644 --- a/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs +++ b/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs @@ -319,6 +319,72 @@ public void TestReturnInEmptyFile() testState.SendReturn(handled: false); } + [WpfFact] + public void TestReturnAfterThreeQuotesFollowingText() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = """$$following text"""; + """"); + + testState.SendReturn(handled: true); + testState.AssertCodeIs( + """" + var v = """ + $$following text + """; + """"); + } + + [WpfFact] + public void TestReturnAfterThreeQuotesFollowingText_Interpolated() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = $"""$$following text {0}"""; + """"); + + testState.SendReturn(handled: true); + testState.AssertCodeIs( + """" + var v = $""" + $$following text {0} + """; + """"); + } + + [WpfFact] + public void TestReturnAfterTextInRawStringFollowingText() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = """before text$$following text"""; + """"); + + testState.SendReturn(handled: true); + testState.AssertCodeIs( + """" + var v = """ + before text + $$following text + """; + """"); + } + + [WpfFact] + public void TestReturnAfterTextInRawStringFollowingText_Interpolated() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = $"""before text$$following text {0}"""; + """"); + + testState.SendReturn(handled: true); + testState.AssertCodeIs( + """" + var v = $""" + before text + $$following text {0} + """; + """"); + } + #endregion #region generate initial empty raw string From f3515008a41ea5a9270d3e7cfe0db84bb53c077f Mon Sep 17 00:00:00 2001 From: Rekkonnect <8298332+Rekkonnect@users.noreply.github.com> Date: Sun, 27 Oct 2024 19:28:07 +0200 Subject: [PATCH 109/508] Suggestions + tests --- .../RawStringLiteralCommandHandler_Return.cs | 12 ++++----- .../RawStringLiteralCommandHandlerTests.cs | 27 +++++++++++++++++++ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs index 5ee443c2c09aa..99cb044672162 100644 --- a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs +++ b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs @@ -56,16 +56,16 @@ public bool ExecuteCommand(ReturnKeyCommandArgs args, CommandExecutionContext co return HandleCaretOnQuote(textView, subjectBuffer, span, position, context.OperationContext.UserCancellationToken); } - return HandleCaretNotOnQuote(textView, subjectBuffer, span, position, context.OperationContext.UserCancellationToken); + return HandleCaretNotOnQuote(textView, subjectBuffer, position, context.OperationContext.UserCancellationToken); } - private bool HandleCaretNotOnQuote(ITextView textView, ITextBuffer subjectBuffer, SnapshotSpan span, int position, CancellationToken cancellationToken) + private bool HandleCaretNotOnQuote(ITextView textView, ITextBuffer subjectBuffer, int position, CancellationToken cancellationToken) { // If the caret is not on a quote, we need to find whether we are within the contents of a single-line raw string literal // but not inside an interpolation // If we are inside a raw string literal and the caret is not on top of a quote, it is part of the literal's text // Here we try to ensure that the literal's closing quotes are properly placed in their own line - // We could reach this point after pressing enter on a single-line raw string + // We could reach this point after pressing enter within a single-line raw string var currentSnapshot = subjectBuffer.CurrentSnapshot; var document = currentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); @@ -104,9 +104,9 @@ when token.Parent?.Parent is InterpolatedStringExpressionSyntax interpolated && var newLineAndIndentation = newLine + indentation; var insertedLines = 1; edit.Insert(closingStart, newLineAndIndentation); - // Add a newline at the requested position + // Add a newline at the caret's position edit.Insert(position, newLineAndIndentation); - // Also add a newline at the start of the text, only if there is text before the requested position + // Also add a newline at the start of the text, only if there is text before the caret's position var openingEnd = GetEndPositionOfOpeningDelimiter(token); if (openingEnd != position) { @@ -129,7 +129,6 @@ private static int GetEndPositionOfOpeningDelimiter(SyntaxToken currentStringLit switch (currentStringLiteralToken.Kind()) { case SyntaxKind.SingleLineRawStringLiteralToken: - case SyntaxKind.MultiLineRawStringLiteralToken: { var text = currentStringLiteralToken.Text; var tokenSpan = currentStringLiteralToken.Span; @@ -169,7 +168,6 @@ private static int GetStartPositionOfClosingDelimiter(SyntaxToken currentStringL switch (currentStringLiteralToken.Kind()) { case SyntaxKind.SingleLineRawStringLiteralToken: - case SyntaxKind.MultiLineRawStringLiteralToken: { var text = currentStringLiteralToken.Text; var tokenSpan = currentStringLiteralToken.Span; diff --git a/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs b/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs index aeae78df6a839..83ac473570b5b 100644 --- a/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs +++ b/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs @@ -385,6 +385,33 @@ before text """"); } + [WpfFact] + public void TestReturnOnInterpolationOpenBraceInRawString() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = $"""before text$${0} following text"""; + """"); + + testState.SendReturn(handled: true); + testState.AssertCodeIs( + """" + var v = $""" + before text + $${0} following text + """; + """"); + } + + [WpfFact] + public void TestReturnInsideInterpolationInRawString() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = $"""before text{0$$} following text"""; + """"); + + testState.SendReturn(handled: false); + } + #endregion #region generate initial empty raw string From 095c5193af4719581e37855347c90f0de4da4b19 Mon Sep 17 00:00:00 2001 From: Rekkonnect <8298332+Rekkonnect@users.noreply.github.com> Date: Sun, 27 Oct 2024 23:38:32 +0200 Subject: [PATCH 110/508] Test and handle interpolations --- .../RawStringLiteralCommandHandler_Return.cs | 32 ++++++++++++++++--- .../RawStringLiteralCommandHandlerTests.cs | 20 ++++++++++++ 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs index 99cb044672162..bd890273d8d1c 100644 --- a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs +++ b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs @@ -75,22 +75,42 @@ private bool HandleCaretNotOnQuote(ITextView textView, ITextBuffer subjectBuffer var parsedDocument = ParsedDocument.CreateSynchronously(document, cancellationToken); var token = parsedDocument.Root.FindToken(position); + var preferredIndentationToken = token; switch (token.Kind()) { case SyntaxKind.SingleLineRawStringLiteralToken: break; - case SyntaxKind.InterpolatedStringTextToken - when token.Parent?.Parent is InterpolatedStringExpressionSyntax interpolated && - interpolated.StringStartToken.Kind() is SyntaxKind.InterpolatedSingleLineRawStringStartToken: - break; + case SyntaxKind.InterpolatedStringTextToken: + case SyntaxKind.OpenBraceToken: + if (token.Parent?.Parent is InterpolatedStringExpressionSyntax interpolated && + interpolated.StringStartToken.Kind() is SyntaxKind.InterpolatedSingleLineRawStringStartToken) + { + if (token.Kind() is SyntaxKind.OpenBraceToken) + { + // If we are not at the start of the interpolation braces, we do not intend to handle converting the raw string + // into a new one + if (position != token.SpanStart) + { + return false; + } + + // We prefer the indentation options of the string start delimiter because the indentation of the interpolation + // is empty and thus we cannot properly indent the lines that we insert + preferredIndentationToken = interpolated.StringStartToken; + } + + break; + } + + return false; default: return false; } var indentationOptions = subjectBuffer.GetIndentationOptions(_editorOptionsService, document.Project.GetFallbackAnalyzerOptions(), document.Project.Services, explicitFormat: false); - var indentation = token.GetPreferredIndentation(parsedDocument, indentationOptions, cancellationToken); + var indentation = preferredIndentationToken.GetPreferredIndentation(parsedDocument, indentationOptions, cancellationToken); var newLine = indentationOptions.FormattingOptions.NewLine; @@ -148,6 +168,7 @@ private static int GetEndPositionOfOpeningDelimiter(SyntaxToken currentStringLit } case SyntaxKind.InterpolatedStringTextToken: + case SyntaxKind.OpenBraceToken: var tokenParent = currentStringLiteralToken.Parent?.Parent; if (tokenParent is not InterpolatedStringExpressionSyntax interpolatedStringExpression) { @@ -186,6 +207,7 @@ private static int GetStartPositionOfClosingDelimiter(SyntaxToken currentStringL } case SyntaxKind.InterpolatedStringTextToken: + case SyntaxKind.OpenBraceToken: var tokenParent = currentStringLiteralToken.Parent?.Parent; if (tokenParent is not InterpolatedStringExpressionSyntax interpolatedStringExpression) { diff --git a/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs b/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs index 83ac473570b5b..d40c7e034a298 100644 --- a/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs +++ b/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs @@ -412,6 +412,26 @@ public void TestReturnInsideInterpolationInRawString() testState.SendReturn(handled: false); } + [WpfFact] + public void TestReturnWithinOpenBracesInterpolationInRawString() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = $$$"""before text{[||]{{0}}} following text"""; + """"); + + testState.SendReturn(handled: false); + } + + [WpfFact] + public void TestReturnWithinCloseBracesInterpolationInRawString() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = $$$"""before text{{{0}}[||]} following text"""; + """"); + + testState.SendReturn(handled: false); + } + #endregion #region generate initial empty raw string From 85fd5f53eb5bdc8ebaba87b5028fab0c61e930ef Mon Sep 17 00:00:00 2001 From: Rekkonnect <8298332+Rekkonnect@users.noreply.github.com> Date: Sun, 27 Oct 2024 23:42:14 +0200 Subject: [PATCH 111/508] Add end quotes tests --- .../RawStringLiteralCommandHandlerTests.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs b/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs index d40c7e034a298..f9a0cadf92835 100644 --- a/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs +++ b/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs @@ -432,6 +432,33 @@ public void TestReturnWithinCloseBracesInterpolationInRawString() testState.SendReturn(handled: false); } + [WpfFact] + public void TestReturnBeforeEndQuotesInRawString() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = """before text$$"""; + """"); + + testState.SendReturn(handled: true); + testState.AssertCodeIs( + """" + var v = $""" + before text + $$ + """; + """"); + } + + [WpfFact] + public void TestReturnWithinEndQuotesInRawString() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = """before text""$$"; + """"); + + testState.SendReturn(handled: false); + } + #endregion #region generate initial empty raw string From be2bf42fb4097d92e86508778bec0baefaad0026 Mon Sep 17 00:00:00 2001 From: Rekkonnect <8298332+Rekkonnect@users.noreply.github.com> Date: Mon, 28 Oct 2024 00:24:04 +0200 Subject: [PATCH 112/508] Generalize cases --- .../RawStringLiteralCommandHandler_Return.cs | 120 +++++++++++------- .../RawStringLiteralCommandHandlerTests.cs | 2 +- 2 files changed, 76 insertions(+), 46 deletions(-) diff --git a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs index bd890273d8d1c..45135cb00a565 100644 --- a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs +++ b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs @@ -109,29 +109,42 @@ private bool HandleCaretNotOnQuote(ITextView textView, ITextBuffer subjectBuffer return false; } + return MakeEdit(textView, subjectBuffer, position, document, parsedDocument, token, preferredIndentationToken, isEmpty: false, cancellationToken); + } + + private bool MakeEdit( + ITextView textView, ITextBuffer subjectBuffer, int position, Document document, + ParsedDocument parsedDocument, SyntaxToken token, SyntaxToken preferredIndentationToken, + bool isEmpty, CancellationToken cancellationToken) + { var indentationOptions = subjectBuffer.GetIndentationOptions(_editorOptionsService, document.Project.GetFallbackAnalyzerOptions(), document.Project.Services, explicitFormat: false); var indentation = preferredIndentationToken.GetPreferredIndentation(parsedDocument, indentationOptions, cancellationToken); var newLine = indentationOptions.FormattingOptions.NewLine; - using var transaction = CaretPreservingEditTransaction.TryCreate( CSharpEditorResources.Split_raw_string, textView, _undoHistoryRegistry, _editorOperationsFactoryService); - var edit = subjectBuffer.CreateEdit(); - // Add a newline at the position of the end literal - var closingStart = GetStartPositionOfClosingDelimiter(token); - var newLineAndIndentation = newLine + indentation; + var openingEnd = GetEndPositionOfOpeningDelimiter(token, isEmpty); var insertedLines = 1; - edit.Insert(closingStart, newLineAndIndentation); - // Add a newline at the caret's position - edit.Insert(position, newLineAndIndentation); - // Also add a newline at the start of the text, only if there is text before the caret's position - var openingEnd = GetEndPositionOfOpeningDelimiter(token); - if (openingEnd != position) + if (isEmpty) { - insertedLines++; - edit.Insert(openingEnd, newLineAndIndentation); + edit.Insert(position, newLine + newLine + indentation); + } + else + { + var newLineAndIndentation = newLine + indentation; + // Add a newline at the position of the end literal + var closingStart = GetStartPositionOfClosingDelimiter(token); + edit.Insert(closingStart, newLineAndIndentation); + // Add a newline at the caret's position + edit.Insert(position, newLineAndIndentation); + // Also add a newline at the start of the text, only if there is text before the caret's position + if (openingEnd != position) + { + insertedLines++; + edit.Insert(openingEnd, newLineAndIndentation); + } } var snapshot = edit.Apply(); @@ -144,11 +157,12 @@ private bool HandleCaretNotOnQuote(ITextView textView, ITextBuffer subjectBuffer return true; } - private static int GetEndPositionOfOpeningDelimiter(SyntaxToken currentStringLiteralToken) + private static int GetEndPositionOfOpeningDelimiter(SyntaxToken currentStringLiteralToken, bool isEmpty) { switch (currentStringLiteralToken.Kind()) { case SyntaxKind.SingleLineRawStringLiteralToken: + case SyntaxKind.MultiLineRawStringLiteralToken: { var text = currentStringLiteralToken.Text; var tokenSpan = currentStringLiteralToken.Span; @@ -159,25 +173,44 @@ private static int GetEndPositionOfOpeningDelimiter(SyntaxToken currentStringLit { var c = text[index]; if (c != '"') - return tokenStart + index; + { + var quotes = isEmpty ? index / 2 : index; + return tokenStart + quotes; + } index++; } - Contract.Fail("This should only be triggered by raw string literals that contain text aside from the double quotes"); - return -1; + // We have evaluated an emtpy raw string literal here and so we split the continuous double quotes into the start and end delimiters + Contract.ThrowIfFalse(isEmpty); + return tokenStart + length / 2; } case SyntaxKind.InterpolatedStringTextToken: case SyntaxKind.OpenBraceToken: + case SyntaxKind.CloseBraceToken: var tokenParent = currentStringLiteralToken.Parent?.Parent; if (tokenParent is not InterpolatedStringExpressionSyntax interpolatedStringExpression) { - Contract.Fail("This token should only be contained in an interpolated string text syntax"); + Contract.Fail("This token should only be contained in an interpolated string expression syntax"); return -1; } return interpolatedStringExpression.StringStartToken.Span.End; + case SyntaxKind.InterpolatedRawStringEndToken: + return currentStringLiteralToken.SpanStart; + + // This represents the case of a seemingly empty single-line interpolated raw string literal + // looking like this: $"""""", where all the quotes are parsed as the start delimiter + // We handle this as an empty interpolated string, so we return the index at where the text would begin + case SyntaxKind.InterpolatedSingleLineRawStringStartToken: + { + var firstQuoteOffset = currentStringLiteralToken.Text.IndexOf('"'); + var length = currentStringLiteralToken.Span.Length; + var quotes = length - firstQuoteOffset; + return currentStringLiteralToken.SpanStart + firstQuoteOffset + quotes / 2; + } + default: Contract.Fail("This should only be triggered on a known raw string literal kind"); return -1; @@ -189,6 +222,7 @@ private static int GetStartPositionOfClosingDelimiter(SyntaxToken currentStringL switch (currentStringLiteralToken.Kind()) { case SyntaxKind.SingleLineRawStringLiteralToken: + case SyntaxKind.MultiLineRawStringLiteralToken: { var text = currentStringLiteralToken.Text; var tokenSpan = currentStringLiteralToken.Span; @@ -202,21 +236,36 @@ private static int GetStartPositionOfClosingDelimiter(SyntaxToken currentStringL index--; } - Contract.Fail("This should only be triggered by raw string literals that contain text aside from the double quotes"); - return -1; + // We have evaluated an emtpy raw string literal here and so we split the continuous double quotes into the start and end delimiters + return tokenStart + tokenSpan.Length / 2; } case SyntaxKind.InterpolatedStringTextToken: case SyntaxKind.OpenBraceToken: + case SyntaxKind.CloseBraceToken: var tokenParent = currentStringLiteralToken.Parent?.Parent; if (tokenParent is not InterpolatedStringExpressionSyntax interpolatedStringExpression) { - Contract.Fail("This token should only be contained in an interpolated string text syntax"); + Contract.Fail("This token should only be contained in an interpolated string expression syntax"); return -1; } return interpolatedStringExpression.StringEndToken.SpanStart; + case SyntaxKind.InterpolatedRawStringEndToken: + return currentStringLiteralToken.SpanStart; + + // This represents the case of a seemingly empty single-line interpolated raw string literal + // looking like this: $"""""", where all the quotes are parsed as the start delimiter + // We handle this as an empty interpolated string, so we return the index at where the text would begin + case SyntaxKind.InterpolatedSingleLineRawStringStartToken: + { + var firstQuoteOffset = currentStringLiteralToken.Text.IndexOf('"'); + var length = currentStringLiteralToken.Span.Length; + var quotes = length - firstQuoteOffset; + return currentStringLiteralToken.SpanStart + firstQuoteOffset + quotes / 2; + } + default: Contract.Fail("This should only be triggered on a known raw string literal kind"); return -1; @@ -245,16 +294,17 @@ private bool HandleCaretOnQuote(ITextView textView, ITextBuffer subjectBuffer, S quotesBefore++; } - if (quotesAfter != quotesBefore) + var isEmpty = quotesBefore > 0; + if (isEmpty && quotesAfter != quotesBefore) return false; if (quotesAfter < 3) return false; - return SplitRawString(textView, subjectBuffer, span.Start.Position, cancellationToken); + return SplitRawString(textView, subjectBuffer, span.Start.Position, isEmpty, cancellationToken); } - private bool SplitRawString(ITextView textView, ITextBuffer subjectBuffer, int position, CancellationToken cancellationToken) + private bool SplitRawString(ITextView textView, ITextBuffer subjectBuffer, int position, bool isEmpty, CancellationToken cancellationToken) { var document = subjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); if (document == null) @@ -271,26 +321,6 @@ SyntaxKind.InterpolatedSingleLineRawStringStartToken or return false; } - var indentationOptions = subjectBuffer.GetIndentationOptions(_editorOptionsService, document.Project.GetFallbackAnalyzerOptions(), document.Project.Services, explicitFormat: false); - var indentation = token.GetPreferredIndentation(parsedDocument, indentationOptions, cancellationToken); - - var newLine = indentationOptions.FormattingOptions.NewLine; - - using var transaction = CaretPreservingEditTransaction.TryCreate( - CSharpEditorResources.Split_raw_string, textView, _undoHistoryRegistry, _editorOperationsFactoryService); - - var edit = subjectBuffer.CreateEdit(); - - // apply the change: - edit.Insert(position, newLine + newLine + indentation); - var snapshot = edit.Apply(); - - // move caret: - var lineInNewSnapshot = snapshot.GetLineFromPosition(position); - var nextLine = snapshot.GetLineFromLineNumber(lineInNewSnapshot.LineNumber + 1); - textView.Caret.MoveTo(new VirtualSnapshotPoint(nextLine, indentation.Length)); - - transaction?.Complete(); - return true; + return MakeEdit(textView, subjectBuffer, position, document, parsedDocument, token, preferredIndentationToken: token, isEmpty, cancellationToken); } } diff --git a/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs b/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs index f9a0cadf92835..e1b54f1a0a14c 100644 --- a/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs +++ b/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs @@ -442,7 +442,7 @@ public void TestReturnBeforeEndQuotesInRawString() testState.SendReturn(handled: true); testState.AssertCodeIs( """" - var v = $""" + var v = """ before text $$ """; From 1f68b0e2a2bcf6c1fec0d852220b8ec6ff21731b Mon Sep 17 00:00:00 2001 From: Youssef1313 Date: Sat, 13 Aug 2022 08:25:44 +0200 Subject: [PATCH 113/508] Fix generate type dialog invoked on type argument in a base list --- .../GenerateType/GenerateTypeTests_Dialog.cs | 28 +++++++++++++++++++ ...ctUserDiagnosticTest_GenerateTypeDialog.cs | 6 ++-- .../GenerateType/CSharpGenerateTypeService.cs | 17 ++++------- 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.cs b/src/EditorFeatures/CSharpTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.cs index 2da0b9dcaf527..7651c444ad9dd 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.cs @@ -2271,6 +2271,34 @@ public struct Bar isNewFile: false, assertGenerateTypeDialogOptions: new GenerateTypeDialogOptions(false, TypeKindOptions.Class | TypeKindOptions.Structure, false)); } + + [Fact, WorkItem(63280, "https://github.com/dotnet/roslyn/issues/63280")] + public async Task GenerateType_GenericBaseList() + { + await TestWithMockedGenerateTypeDialog( +initial: @" +using System.Collections.Generic; + +struct C : IEnumerable<[|$$NewType|]> +{ +}", +languageName: LanguageNames.CSharp, +typeName: "$$NewType", +expected: @" +using System.Collections.Generic; + +struct C : IEnumerable +{ +} + +public class $$NewType +{ +}", +accessibility: Accessibility.Public, +typeKind: TypeKind.Class, +isNewFile: false, +assertGenerateTypeDialogOptions: new GenerateTypeDialogOptions(false, TypeKindOptions.AllOptions, false)); + } #endregion #region Delegates [Fact] diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_GenerateTypeDialog.cs b/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_GenerateTypeDialog.cs index 712b5060e3b07..45605d118ff13 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_GenerateTypeDialog.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_GenerateTypeDialog.cs @@ -167,9 +167,9 @@ await TestOperationsAsync(testState.Workspace, expectedTextWithUsings, operation if (assertGenerateTypeDialogOptions != null) { - Assert.True(assertGenerateTypeDialogOptions.IsPublicOnlyAccessibility == generateTypeDialogOptions.IsPublicOnlyAccessibility); - Assert.True(assertGenerateTypeDialogOptions.TypeKindOptions == generateTypeDialogOptions.TypeKindOptions); - Assert.True(assertGenerateTypeDialogOptions.IsAttribute == generateTypeDialogOptions.IsAttribute); + Assert.Equal(assertGenerateTypeDialogOptions.IsPublicOnlyAccessibility, generateTypeDialogOptions.IsPublicOnlyAccessibility); + Assert.Equal(assertGenerateTypeDialogOptions.TypeKindOptions, generateTypeDialogOptions.TypeKindOptions); + Assert.Equal(assertGenerateTypeDialogOptions.IsAttribute, generateTypeDialogOptions.IsAttribute); } if (assertTypeKindPresent != null) diff --git a/src/Features/CSharp/Portable/GenerateType/CSharpGenerateTypeService.cs b/src/Features/CSharp/Portable/GenerateType/CSharpGenerateTypeService.cs index 129b0945c52bf..303c511a413d0 100644 --- a/src/Features/CSharp/Portable/GenerateType/CSharpGenerateTypeService.cs +++ b/src/Features/CSharp/Portable/GenerateType/CSharpGenerateTypeService.cs @@ -664,23 +664,16 @@ internal override bool TryGetBaseList(ExpressionSyntax expression, out TypeKindO return false; } - var node = expression as SyntaxNode; - - while (node != null) + if (expression.Parent is BaseTypeSyntax { Parent: BaseListSyntax baseList }) { - if (node is BaseListSyntax) + if (baseList.Parent.IsKind(SyntaxKind.InterfaceDeclaration, SyntaxKind.StructDeclaration, SyntaxKind.RecordStructDeclaration)) { - if (node.Parent.Kind() is SyntaxKind.InterfaceDeclaration or SyntaxKind.StructDeclaration or SyntaxKind.RecordStructDeclaration) - { - typeKindValue = TypeKindOptions.Interface; - return true; - } - - typeKindValue = TypeKindOptions.BaseList; + typeKindValue = TypeKindOptions.Interface; return true; } - node = node.Parent; + typeKindValue = TypeKindOptions.BaseList; + return true; } return false; From e1a90899f7b7882fc8d4564b31a427b1e0076921 Mon Sep 17 00:00:00 2001 From: Youssef1313 Date: Sat, 13 Aug 2022 08:52:40 +0200 Subject: [PATCH 114/508] Simplify code, add test --- .../GenerateType/GenerateTypeTests_Dialog.cs | 36 +++++++++++++++++++ .../AbstractGenerateTypeService.CodeAction.cs | 3 +- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.cs b/src/EditorFeatures/CSharpTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.cs index 7651c444ad9dd..a3f21bcf538a9 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.cs @@ -2299,6 +2299,42 @@ public class $$NewType isNewFile: false, assertGenerateTypeDialogOptions: new GenerateTypeDialogOptions(false, TypeKindOptions.AllOptions, false)); } + + [Fact] + public async Task GenerateType_QualifiedBaseList() + { + await TestWithMockedGenerateTypeDialog( +initial: @" +using System.Collections.Generic; + +struct C : A.B.[|$$INewType|] +{ +} + +namespace A.B +{ +}", +languageName: LanguageNames.CSharp, +typeName: "$$INewType", +expected: @" +using System.Collections.Generic; + +struct C : A.B.INewType +{ +} + +namespace A.B +{ + public interface $$INewType + { + } +}", +accessibility: Accessibility.Public, +typeKind: TypeKind.Interface, +isNewFile: false, +assertGenerateTypeDialogOptions: new GenerateTypeDialogOptions(false, TypeKindOptions.Interface, false)); + } + #endregion #region Delegates [Fact] diff --git a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.CodeAction.cs b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.CodeAction.cs index 6c3ca86fd1c93..e7872f4dae9d6 100644 --- a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.CodeAction.cs +++ b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.CodeAction.cs @@ -140,8 +140,7 @@ private bool GetPredefinedTypeKindOption(State state, out TypeKindOptions typeKi return true; } - if (_service.TryGetBaseList(state.NameOrMemberAccessExpression, out var typeKindValue) || - _service.TryGetBaseList(state.SimpleName, out typeKindValue)) + if (_service.TryGetBaseList(state.NameOrMemberAccessExpression, out var typeKindValue)) { typeKindValueFinal = typeKindValue; return true; From 7bc4305a99d19d40bcc04f61f00255b6ff04f91d Mon Sep 17 00:00:00 2001 From: Youssef1313 Date: Sat, 13 Aug 2022 08:55:12 +0200 Subject: [PATCH 115/508] One more test case --- .../GenerateType/GenerateTypeTests_Dialog.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/EditorFeatures/CSharpTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.cs b/src/EditorFeatures/CSharpTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.cs index a3f21bcf538a9..75a8c2c37e8b2 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.cs @@ -2335,6 +2335,40 @@ public interface $$INewType assertGenerateTypeDialogOptions: new GenerateTypeDialogOptions(false, TypeKindOptions.Interface, false)); } + [Fact] + public async Task GenerateType_AliasQualifiedBaseList() + { + await TestWithMockedGenerateTypeDialog( +initial: @" +using System.Collections.Generic; + +struct C : global::A.B.[|$$INewType|] +{ +} + +namespace A.B +{ +}", +languageName: LanguageNames.CSharp, +typeName: "$$INewType", +expected: @" +using System.Collections.Generic; + +struct C : global::A.B.INewType +{ +} + +namespace A.B +{ + public interface $$INewType + { + } +}", +accessibility: Accessibility.Public, +typeKind: TypeKind.Interface, +isNewFile: false, +assertGenerateTypeDialogOptions: new GenerateTypeDialogOptions(false, TypeKindOptions.Interface, false)); + } #endregion #region Delegates [Fact] From c5173d1dbd61f087a7dd5f7e35b2b9f5fca318af Mon Sep 17 00:00:00 2001 From: Youssef1313 Date: Sat, 13 Aug 2022 11:20:58 +0200 Subject: [PATCH 116/508] Fix VB, fix tests --- .../GenerateType/GenerateTypeTests_Dialog.cs | 12 +++---- .../GenerateType/GenerateTypeTests_Dialog.vb | 31 +++++++++++++++++++ .../VisualBasicGenerateTypeService.vb | 21 +++++-------- 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.cs b/src/EditorFeatures/CSharpTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.cs index 75a8c2c37e8b2..8a6acfa346e29 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.cs @@ -2283,7 +2283,7 @@ struct C : IEnumerable<[|$$NewType|]> { }", languageName: LanguageNames.CSharp, -typeName: "$$NewType", +typeName: "NewType", expected: @" using System.Collections.Generic; @@ -2291,7 +2291,7 @@ struct C : IEnumerable { } -public class $$NewType +public class NewType { }", accessibility: Accessibility.Public, @@ -2315,7 +2315,7 @@ namespace A.B { }", languageName: LanguageNames.CSharp, -typeName: "$$INewType", +typeName: "INewType", expected: @" using System.Collections.Generic; @@ -2325,7 +2325,7 @@ struct C : A.B.INewType namespace A.B { - public interface $$INewType + public interface INewType { } }", @@ -2350,7 +2350,7 @@ namespace A.B { }", languageName: LanguageNames.CSharp, -typeName: "$$INewType", +typeName: "INewType", expected: @" using System.Collections.Generic; @@ -2360,7 +2360,7 @@ struct C : global::A.B.INewType namespace A.B { - public interface $$INewType + public interface INewType { } }", diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.vb b/src/EditorFeatures/VisualBasicTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.vb index 9d71a41dfd401..f7f5e9bd1e3c5 100644 --- a/src/EditorFeatures/VisualBasicTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.vb +++ b/src/EditorFeatures/VisualBasicTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.vb @@ -1626,6 +1626,37 @@ typeName:="Bar", isMissing:=True) End Function + + + Public Async Function GenerateType_GenericBaseList() As Task + Await TestWithMockedGenerateTypeDialog( +initial:= +Imports System.Collections.Generic + +Structure S + Implements IEnumerable(Of [|$$NewType|]) + +End Structure +.NormalizedValue, +languageName:=LanguageNames.VisualBasic, +typeName:="NewType", +expected:= +Imports System.Collections.Generic + +Structure S + Implements IEnumerable(Of NewType) + +End Structure + +Public Class NewType +End Class +.NormalizedValue, +isNewFile:=False, +accessibility:=Accessibility.Public, +typeKind:=TypeKind.Class, +assertGenerateTypeDialogOptions:=New GenerateTypeDialogOptions(False, TypeKindOptions.AllOptions, False)) + End Function + #End Region #Region "Delegates" diff --git a/src/Features/VisualBasic/Portable/GenerateType/VisualBasicGenerateTypeService.vb b/src/Features/VisualBasic/Portable/GenerateType/VisualBasicGenerateTypeService.vb index ec54305a08db7..f0bf267e39a6f 100644 --- a/src/Features/VisualBasic/Portable/GenerateType/VisualBasicGenerateTypeService.vb +++ b/src/Features/VisualBasic/Portable/GenerateType/VisualBasicGenerateTypeService.vb @@ -539,23 +539,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.GenerateType Return False End If - Dim node As SyntaxNode = expression - While node IsNot Nothing - If TypeOf node Is InheritsStatementSyntax Then - If node.Parent IsNot Nothing AndAlso TypeOf node.Parent Is InterfaceBlockSyntax Then - typeKindValue = TypeKindOptions.Interface - Return True - End If - - typeKindValue = TypeKindOptions.Class - Return True - ElseIf TypeOf node Is ImplementsStatementSyntax Then + If TypeOf expression.Parent Is InheritsStatementSyntax Then + If expression.Parent.Parent IsNot Nothing AndAlso TypeOf expression.Parent.Parent Is InterfaceBlockSyntax Then typeKindValue = TypeKindOptions.Interface Return True End If - node = node.Parent - End While + typeKindValue = TypeKindOptions.Class + Return True + ElseIf TypeOf expression.Parent Is ImplementsStatementSyntax Then + typeKindValue = TypeKindOptions.Interface + Return True + End If Return False End Function From 574373044f5fee529b0a6a26a4ff1a4ac115890e Mon Sep 17 00:00:00 2001 From: Youssef Victor Date: Fri, 11 Nov 2022 00:11:54 +0200 Subject: [PATCH 117/508] Update CSharpGenerateTypeService.cs --- .../CSharp/Portable/GenerateType/CSharpGenerateTypeService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/CSharp/Portable/GenerateType/CSharpGenerateTypeService.cs b/src/Features/CSharp/Portable/GenerateType/CSharpGenerateTypeService.cs index 303c511a413d0..468e73304c84f 100644 --- a/src/Features/CSharp/Portable/GenerateType/CSharpGenerateTypeService.cs +++ b/src/Features/CSharp/Portable/GenerateType/CSharpGenerateTypeService.cs @@ -666,7 +666,7 @@ internal override bool TryGetBaseList(ExpressionSyntax expression, out TypeKindO if (expression.Parent is BaseTypeSyntax { Parent: BaseListSyntax baseList }) { - if (baseList.Parent.IsKind(SyntaxKind.InterfaceDeclaration, SyntaxKind.StructDeclaration, SyntaxKind.RecordStructDeclaration)) + if (baseList.Parent.Kind() is SyntaxKind.InterfaceDeclaration or SyntaxKind.StructDeclaration or SyntaxKind.RecordStructDeclaration) { typeKindValue = TypeKindOptions.Interface; return true; From 4c6be58bd995ccdbfe1cbd171b7f2533e30c8f1c Mon Sep 17 00:00:00 2001 From: David Barbet Date: Tue, 8 Oct 2024 18:30:32 -0700 Subject: [PATCH 118/508] Ensure discards are initially soft selected in VSCode --- .../ItemManager.CompletionListUpdater.cs | 80 +------------ .../Portable/Completion/CompletionService.cs | 76 ++++++++++++ .../Handler/Completion/CompletionHandler.cs | 111 ++++++++++++++++-- .../Completion/CompletionResultFactory.cs | 5 +- .../Completion/CompletionFeaturesTests.cs | 52 ++++++++ 5 files changed, 231 insertions(+), 93 deletions(-) diff --git a/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs b/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs index 23542684ff4b8..ef4783951a8f4 100644 --- a/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs +++ b/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs @@ -427,7 +427,7 @@ int DefaultIndexToFabricatedOriginalSortedIndex(int i) return null; } - var isHardSelection = IsHardSelection(bestOrFirstMatchResult.CompletionItem, bestOrFirstMatchResult.ShouldBeConsideredMatchingFilterText); + var isHardSelection = CompletionService.IsHardSelection(bestOrFirstMatchResult.CompletionItem, bestOrFirstMatchResult.ShouldBeConsideredMatchingFilterText, _hasSuggestedItemOptions, _filterText); var updateSelectionHint = isHardSelection ? UpdateSelectionHint.Selected : UpdateSelectionHint.SoftSelected; return new(selectedItemIndex, updateSelectionHint, uniqueItem); @@ -754,84 +754,6 @@ private static bool TryGetInitialTriggerLocation(AsyncCompletionSessionDataSnaps return false; } - private bool IsHardSelection( - RoslynCompletionItem item, - bool matchedFilterText) - { - if (_hasSuggestedItemOptions) - { - return false; - } - - // We don't have a builder and we have a best match. Normally this will be hard - // selected, except for a few cases. Specifically, if no filter text has been - // provided, and this is not a preselect match then we will soft select it. This - // happens when the completion list comes up implicitly and there is something in - // the MRU list. In this case we do want to select it, but not with a hard - // selection. Otherwise you can end up with the following problem: - // - // dim i as integer = - // - // Completion will comes up after = with 'integer' selected (Because of MRU). We do - // not want 'space' to commit this. - - // If all that has been typed is punctuation, then don't hard select anything. - // It's possible the user is just typing language punctuation and selecting - // anything in the list will interfere. We only allow this if the filter text - // exactly matches something in the list already. - if (_filterText.Length > 0 && IsAllPunctuation(_filterText) && _filterText != item.DisplayText) - { - return false; - } - - // If the user hasn't actually typed anything, then don't hard select any item. - // The only exception to this is if the completion provider has requested the - // item be preselected. - if (_filterText.Length == 0) - { - // Item didn't want to be hard selected with no filter text. - // So definitely soft select it. - if (item.Rules.SelectionBehavior != CompletionItemSelectionBehavior.HardSelection) - { - return false; - } - - // Item did not ask to be preselected. So definitely soft select it. - if (item.Rules.MatchPriority == MatchPriority.Default) - { - return false; - } - } - - // The user typed something, or the item asked to be preselected. In - // either case, don't soft select this. - Debug.Assert(_filterText.Length > 0 || item.Rules.MatchPriority != MatchPriority.Default); - - // If the user moved the caret left after they started typing, the 'best' match may not match at all - // against the full text span that this item would be replacing. - if (!matchedFilterText) - { - return false; - } - - // There was either filter text, or this was a preselect match. In either case, we - // can hard select this. - return true; - } - - private static bool IsAllPunctuation(string filterText) - { - foreach (var ch in filterText) - { - if (!char.IsPunctuation(ch)) - { - return false; - } - } - - return true; - } - /// /// A potential filter character is something that can filter a completion lists and is /// *guaranteed* to not be a commit character. diff --git a/src/Features/Core/Portable/Completion/CompletionService.cs b/src/Features/Core/Portable/Completion/CompletionService.cs index af9865823b7b9..d8eb7de5ad784 100644 --- a/src/Features/Core/Portable/Completion/CompletionService.cs +++ b/src/Features/Core/Portable/Completion/CompletionService.cs @@ -363,6 +363,82 @@ internal static void FilterItems( } } + internal static bool IsHardSelection(CompletionItem item, bool matchedFilterText, bool hasSuggestionModeItem, string filterText) + { + if (hasSuggestionModeItem) + { + return false; + } + + // We don't have a builder and we have a best match. Normally this will be hard + // selected, except for a few cases. Specifically, if no filter text has been + // provided, and this is not a preselect match then we will soft select it. This + // happens when the completion list comes up implicitly and there is something in + // the MRU list. In this case we do want to select it, but not with a hard + // selection. Otherwise you can end up with the following problem: + // + // dim i as integer = + // + // Completion will comes up after = with 'integer' selected (Because of MRU). We do + // not want 'space' to commit this. + + // If all that has been typed is punctuation, then don't hard select anything. + // It's possible the user is just typing language punctuation and selecting + // anything in the list will interfere. We only allow this if the filter text + // exactly matches something in the list already. + if (filterText.Length > 0 && IsAllPunctuation(filterText) && filterText != item.DisplayText) + { + return false; + } + + // If the user hasn't actually typed anything, then don't hard select any item. + // The only exception to this is if the completion provider has requested the + // item be preselected. + if (filterText.Length == 0) + { + // Item didn't want to be hard selected with no filter text. + // So definitely soft select it. + if (item.Rules.SelectionBehavior != CompletionItemSelectionBehavior.HardSelection) + { + return false; + } + + // Item did not ask to be preselected. So definitely soft select it. + if (item.Rules.MatchPriority == MatchPriority.Default) + { + return false; + } + } + + // The user typed something, or the item asked to be preselected. In + // either case, don't soft select this. + Debug.Assert(filterText.Length > 0 || item.Rules.MatchPriority != MatchPriority.Default); + + // If the user moved the caret left after they started typing, the 'best' match may not match at all + // against the full text span that this item would be replacing. + if (!matchedFilterText) + { + return false; + } + + // There was either filter text, or this was a preselect match. In either case, we + // can hard select this. + return true; + } + + internal static bool IsAllPunctuation(string filterText) + { + foreach (var ch in filterText) + { + if (!char.IsPunctuation(ch)) + { + return false; + } + } + + return true; + } + /// /// Don't call. Used for pre-populating MEF providers only. /// diff --git a/src/LanguageServer/Protocol/Handler/Completion/CompletionHandler.cs b/src/LanguageServer/Protocol/Handler/Completion/CompletionHandler.cs index 9ad91dcbe6864..336eaf4a5bc6c 100644 --- a/src/LanguageServer/Protocol/Handler/Completion/CompletionHandler.cs +++ b/src/LanguageServer/Protocol/Handler/Completion/CompletionHandler.cs @@ -3,8 +3,10 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -99,16 +101,16 @@ public CompletionHandler( if (completionListResult == null) return null; - var (list, isIncomplete, resultId) = completionListResult.Value; + var (list, isIncomplete, isHardSelection, resultId) = completionListResult.Value; var result = await CompletionResultFactory - .ConvertToLspCompletionListAsync(document, capabilityHelper, list, isIncomplete, resultId, cancellationToken) + .ConvertToLspCompletionListAsync(document, capabilityHelper, list, isIncomplete, isHardSelection, resultId, cancellationToken) .ConfigureAwait(false); return result; } - private static async Task<(CompletionList CompletionList, bool IsIncomplete, long ResultId)?> GetFilteredCompletionListAsync( + private static async Task<(CompletionList CompletionList, bool IsIncomplete, bool isHardSelection, long ResultId)?> GetFilteredCompletionListAsync( LSP.CompletionContext? context, Document document, SourceText sourceText, @@ -157,9 +159,9 @@ public CompletionHandler( completionList = completionList.WithSpan(defaultSpan); } - var (filteredCompletionList, isIncomplete) = FilterCompletionList(completionList, completionListMaxSize, completionTrigger, sourceText); + var (filteredCompletionList, isIncomplete, isHardSelection) = FilterCompletionList(completionList, completionListMaxSize, completionTrigger, sourceText, capabilityHelper); - return (filteredCompletionList, isIncomplete, resultId); + return (filteredCompletionList, isIncomplete, isHardSelection, resultId); } private static async Task<(CompletionList CompletionList, long ResultId)?> CalculateListAsync( @@ -184,15 +186,13 @@ public CompletionHandler( return (completionList, resultId); } - private static (CompletionList CompletionList, bool IsIncomplete) FilterCompletionList( + private static (CompletionList CompletionList, bool IsIncomplete, bool isHardSelection) FilterCompletionList( CompletionList completionList, int completionListMaxSize, CompletionTrigger completionTrigger, - SourceText sourceText) + SourceText sourceText, + CompletionCapabilityHelper completionCapabilityHelper) { - if (completionListMaxSize < 0 || completionListMaxSize >= completionList.ItemsList.Count) - return (completionList, false); - var filterText = sourceText.GetSubText(completionList.Span).ToString(); var filterReason = GetFilterReason(completionTrigger); @@ -219,6 +219,29 @@ private static (CompletionList CompletionList, bool IsIncomplete) FilterCompleti // Next, we sort the list based on the pattern matching result. matchResultsBuilder.Sort(MatchResult.SortingComparer); + // Determine if the list should be hard selected or soft selected. + bool isHardSelection; + if (matchResultsBuilder.Count > 0) + { + var bestResult = GetBestCompletionItemSelectionFromFilteredResults(matchResultsBuilder); + isHardSelection = CompletionService.IsHardSelection(bestResult.CompletionItem, bestResult.ShouldBeConsideredMatchingFilterText, completionList.SuggestionModeItem != null, filterText); + } + else + { + isHardSelection = completionList.SuggestionModeItem != null; + } + + // If we only had punctuation - we set the list to be incomplete so we get called back when the user continues typing. + // If they type something that is not punctuation, we may need to update the hard vs soft selection. + // For example, typing '_' should initially be soft selection, but if the user types 'o' we should hard select '_otherVar' (if it exists). + // This isn't perfect - ideally we would make this determination every time a filter character is typed, but we do not get called back + // for typing filter characters in LSP (unless we always set isIncomplete, which is expensive). + var isIncomplete = CompletionService.IsAllPunctuation(filterText); + + // If our completion list hasn't hit the max size, we don't need to do anything filtering + if (completionListMaxSize < 0 || completionListMaxSize >= completionList.ItemsList.Count) + return (completionList, isIncomplete, isHardSelection); + // Finally, truncate the list to 1000 items plus any preselected items that occur after the first 1000. var filteredList = matchResultsBuilder .Take(completionListMaxSize) @@ -239,9 +262,11 @@ private static (CompletionList CompletionList, bool IsIncomplete) FilterCompleti // Currently the VS client does not remember to re-request, so the completion list only ever shows items from "Som" // so we always set the isIncomplete flag to true when the original list size (computed when no filter text was typed) is too large. // VS bug here - https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1335142 - var isIncomplete = completionList.ItemsList.Count > newCompletionList.ItemsList.Count; + isIncomplete |= completionCapabilityHelper.SupportVSInternalClientCapabilities + ? completionList.ItemsList.Count > newCompletionList.ItemsList.Count + : matchResultsBuilder.Count > filteredList.Length; - return (newCompletionList, isIncomplete); + return (newCompletionList, isIncomplete, isHardSelection); static CompletionFilterReason GetFilterReason(CompletionTrigger trigger) { @@ -253,5 +278,67 @@ static CompletionFilterReason GetFilterReason(CompletionTrigger trigger) }; } } + + private static MatchResult GetBestCompletionItemSelectionFromFilteredResults(IReadOnlyList filteredMatchResults) + { + Debug.Assert(filteredMatchResults.Count > 0); + + var bestResult = filteredMatchResults[0]; + var bestResultMruIndex = bestResult.RecentItemIndex; + + for (int i = 1, n = filteredMatchResults.Count; i < n; i++) + { + var currentResult = filteredMatchResults[i]; + var currentResultMruIndex = currentResult.RecentItemIndex; + + // Most recently used item is our top preference. + if (currentResultMruIndex != bestResultMruIndex) + { + if (currentResultMruIndex > bestResultMruIndex) + { + bestResult = currentResult; + bestResultMruIndex = currentResultMruIndex; + } + + continue; + } + + // 2nd preference is IntelliCode item + var currentIsPreferred = currentResult.CompletionItem.IsPreferredItem(); + var bestIsPreferred = bestResult.CompletionItem.IsPreferredItem(); + + if (currentIsPreferred != bestIsPreferred) + { + if (currentIsPreferred && !bestIsPreferred) + { + bestResult = currentResult; + } + + continue; + } + + // 3rd preference is higher MatchPriority + var currentMatchPriority = currentResult.CompletionItem.Rules.MatchPriority; + var bestMatchPriority = bestResult.CompletionItem.Rules.MatchPriority; + + if (currentMatchPriority != bestMatchPriority) + { + if (currentMatchPriority > bestMatchPriority) + { + bestResult = currentResult; + } + + continue; + } + + // final preference is match to FilterText over AdditionalFilterTexts + if (bestResult.MatchedWithAdditionalFilterTexts && !currentResult.MatchedWithAdditionalFilterTexts) + { + bestResult = currentResult; + } + } + + return bestResult; + } } } diff --git a/src/LanguageServer/Protocol/Handler/Completion/CompletionResultFactory.cs b/src/LanguageServer/Protocol/Handler/Completion/CompletionResultFactory.cs index a74e297641ab0..a01923c0fe5f9 100644 --- a/src/LanguageServer/Protocol/Handler/Completion/CompletionResultFactory.cs +++ b/src/LanguageServer/Protocol/Handler/Completion/CompletionResultFactory.cs @@ -38,10 +38,11 @@ internal static class CompletionResultFactory CompletionCapabilityHelper capabilityHelper, CompletionList list, bool isIncomplete, + bool isHardSelection, long resultId, CancellationToken cancellationToken) { - var isSuggestionMode = list.SuggestionModeItem is not null; + var isSuggestionMode = !isHardSelection; if (list.ItemsList.Count == 0) { return new LSP.VSInternalCompletionList @@ -92,7 +93,7 @@ internal static class CompletionResultFactory // // If we have a suggestion mode item, we just need to keep the list in suggestion mode. // We don't need to return the fake suggestion mode item. - SuggestionMode = list.SuggestionModeItem != null, + SuggestionMode = isSuggestionMode, Data = capabilityHelper.SupportVSInternalCompletionListData ? resolveData : null, }; diff --git a/src/LanguageServer/ProtocolUnitTests/Completion/CompletionFeaturesTests.cs b/src/LanguageServer/ProtocolUnitTests/Completion/CompletionFeaturesTests.cs index d3353ae4f9a35..0b68236cd9f3a 100644 --- a/src/LanguageServer/ProtocolUnitTests/Completion/CompletionFeaturesTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Completion/CompletionFeaturesTests.cs @@ -809,6 +809,58 @@ public async Task TestSoftSelectionWhenFilterTextIsEmptyForPreselectItemAsync(bo Assert.Null(item.CommitCharacters); } + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/vscode-csharp/issues/7623")] + public async Task TestSoftSelectionForDiscardAsync(bool mutatingLspWorkspace) + { + var markup = +@" +public class A +{ + public void M() + { + var _someDiscard = 1; + _{|caret:|} + } +}"; + + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, DefaultClientCapabilities); + var caretLocation = testLspServer.GetLocations("caret").Single(); + await testLspServer.OpenDocumentAsync(caretLocation.Uri); + + testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption(CompletionOptionsStorage.TriggerInArgumentLists, LanguageNames.CSharp, true); + + var completionParams = CreateCompletionParams( + caretLocation, + invokeKind: LSP.VSInternalCompletionInvokeKind.Typing, + triggerCharacter: "_", + triggerKind: LSP.CompletionTriggerKind.Invoked); + + var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); + var results = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentCompletionName, completionParams, CancellationToken.None).ConfigureAwait(false); + var actualItem = results.Items.First(i => i.Label == "_someDiscard"); + + Assert.True(results.IsIncomplete); + Assert.Empty(results.ItemDefaults.CommitCharacters); + Assert.Equal("_someDiscard", actualItem.Label); + Assert.Null(actualItem.CommitCharacters); + + await testLspServer.InsertTextAsync(caretLocation.Uri, (caretLocation.Range.End.Line, caretLocation.Range.End.Character, "s")); + + completionParams = CreateCompletionParams( + GetLocationPlusOne(caretLocation), + invokeKind: LSP.VSInternalCompletionInvokeKind.Typing, + triggerCharacter: "s", + triggerKind: LSP.CompletionTriggerKind.TriggerForIncompleteCompletions); + + results = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentCompletionName, completionParams, CancellationToken.None).ConfigureAwait(false); + actualItem = results.Items.First(i => i.Label == "_someDiscard"); + + Assert.False(results.IsIncomplete); + Assert.NotEmpty(results.ItemDefaults.CommitCharacters); + Assert.Equal("_someDiscard", actualItem.Label); + Assert.Null(actualItem.CommitCharacters); + } + private sealed class CSharpLspThrowExceptionOnChangeCompletionService : CompletionService { private CSharpLspThrowExceptionOnChangeCompletionService(SolutionServices services, IAsynchronousOperationListenerProvider listenerProvider) : base(services, listenerProvider) From 502d0ec7374c2a566e220c629c03516b1a6b3e57 Mon Sep 17 00:00:00 2001 From: Ankita Khera <40616383+akhera99@users.noreply.github.com> Date: Mon, 28 Oct 2024 16:13:04 -0700 Subject: [PATCH 119/508] update configs for 17.13p2 snap (#75650) --- eng/Versions.props | 2 +- eng/config/PublishData.json | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index be8b3a8265310..0da02a083ab20 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -8,7 +8,7 @@ 4 13 0 - 1 + 2 $(MajorVersion).$(MinorVersion).$(PatchVersion) + From 76a654b2c208d8aa9018f4230b784e8bf88c208e Mon Sep 17 00:00:00 2001 From: David Barbet Date: Tue, 29 Oct 2024 17:52:22 -0700 Subject: [PATCH 128/508] feedback --- .../Handler/Completion/CompletionHandler.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/LanguageServer/Protocol/Handler/Completion/CompletionHandler.cs b/src/LanguageServer/Protocol/Handler/Completion/CompletionHandler.cs index 2c9951172b4c1..050bfd25f152e 100644 --- a/src/LanguageServer/Protocol/Handler/Completion/CompletionHandler.cs +++ b/src/LanguageServer/Protocol/Handler/Completion/CompletionHandler.cs @@ -196,6 +196,21 @@ private static (CompletionList CompletionList, bool IsIncomplete, bool isHardSel var filterText = sourceText.GetSubText(completionList.Span).ToString(); var filterReason = GetFilterReason(completionTrigger); + // Determine if the list should be hard selected or soft selected. + var isFilterTextAllPunctuation = CompletionService.IsAllPunctuation(filterText); + + // If we only had punctuation - we set soft selection and the list to be incomplete so we get called back when the user continues typing. + // If they type something that is not punctuation, we may need to update the hard vs soft selection. + // For example, typing '_' should initially be soft selection, but if the user types 'o' we should hard select '_otherVar' (if it exists). + // This isn't perfect - ideally we would make this determination every time a filter character is typed, but we do not get called back + // for typing filter characters in LSP (unless we always set isIncomplete, which is expensive). + var isHardSelection = completionList.SuggestionModeItem is null && !isFilterTextAllPunctuation; + var isIncomplete = isFilterTextAllPunctuation; + + // If our completion list hasn't hit the max size, we don't need to do anything filtering + if (completionListMaxSize < 0 || completionListMaxSize >= completionList.ItemsList.Count) + return (completionList, isIncomplete, isHardSelection); + // Use pattern matching to determine which items are most relevant out of the calculated items. using var _ = ArrayBuilder.GetInstance(out var matchResultsBuilder); var index = 0; @@ -219,21 +234,6 @@ private static (CompletionList CompletionList, bool IsIncomplete, bool isHardSel // Next, we sort the list based on the pattern matching result. matchResultsBuilder.Sort(MatchResult.SortingComparer); - // Determine if the list should be hard selected or soft selected. - var isFilterTextAllPunctuation = CompletionService.IsAllPunctuation(filterText); - - // If we only had punctuation - we set soft selection and the list to be incomplete so we get called back when the user continues typing. - // If they type something that is not punctuation, we may need to update the hard vs soft selection. - // For example, typing '_' should initially be soft selection, but if the user types 'o' we should hard select '_otherVar' (if it exists). - // This isn't perfect - ideally we would make this determination every time a filter character is typed, but we do not get called back - // for typing filter characters in LSP (unless we always set isIncomplete, which is expensive). - var isHardSelection = completionList.SuggestionModeItem is null && !isFilterTextAllPunctuation; - var isIncomplete = isFilterTextAllPunctuation; - - // If our completion list hasn't hit the max size, we don't need to do anything filtering - if (completionListMaxSize < 0 || completionListMaxSize >= completionList.ItemsList.Count) - return (completionList, isIncomplete, isHardSelection); - // Finally, truncate the list to 1000 items plus any preselected items that occur after the first 1000. var filteredList = matchResultsBuilder .Take(completionListMaxSize) From 5d355f603fea5193e055af58083966c73cbb5aa8 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Wed, 30 Oct 2024 12:49:29 +0100 Subject: [PATCH 129/508] Fix ref analysis of self-assignment (#75618) * Fix ref analysis of self-assignment * Update tests * Extend tests * Add a test variant --- .../Portable/Binder/Binder.ValueChecks.cs | 5 - .../Test/Emit3/RefStructInterfacesTests.cs | 34 +++++ .../Semantic/Semantics/RefEscapingTests.cs | 132 ++++++++++++++++++ .../Test/Semantic/Semantics/RefFieldTests.cs | 20 ++- 4 files changed, 185 insertions(+), 6 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index aea6580634b3a..7cd80faf644bc 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -2920,11 +2920,6 @@ private bool CheckInvocationArgMixingWithUpdatedRules( var toArgEscape = GetValEscape(mixableArg.Argument, scopeOfTheContainingExpression); foreach (var (fromParameter, fromArg, escapeKind, isRefEscape) in escapeValues) { - if (mixableArg.Parameter is not null && object.ReferenceEquals(mixableArg.Parameter, fromParameter)) - { - continue; - } - // This checks to see if the EscapeValue could ever be assigned to this argument based // on comparing the EscapeLevel of both. If this could never be assigned due to // this then we don't need to consider it for MAMM analysis. diff --git a/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs b/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs index aafcdb5dc3db6..ec891680f6a61 100644 --- a/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs @@ -26475,6 +26475,40 @@ static ref S F2(S x2) } }"; + var comp = CreateCompilation(source, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics); + comp.VerifyEmitDiagnostics( + // (12,26): error CS8350: This combination of arguments to 'Program.F1(ref S)' is disallowed because it may expose variables referenced by parameter 'x1' outside of their declaration scope + // ref var y2 = ref F1(ref x2); + Diagnostic(ErrorCode.ERR_CallArgMixing, "F1(ref x2)").WithArguments("Program.F1(ref S)", "x1").WithLocation(12, 26), + // (12,33): error CS8166: Cannot return a parameter by reference 'x2' because it is not a ref parameter + // ref var y2 = ref F1(ref x2); + Diagnostic(ErrorCode.ERR_RefReturnParameter, "x2").WithArguments("x2").WithLocation(12, 33), + // (13,20): error CS8157: Cannot return 'y2' by reference because it was initialized to a value that cannot be returned by reference + // return ref y2; // 1 + Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "y2").WithArguments("y2").WithLocation(13, 20) + ); + } + + [Fact] + public void ReturnRefToByValueParameter_02() + { + var source = +@" +using System.Diagnostics.CodeAnalysis; + +class Program where S : allows ref struct +{ + static ref S F1(ref S x1) + { + return ref x1; + } + static ref S F2(S x2) + { + ref var y2 = ref F1(ref x2); + return ref y2; // 1 + } +}"; + var comp = CreateCompilation(source, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics); comp.VerifyEmitDiagnostics( // (13,20): error CS8157: Cannot return 'y2' by reference because it was initialized to a value that cannot be returned by reference diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs index 96fe30c45dc78..c0c092edba74b 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs @@ -10226,5 +10226,137 @@ public void Utf8Addition() """; CreateCompilation(code, targetFramework: TargetFramework.Net70).VerifyDiagnostics(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75592")] + public void SelfAssignment_ReturnOnly() + { + var source = """ + S s = default; + S.M(ref s); + + ref struct S + { + int field; + ref int refField; + + public static void M(ref S s) + { + s.refField = ref s.field; + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics( + // (11,9): error CS9079: Cannot ref-assign 's.field' to 'refField' because 's.field' can only escape the current method through a return statement. + // s.refField = ref s.field; + Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "s.refField = ref s.field").WithArguments("refField", "s.field").WithLocation(11, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75592")] + public void SelfAssignment_CallerContext() + { + var source = """ + using System.Diagnostics.CodeAnalysis; + + S s = default; + S.M(ref s); + + ref struct S + { + int field; + ref int refField; + + public static void M([UnscopedRef] ref S s) + { + s.refField = ref s.field; + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics( + // (4,1): error CS8350: This combination of arguments to 'S.M(ref S)' is disallowed because it may expose variables referenced by parameter 's' outside of their declaration scope + // S.M(ref s); + Diagnostic(ErrorCode.ERR_CallArgMixing, "S.M(ref s)").WithArguments("S.M(ref S)", "s").WithLocation(4, 1), + // (4,9): error CS8168: Cannot return local 's' by reference because it is not a ref local + // S.M(ref s); + Diagnostic(ErrorCode.ERR_RefReturnLocal, "s").WithArguments("s").WithLocation(4, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75592")] + public void SelfAssignment_CallerContext_StructReceiver() + { + var source = """ + using System.Diagnostics.CodeAnalysis; + + S s = default; + s.M(); + + ref struct S + { + int field; + ref int refField; + + [UnscopedRef] public void M() + { + this.refField = ref this.field; + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics( + // (13,9): error CS9079: Cannot ref-assign 'this.field' to 'refField' because 'this.field' can only escape the current method through a return statement. + // this.refField = ref this.field; + Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "this.refField = ref this.field").WithArguments("refField", "this.field").WithLocation(13, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75592")] + public void SelfAssignment_CallerContext_Out() + { + var source = """ + using System.Diagnostics.CodeAnalysis; + + S s = default; + S.M(out s); + + ref struct S + { + int field; + ref int refField; + + public static void M([UnscopedRef] out S s) + { + s = default; + s.refField = ref s.field; + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics( + // (4,1): error CS8350: This combination of arguments to 'S.M(out S)' is disallowed because it may expose variables referenced by parameter 's' outside of their declaration scope + // S.M(out s); + Diagnostic(ErrorCode.ERR_CallArgMixing, "S.M(out s)").WithArguments("S.M(out S)", "s").WithLocation(4, 1), + // (4,9): error CS8168: Cannot return local 's' by reference because it is not a ref local + // S.M(out s); + Diagnostic(ErrorCode.ERR_RefReturnLocal, "s").WithArguments("s").WithLocation(4, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75592")] + public void SelfAssignment_CallerContext_Scoped() + { + var source = """ + using System.Diagnostics.CodeAnalysis; + + scoped S s = default; + S.M(ref s); + + ref struct S + { + int field; + ref int refField; + + public static void M([UnscopedRef] ref S s) + { + s.refField = ref s.field; + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics(); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 762d9f656e705..5e094ed174443 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -9757,6 +9757,12 @@ static ref S F2(S x2) else { comp.VerifyEmitDiagnostics( + // (14,26): error CS8350: This combination of arguments to 'Program.F1(ref S)' is disallowed because it may expose variables referenced by parameter 'x1' outside of their declaration scope + // ref var y2 = ref F1(ref x2); + Diagnostic(ErrorCode.ERR_CallArgMixing, "F1(ref x2)").WithArguments("Program.F1(ref S)", "x1").WithLocation(14, 26), + // (14,33): error CS8166: Cannot return a parameter by reference 'x2' because it is not a ref parameter + // ref var y2 = ref F1(ref x2); + Diagnostic(ErrorCode.ERR_RefReturnParameter, "x2").WithArguments("x2").WithLocation(14, 33), // (15,20): error CS8157: Cannot return 'y2' by reference because it was initialized to a value that cannot be returned by reference // return ref y2; // 1 Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "y2").WithArguments("y2").WithLocation(15, 20)); @@ -29523,9 +29529,15 @@ private ref struct RefStruct // (9,9): error CS8374: Cannot ref-assign 'value' to 'RefField' because 'value' has a narrower escape scope than 'RefField'. // GetReference2(in s1).RefField = ref value; // 2 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "GetReference2(in s1).RefField = ref value").WithArguments("RefField", "value").WithLocation(9, 9), + // (10,9): error CS8350: This combination of arguments to 'Repro.GetReference3(out Repro.RefStruct)' is disallowed because it may expose variables referenced by parameter 'rs' outside of their declaration scope + // GetReference3(out s1).RefField = ref value; // 3 + Diagnostic(ErrorCode.ERR_CallArgMixing, "GetReference3(out s1)").WithArguments("Repro.GetReference3(out Repro.RefStruct)", "rs").WithLocation(10, 9), // (10,9): error CS8374: Cannot ref-assign 'value' to 'RefField' because 'value' has a narrower escape scope than 'RefField'. // GetReference3(out s1).RefField = ref value; // 3 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "GetReference3(out s1).RefField = ref value").WithArguments("RefField", "value").WithLocation(10, 9), + // (10,27): error CS8168: Cannot return local 's1' by reference because it is not a ref local + // GetReference3(out s1).RefField = ref value; // 3 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "s1").WithArguments("s1").WithLocation(10, 27), // (12,9): error CS8332: Cannot assign to a member of method 'GetReadonlyReference1' or use it as the right hand side of a ref assignment because it is a readonly variable // GetReadonlyReference1(ref s1).RefField = ref value; // 4 Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "GetReadonlyReference1(ref s1).RefField").WithArguments("method", "GetReadonlyReference1").WithLocation(12, 9), @@ -29534,7 +29546,13 @@ private ref struct RefStruct Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "GetReadonlyReference2(in s1).RefField").WithArguments("method", "GetReadonlyReference2").WithLocation(13, 9), // (14,9): error CS8332: Cannot assign to a member of method 'GetReadonlyReference3' or use it as the right hand side of a ref assignment because it is a readonly variable // GetReadonlyReference3(out s1).RefField = ref value; // 6 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "GetReadonlyReference3(out s1).RefField").WithArguments("method", "GetReadonlyReference3").WithLocation(14, 9)); + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "GetReadonlyReference3(out s1).RefField").WithArguments("method", "GetReadonlyReference3").WithLocation(14, 9), + // (14,9): error CS8350: This combination of arguments to 'Repro.GetReadonlyReference3(out Repro.RefStruct)' is disallowed because it may expose variables referenced by parameter 'rs' outside of their declaration scope + // GetReadonlyReference3(out s1).RefField = ref value; // 6 + Diagnostic(ErrorCode.ERR_CallArgMixing, "GetReadonlyReference3(out s1)").WithArguments("Repro.GetReadonlyReference3(out Repro.RefStruct)", "rs").WithLocation(14, 9), + // (14,35): error CS8168: Cannot return local 's1' by reference because it is not a ref local + // GetReadonlyReference3(out s1).RefField = ref value; // 6 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "s1").WithArguments("s1").WithLocation(14, 35)); } [WorkItem(66128, "https://github.com/dotnet/roslyn/issues/66128")] From 2c2130be39666b7a97a4f26f5784f5fa407417fc Mon Sep 17 00:00:00 2001 From: Rekkonnect <8298332+Rekkonnect@users.noreply.github.com> Date: Wed, 30 Oct 2024 14:09:30 +0200 Subject: [PATCH 130/508] Add symbol display tests --- .../SymbolDisplay/SymbolDisplayTests.cs | 32 +++++++++++++++++++ .../SymbolDisplay/SymbolDisplayTests.vb | 31 ++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs b/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs index 8b6b16d4ef626..5bd36448cc083 100644 --- a/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs @@ -8837,5 +8837,37 @@ void M() SymbolDisplayPartKind.ParameterName, SymbolDisplayPartKind.Punctuation); } + + [Fact, WorkItem(66009, "https://github.com/dotnet/roslyn/issues/66009")] + public void PreprocessingSymbol() + { + var source = """ + #if NET5_0_OR_GREATER + #endif + """; + + var comp = CreateCompilation(source); + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var preprocessingNameSyntax = tree.GetRoot().DescendantNodes(descendIntoTrivia: true) + .OfType().First(); + var preprocessingSymbolInfo = model.GetPreprocessingSymbolInfo(preprocessingNameSyntax); + var preprocessingSymbol = preprocessingSymbolInfo.Symbol; + + var format = new SymbolDisplayFormat( + memberOptions: SymbolDisplayMemberOptions.IncludeParameters, + parameterOptions: SymbolDisplayParameterOptions.IncludeType | + SymbolDisplayParameterOptions.IncludeName | + SymbolDisplayParameterOptions.IncludeDefaultValue); + + Assert.Equal("NET5_0_OR_GREATER", preprocessingSymbol.ToDisplayString(format)); + + var displayParts = preprocessingSymbol.ToDisplayParts(format); + AssertEx.Equal( + expected: [ + new SymbolDisplayPart(SymbolDisplayPartKind.Text, preprocessingSymbol, "NET5_0_OR_GREATER") + ], + actual: displayParts); + } } } diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb index e67cb60331d87..c6a271ba02d81 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb @@ -6001,6 +6001,37 @@ end class" SymbolDisplayPartKind.Punctuation) End Sub + + Public Sub PreprocessingSymbol() + Dim source = +" +#If NET5_0_OR_GREATER +#End If" + Dim format = New SymbolDisplayFormat( + memberOptions:=SymbolDisplayMemberOptions.IncludeParameters Or SymbolDisplayMemberOptions.IncludeType Or SymbolDisplayMemberOptions.IncludeModifiers, + miscellaneousOptions:=SymbolDisplayMiscellaneousOptions.UseSpecialTypes) + + Dim comp = CreateCompilation(source) + Dim tree = comp.SyntaxTrees.First() + Dim model = comp.GetSemanticModel(tree) + Dim preprocessingNameSyntax = tree.GetRoot().DescendantNodes(descendIntoTrivia:=True).OfType(Of IdentifierNameSyntax).First() + Dim preprocessingSymbolInfo = model.GetPreprocessingSymbolInfo(preprocessingNameSyntax) + Dim preprocessingSymbol = preprocessingSymbolInfo.Symbol + + Assert.Equal( + "NET5_0_OR_GREATER", + SymbolDisplay.ToDisplayString(preprocessingSymbol, format)) + + Dim displayParts = preprocessingSymbol.ToDisplayParts(format) + Dim expectedDisplayParts = + { + New SymbolDisplayPart(SymbolDisplayPartKind.Text, preprocessingSymbol, "NET5_0_OR_GREATER") + } + Assert.Equal( + expected:=expectedDisplayParts, + actual:=displayParts) + End Sub + #Region "Helpers" Private Shared Sub TestSymbolDescription( From ba1a55134f997877357c061cb0cba8dd5fdbb0f6 Mon Sep 17 00:00:00 2001 From: Rekkonnect <8298332+Rekkonnect@users.noreply.github.com> Date: Wed, 30 Oct 2024 14:42:27 +0200 Subject: [PATCH 131/508] Add tests for CanBeReferencedByName --- .../Compilation/SemanticModelAPITests.cs | 18 ++++++++++++++++++ .../Compilation/SemanticModelAPITests.vb | 19 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs index 9c568f9d76dcb..9bc25adb50ba3 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs @@ -4745,6 +4745,24 @@ class C(out Type[M2(out object y)]) Assert.Null(model.GetAliasInfo(identifier)); } + [Fact] + public void CommonPreprocessingSymbolProperties() + { + var text = """ + #if NET5_0_OR_GREATER + #endif + """; + var compilation = CreateCompilation(text); + + var tree = compilation.SyntaxTrees.Single(); + var identifier = tree.GetRoot().DescendantNodes(descendIntoTrivia: true).OfType().First(); + var model = compilation.GetSemanticModel(tree); + var preprocessingSymbol = model.GetPreprocessingSymbolInfo(identifier).Symbol; + Assert.NotNull(preprocessingSymbol); + Assert.Equal("NET5_0_OR_GREATER", preprocessingSymbol.Name); + Assert.True(preprocessingSymbol.CanBeReferencedByName); + } + #region "regression helper" private void Regression(string text) { diff --git a/src/Compilers/VisualBasic/Test/Semantic/Compilation/SemanticModelAPITests.vb b/src/Compilers/VisualBasic/Test/Semantic/Compilation/SemanticModelAPITests.vb index b91a3444a2145..927edb2770b6e 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Compilation/SemanticModelAPITests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Compilation/SemanticModelAPITests.vb @@ -4897,5 +4897,24 @@ BC30990: Member 'Key' cannot be initialized in an object initializer expression ) End Sub + + Public Sub CommonPreprocessingSymbolProperties() + Dim compilation = CompilationUtils.CreateCompilationWithMscorlib40( + + +#If NET5_0_OR_GREATER +#End If + +) + + Dim tree = CompilationUtils.GetTree(compilation, "a.vb") + Dim semanticModel = compilation.GetSemanticModel(tree) + Dim node = tree.GetCompilationUnitRoot().DescendantNodes(descendIntoTrivia:=True).OfType(Of IdentifierNameSyntax).First() + Dim symbol = semanticModel.GetPreprocessingSymbolInfo(node).Symbol + Assert.NotNull(symbol) + Assert.Equal("NET5_0_OR_GREATER", symbol.Name) + Assert.True(symbol.CanBeReferencedByName) + End Sub + End Class End Namespace From 5ab7f5b48ecad900a2d899a047d53f0ee0c388fa Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Wed, 30 Oct 2024 08:05:18 -0700 Subject: [PATCH 132/508] Reduce allocations in TextDocumentStates.AddRange (#75640) This method was calling ImmutableList.AddRange giving it the result of a Select linq expression. The underlying ImmutableList code doesn't end up special casing that collection type, and thus creates a FallbackWrapper around the enumeration. The FallbackWrapper.Count method uses Enumerable.ToArray to cache a collection object, which ends up 1) doing the double resize allocation dance pass until done 2) resizing that final array This essentially means we've allocated around 3x the amount of memory needed to hold our documentids. Instead, if we use a pooled list, we can generally avoid the allocations completely (the ImmutableList code instead creates a ListOfTWrapper around our List which doesn't need to allocate) This method is showing up as around 0.8% of allocations in the typing section of the csharp editing speedometer test. --- .../Workspace/Solution/TextDocumentStates.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentStates.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentStates.cs index 33ead1daf977d..1e5f9513360dc 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentStates.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentStates.cs @@ -138,9 +138,18 @@ public ImmutableArray SelectAsArray(Func AddRange(ImmutableArray states) - => new(_ids.AddRange(states.Select(state => state.Id)), - States.AddRange(states.Select(state => KeyValuePairUtil.Create(state.Id, state))), - filePathToDocumentIds: null); + { + using var pooledIds = SharedPools.Default>().GetPooledObject(); + var ids = pooledIds.Object; + + foreach (var state in states) + ids.Add(state.Id); + + return new( + _ids.AddRange(ids), + States.AddRange(states.Select(state => KeyValuePairUtil.Create(state.Id, state))), + filePathToDocumentIds: null); + } public TextDocumentStates RemoveRange(ImmutableArray ids) { From 308cda3f583adb7d17c298da64f3019730100e6c Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Wed, 30 Oct 2024 10:01:55 -0700 Subject: [PATCH 133/508] Reduce allocations in VirtualCharService.CreateVirtualCharSequence (#75654) This method shows up in OOP allocations in the scrolling speedometer test. This method is invoked to create runes for text sequences for strings. The standard case is that there aren't any multi-char runes in a string, in which case AbstractVirtualCharService.CreateVirtualCharSequence doesn't end up using the ImmutableSegmentedList in the result it creates. This means that we can reuse this builder and it's underlying array after it's been cleared out. This shows up as around 2.0% of allocations in out OOP in the scrolling speedometer test. --- .../VirtualChars/CSharpVirtualCharService.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs index 11f2d9ceff053..991dbfe11d3c8 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs @@ -23,6 +23,8 @@ internal class CSharpVirtualCharService : AbstractVirtualCharService { public static readonly IVirtualCharService Instance = new CSharpVirtualCharService(); + private static readonly ObjectPool.Builder> s_pooledBuilders = new(() => ImmutableSegmentedList.CreateBuilder()); + protected CSharpVirtualCharService() { } @@ -285,11 +287,20 @@ private static VirtualCharSequence CreateVirtualCharSequence( string tokenText, int offset, int startIndexInclusive, int endIndexExclusive, ArrayBuilder<(char ch, TextSpan span)> charResults) { // Second pass. Convert those characters to Runes. - var runeResults = ImmutableSegmentedList.CreateBuilder(); + using var pooledRuneResults = s_pooledBuilders.GetPooledObject(); + var runeResults = pooledRuneResults.Object; - ConvertCharactersToRunes(charResults, runeResults); + try + { + ConvertCharactersToRunes(charResults, runeResults); - return CreateVirtualCharSequence(tokenText, offset, startIndexInclusive, endIndexExclusive, runeResults); + return CreateVirtualCharSequence(tokenText, offset, startIndexInclusive, endIndexExclusive, runeResults); + } + finally + { + // Ensure the builder is cleared out before releasing back to the pool. + runeResults.Clear(); + } } private static void ConvertCharactersToRunes(ArrayBuilder<(char ch, TextSpan span)> charResults, ImmutableSegmentedList.Builder runeResults) From 9e862f15fc4d57a479ac45724748a41c87a7d0b6 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Wed, 30 Oct 2024 15:13:16 -0700 Subject: [PATCH 134/508] Bind lambda in `yield return` despite errors (#75660) --- .../Portable/Binder/Binder_Statements.cs | 4 +- .../Emit/CodeGen/CodeGenAsyncIteratorTests.cs | 136 ++++++++++++++++++ .../Semantics/PrimaryConstructorTests.cs | 5 +- 3 files changed, 142 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 0b4c03c795406..fe05583f0f347 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -239,7 +239,7 @@ private BoundStatement BindYieldReturnStatement(YieldStatementSyntax node, Bindi ? BadExpression(node).MakeCompilerGenerated() : BindValue(node.Expression, diagnostics, BindValueKind.RValue); - if (!argument.HasAnyErrors) + if (!argument.HasErrors && ((object)argument.Type == null || !argument.Type.IsErrorType())) { argument = GenerateConversionForAssignment(elementType, argument, diagnostics); } @@ -3154,7 +3154,7 @@ internal BoundExpression CreateReturnConversion( diagnostics.Add(syntax, useSiteInfo); - if (!argument.HasAnyErrors) + if (!argument.HasAnyErrors || argument.Kind == BoundKind.UnboundLambda) { if (returnRefKind != RefKind.None) { diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs index a607dbf9aa094..3dd1da3b37283 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs @@ -11,6 +11,7 @@ using System.Text; using Basic.Reference.Assemblies; using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; @@ -8672,5 +8673,140 @@ static async System.Threading.Tasks.Task Main() comp2.VerifyEmitDiagnostics(); // Indirectly calling IsMetadataVirtual on S.DisposeAsync (a read which causes the lock to be set) comp1.VerifyEmitDiagnostics(); // Would call EnsureMetadataVirtual on S.DisposeAsync and would therefore assert if S was not already ForceCompleted } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68027")] + public void LambdaWithBindingErrorInYieldReturn() + { + var src = """ +#pragma warning disable CS1998 // This async method lacks 'await' operators and will run synchronously +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +class C +{ + static async IAsyncEnumerable>> BarAsync() + { + yield return async s => + { + await Task.CompletedTask; + throw new NotImplementedException(); + }; + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); + comp.VerifyDiagnostics(); + + src = """ +#pragma warning disable CS1998 // This async method lacks 'await' operators and will run synchronously +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +class C +{ + static async IAsyncEnumerable>> BarAsync() + { + yield return async s => + { + s // 1 + await Task.CompletedTask; + throw new NotImplementedException(); + }; + } +} +"""; + comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); + + comp.VerifyDiagnostics( + // (12,13): error CS0118: 's' is a variable but is used like a type + // s // 1 + Diagnostic(ErrorCode.ERR_BadSKknown, "s").WithArguments("s", "variable", "type").WithLocation(12, 13), + // (13,13): error CS4003: 'await' cannot be used as an identifier within an async method or lambda expression + // await Task.CompletedTask; + Diagnostic(ErrorCode.ERR_BadAwaitAsIdentifier, "await").WithLocation(13, 13), + // (13,13): warning CS0168: The variable 'await' is declared but never used + // await Task.CompletedTask; + Diagnostic(ErrorCode.WRN_UnreferencedVar, "await").WithArguments("await").WithLocation(13, 13), + // (13,19): error CS1002: ; expected + // await Task.CompletedTask; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "Task").WithLocation(13, 19), + // (13,19): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // await Task.CompletedTask; + Diagnostic(ErrorCode.ERR_IllegalStatement, "Task.CompletedTask").WithLocation(13, 19)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var s = GetSyntax(tree, "s"); + Assert.Null(model.GetSymbolInfo(s).Symbol); + Assert.Equal(new[] { "System.String s" }, model.GetSymbolInfo(s).CandidateSymbols.ToTestDisplayStrings()); + } + + [Fact] + public void LambdaWithBindingErrorInReturn() + { + var src = """ +#pragma warning disable CS1998 // This async method lacks 'await' operators and will run synchronously +using System; +using System.Threading.Tasks; + +class C +{ + static async Task>> BarAsync() + { + return async s => + { + await Task.CompletedTask; + throw new NotImplementedException(); + }; + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); + comp.VerifyDiagnostics(); + + src = """ +#pragma warning disable CS1998 // This async method lacks 'await' operators and will run synchronously +using System; +using System.Threading.Tasks; + +class C +{ + static async Task>> BarAsync() + { + return async s => + { + s // 1 + await Task.CompletedTask; + throw new NotImplementedException(); + }; + } +} +"""; + comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); + comp.VerifyDiagnostics( + // (11,13): error CS0118: 's' is a variable but is used like a type + // s // 1 + Diagnostic(ErrorCode.ERR_BadSKknown, "s").WithArguments("s", "variable", "type").WithLocation(11, 13), + // (12,13): error CS4003: 'await' cannot be used as an identifier within an async method or lambda expression + // await Task.CompletedTask; + Diagnostic(ErrorCode.ERR_BadAwaitAsIdentifier, "await").WithLocation(12, 13), + // (12,13): warning CS0168: The variable 'await' is declared but never used + // await Task.CompletedTask; + Diagnostic(ErrorCode.WRN_UnreferencedVar, "await").WithArguments("await").WithLocation(12, 13), + // (12,19): error CS1002: ; expected + // await Task.CompletedTask; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "Task").WithLocation(12, 19), + // (12,19): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // await Task.CompletedTask; + Diagnostic(ErrorCode.ERR_IllegalStatement, "Task.CompletedTask").WithLocation(12, 19)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var s = GetSyntax(tree, "s"); + Assert.Null(model.GetSymbolInfo(s).Symbol); + Assert.Equal(new[] { "System.String s" }, model.GetSymbolInfo(s).CandidateSymbols.ToTestDisplayStrings()); + } } } diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/PrimaryConstructorTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/PrimaryConstructorTests.cs index ef70e6a583910..3559b208e34e7 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/PrimaryConstructorTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/PrimaryConstructorTests.cs @@ -9540,7 +9540,10 @@ class C1 (int p1) comp1.VerifyEmitDiagnostics( // (4,38): error CS1041: Identifier expected; 'delegate' is a keyword // public System.Func M21() => delegate => p1; - Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "delegate").WithArguments("", "delegate").WithLocation(4, 38) + Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "delegate").WithArguments("", "delegate").WithLocation(4, 38), + // (4,47): error CS1593: Delegate 'Func' does not take 1 arguments + // public System.Func M21() => delegate => p1; + Diagnostic(ErrorCode.ERR_BadDelArgCount, "=>").WithArguments("System.Func", "1").WithLocation(4, 47) ); var source = @" From 1fde068b9cd69a472d2aa0be01fd75f290ad4de9 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Wed, 30 Oct 2024 15:35:10 -0700 Subject: [PATCH 135/508] Detect struct cycles caused by erroneous field-like event declarations (#75668) Fixes #75620. --- .../Source/SourceMemberContainerSymbol.cs | 37 ++++++++++++++++--- .../Emit3/FlowAnalysis/FlowDiagnosticTests.cs | 13 ++++++- .../Semantics/GenericConstraintsTests.cs | 24 +++++++----- 3 files changed, 57 insertions(+), 17 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index 8e64bfd4ed0ae..f94f39639f149 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -2380,13 +2380,23 @@ private bool HasStructCircularity(BindingDiagnosticBag diagnostics) { foreach (var member in valuesByName) { - if (member.Kind != SymbolKind.Field) + FieldSymbol? field; + + // Only instance fields (including field-like events) affect the outcome. + switch (member.Kind) { - // NOTE: don't have to check field-like events, because they can't have struct types. - continue; + case SymbolKind.Field: + field = (FieldSymbol)member; + Debug.Assert(field.AssociatedSymbol is not EventSymbol, "Didn't expect to find a field-like event backing field in the member list."); + break; + case SymbolKind.Event: + field = ((EventSymbol)member).AssociatedField; + break; + default: + continue; } - var field = (FieldSymbol)member; - if (field.IsStatic) + + if (field is null || field.IsStatic) { continue; } @@ -2670,7 +2680,22 @@ private void CheckFiniteFlatteningGraph(BindingDiagnosticBag diagnostics) instanceMap.Add(this, this); foreach (var m in this.GetMembersUnordered()) { - var f = m as FieldSymbol; + FieldSymbol? f; + + // Only instance fields (including field-like events) affect the outcome. + switch (m.Kind) + { + case SymbolKind.Field: + f = (FieldSymbol)m; + Debug.Assert(f.AssociatedSymbol is not EventSymbol, "Didn't expect to find a field-like event backing field in the member list."); + break; + case SymbolKind.Event: + f = ((EventSymbol)m).AssociatedField; + break; + default: + continue; + } + if (f is null || !f.IsStatic || f.Type.TypeKind != TypeKind.Struct) continue; var type = (NamedTypeSymbol)f.Type; if (InfiniteFlatteningGraph(this, type, instanceMap)) diff --git a/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/FlowDiagnosticTests.cs b/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/FlowDiagnosticTests.cs index 4a8797b493957..a85ac73bb0e85 100644 --- a/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/FlowDiagnosticTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/FlowDiagnosticTests.cs @@ -964,6 +964,11 @@ struct D { static C> x; } +#pragma warning disable CS0067 // The event 'E.x' is never used +struct E +{ + static event E> x; +} "; CreateCompilation(program) .VerifyDiagnostics( @@ -987,7 +992,13 @@ struct D Diagnostic(ErrorCode.WRN_UnreferencedField, "x").WithArguments("D.x").WithLocation(18, 20), // (14,17): warning CS0169: The field 'C.x' is never used // static D x; - Diagnostic(ErrorCode.WRN_UnreferencedField, "x").WithArguments("C.x").WithLocation(14, 17) + Diagnostic(ErrorCode.WRN_UnreferencedField, "x").WithArguments("C.x").WithLocation(14, 17), + // (23,26): error CS0523: Struct member 'E.x' of type 'E>' causes a cycle in the struct layout + // static event E> x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("E.x", "E>").WithLocation(23, 26), + // (23,26): error CS0066: 'E.x': event must be of a delegate type + // static event E> x; + Diagnostic(ErrorCode.ERR_EventNotDelegate, "x").WithArguments("E.x").WithLocation(23, 26) ); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/GenericConstraintsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/GenericConstraintsTests.cs index 69fc5fd116813..cac8e9be0826b 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/GenericConstraintsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/GenericConstraintsTests.cs @@ -3773,7 +3773,7 @@ public struct YourStruct where T : unmanaged Assert.False(compilation.GetMember("YourStruct").IsManagedTypeNoUseSiteDiagnostics); } - [Fact(Skip = "https://github.com/dotnet/roslyn/issues/75620")] + [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/75620")] public void UnmanagedExpandingTypeArgumentConstraintViolation_05() { @@ -3794,6 +3794,9 @@ public struct YourStruct where T : unmanaged var compilation = CreateCompilation(code, options: TestOptions.UnsafeReleaseDll); compilation.VerifyDiagnostics( + // (6,52): error CS0523: Struct member 'MyStruct.field' of type 'YourStruct>>' causes a cycle in the struct layout + // public event YourStruct>> field; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "field").WithArguments("MyStruct.field", "YourStruct>>").WithLocation(6, 52), // (6,52): error CS0066: 'MyStruct.field': event must be of a delegate type // public event YourStruct>> field; Diagnostic(ErrorCode.ERR_EventNotDelegate, "field").WithArguments("MyStruct.field").WithLocation(6, 52), @@ -3806,7 +3809,7 @@ public struct YourStruct where T : unmanaged Assert.False(compilation.GetMember("YourStruct").IsManagedTypeNoUseSiteDiagnostics); } - [Fact(Skip = "https://github.com/dotnet/roslyn/issues/75620")] + [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/75620")] public void UnmanagedExpandingTypeArgumentConstraintViolation_06() { @@ -3826,15 +3829,16 @@ public struct YourStruct where T : unmanaged "; var compilation = CreateCompilation(code, options: TestOptions.UnsafeReleaseDll); compilation.VerifyDiagnostics( - // (4,52): error CS0066: 'MyStruct.field': event must be of a delegate type + // (7,52): error CS0523: Struct member 'MyStruct.field' of type 'YourStruct>>' causes a cycle in the struct layout // public event YourStruct>> field; - Diagnostic(ErrorCode.ERR_EventNotDelegate, "field").WithArguments("MyStruct.field").WithLocation(4, 52), - // (5,46): error CS0523: Struct member 'MyStruct.field' of type 'YourStruct>>' causes a cycle in the struct layout - // public YourStruct>> field; - Diagnostic(ErrorCode.ERR_StructLayoutCycle, "field").WithArguments("MyStruct.field", "YourStruct>>").WithLocation(5, 46), - // (5,46): error CS8377: The type 'MyStruct>' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'YourStruct' - // public YourStruct>> field; - Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "field").WithArguments("YourStruct", "T", "MyStruct>").WithLocation(5, 46)); + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "field").WithArguments("MyStruct.field", "YourStruct>>").WithLocation(7, 52), + // (7,52): error CS0066: 'MyStruct.field': event must be of a delegate type + // public event YourStruct>> field; + Diagnostic(ErrorCode.ERR_EventNotDelegate, "field").WithArguments("MyStruct.field").WithLocation(7, 52), + // (7,52): error CS8377: The type 'MyStruct>' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'YourStruct' + // public event YourStruct>> field; + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "field").WithArguments("YourStruct", "T", "MyStruct>").WithLocation(7, 52) + ); Assert.True(compilation.GetMember("MyStruct").IsManagedTypeNoUseSiteDiagnostics); Assert.False(compilation.GetMember("YourStruct").IsManagedTypeNoUseSiteDiagnostics); From 455196fc2558d9f446d8aef53a58039ffb45147a Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Thu, 31 Oct 2024 11:29:15 +0100 Subject: [PATCH 136/508] Allow suppressing foreach iteration variable nullability mismatch (#75432) * Allow suppressing foreach iteration variable * Unify `suppressed` local and parameter --- .../CSharp/Portable/FlowAnalysis/NullableWalker.cs | 7 ++++--- .../Test/Semantic/Semantics/NullableReferenceTypesTests.cs | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 24273987d3d96..9aaf4199c44be 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -244,7 +244,7 @@ internal string GetDebuggerDisplay() /// /// Map from a target-typed expression (such as a target-typed conditional, switch or new) to the delegate /// that completes analysis once the target type is known. - /// The delegate is invoked by . + /// The delegate is invoked by . /// private PooledDictionary> TargetTypedAnalysisCompletion => _targetTypedAnalysisCompletionOpt ??= PooledDictionary>.GetInstance(); @@ -8703,6 +8703,7 @@ private TypeWithState VisitConversion( ParameterSymbol? parameterOpt = null, bool reportTopLevelWarnings = true, bool reportRemainingWarnings = true, + bool isSuppressed = false, bool extensionMethodThisArgument = false, Optional stateForLambda = default, bool trackMembers = false, @@ -8742,9 +8743,8 @@ private TypeWithState VisitConversion( NullableFlowState resultState = NullableFlowState.NotNull; bool canConvertNestedNullability = true; - bool isSuppressed = false; - if (conversionOperand.IsSuppressed == true) + if (isSuppressed || conversionOperand.IsSuppressed) { reportTopLevelWarnings = false; reportRemainingWarnings = false; @@ -11056,6 +11056,7 @@ public override void VisitForEachIterationVariables(BoundForEachStatement node) AssignmentKind.ForEachIterationVariable, reportTopLevelWarnings: true, reportRemainingWarnings: true, + isSuppressed: node.Expression is BoundConversion { Operand.IsSuppressed: true }, diagnosticLocation: variableLocation); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index e2a7bc8b97f0d..600b2098f4800 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -90888,7 +90888,7 @@ static class Extensions comp.VerifyDiagnostics(); } - [Fact] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75430")] public void Foreach_NullableElementType_Suppression() { var source = """ @@ -90896,7 +90896,7 @@ public void Foreach_NullableElementType_Suppression() class C { void M1(object?[] a) { foreach (object item in a) { } } - void M2(object[] a) { foreach (object item in a!) { } } + void M2(object?[] a) { foreach (object item in a!) { } } } """; CreateCompilation(source).VerifyDiagnostics( From 646d49350a1ad5c330e9e8abf1dab4a9ee010b17 Mon Sep 17 00:00:00 2001 From: Evgeny Tvorun Date: Thu, 31 Oct 2024 09:23:26 -0700 Subject: [PATCH 137/508] Proffer project system query service --- .../BrokeredServices/Services/Descriptors.cs | 1 + src/Workspaces/Remote/Core/BrokeredServiceDescriptors.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/Services/Descriptors.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/Services/Descriptors.cs index 1fb946820d563..da7fbebc4ba6a 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/Services/Descriptors.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/Services/Descriptors.cs @@ -51,6 +51,7 @@ internal class Descriptors { BrokeredServiceDescriptors.MauiLaunchCustomizerService.Moniker, new ServiceRegistration(ServiceAudience.Local, null, allowGuestClients: false) }, { BrokeredServiceDescriptors.DebuggerSymbolLocatorService.Moniker, new ServiceRegistration(ServiceAudience.Local, null, allowGuestClients: false) }, { BrokeredServiceDescriptors.DebuggerSourceLinkService.Moniker, new ServiceRegistration(ServiceAudience.Local, null, allowGuestClients: false) }, + { BrokeredServiceDescriptors.ProjectSystemQueryExecutionService.Moniker, new ServiceRegistration(ServiceAudience.Local, null, allowGuestClients: false) }, }.ToImmutableDictionary(); public static ServiceJsonRpcDescriptor CreateDescriptor(ServiceMoniker serviceMoniker) => new( diff --git a/src/Workspaces/Remote/Core/BrokeredServiceDescriptors.cs b/src/Workspaces/Remote/Core/BrokeredServiceDescriptors.cs index 4e5dfd34a0838..430bdbf39b6da 100644 --- a/src/Workspaces/Remote/Core/BrokeredServiceDescriptors.cs +++ b/src/Workspaces/Remote/Core/BrokeredServiceDescriptors.cs @@ -81,6 +81,8 @@ protected override JsonRpcConnection CreateConnection(JsonRpc jsonRpc) CreateDebuggerServiceDescriptor("SymbolLocatorService", new Version(0, 1), new MultiplexingStream.Options { ProtocolMajorVersion = 3 }); public static readonly ServiceRpcDescriptor DebuggerSourceLinkService = CreateDebuggerServiceDescriptor("SourceLinkService", new Version(0, 1), new MultiplexingStream.Options { ProtocolMajorVersion = 3 }); + public static readonly ServiceRpcDescriptor ProjectSystemQueryExecutionService = + CreateDescriptor(new ServiceMoniker("Microsoft.VisualStudio.ProjectSystem.Query.Remoting.QueryExecutionService", new Version(0, 2))); public static ServiceMoniker CreateMoniker(string namespaceName, string componentName, string serviceName, Version? version) => new(namespaceName + "." + componentName + "." + serviceName, version); From 6200694f1e16db934fdd360d9a4dc0460baf172f Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Thu, 31 Oct 2024 16:08:04 -0700 Subject: [PATCH 138/508] Respect [MemberNotNull] on local functions (#75448) --- .../Portable/FlowAnalysis/NullableWalker.cs | 364 ++-- .../Semantics/NullableReferenceTypesTests.cs | 1882 ++++++++++++++++- 2 files changed, 2092 insertions(+), 154 deletions(-) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 9aaf4199c44be..a928565197eda 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -612,7 +612,7 @@ protected override ImmutableArray Scan(ref bool badRegion) { EnforceParameterNotNullOnExit(returnStatement.Syntax, pendingReturn.State); EnforceNotNullWhenForPendingReturn(pendingReturn, returnStatement); - enforceMemberNotNullWhenForPendingReturn(pendingReturn, returnStatement); + EnforceMemberNotNullWhenForPendingReturn(pendingReturn, returnStatement); } } } @@ -690,7 +690,7 @@ void enforceMemberNotNull(SyntaxNode? syntaxOpt, LocalState state) { foreach (var memberName in method.NotNullMembers) { - enforceMemberNotNullOnMember(syntaxOpt, state, method, memberName); + EnforceMemberNotNullOnMember(syntaxOpt, state, method, memberName); } method = method.OverriddenMethod; @@ -791,113 +791,6 @@ void checkMemberStateOnConstructorExit(MethodSymbol constructor, Symbol member, } } - void enforceMemberNotNullOnMember(SyntaxNode? syntaxOpt, LocalState state, MethodSymbol method, string memberName) - { - foreach (var member in method.ContainingType.GetMembers(memberName)) - { - if (memberHasBadState(member, state)) - { - // Member '{name}' must have a non-null value when exiting. - Diagnostics.Add(ErrorCode.WRN_MemberNotNull, syntaxOpt?.GetLocation() ?? methodMainNode.Syntax.GetLastToken().GetLocation(), member.Name); - } - } - } - - void enforceMemberNotNullWhenForPendingReturn(PendingBranch pendingReturn, BoundReturnStatement returnStatement) - { - if (pendingReturn.IsConditionalState) - { - if (returnStatement.ExpressionOpt is { ConstantValueOpt: { IsBoolean: true, BooleanValue: bool value } }) - { - enforceMemberNotNullWhen(returnStatement.Syntax, sense: value, pendingReturn.State); - return; - } - - if (!pendingReturn.StateWhenTrue.Reachable || !pendingReturn.StateWhenFalse.Reachable) - { - return; - } - - if (_symbol is MethodSymbol method) - { - foreach (var memberName in method.NotNullWhenTrueMembers) - { - enforceMemberNotNullWhenIfAffected(returnStatement.Syntax, sense: true, method.ContainingType.GetMembers(memberName), pendingReturn.StateWhenTrue, pendingReturn.StateWhenFalse); - } - - foreach (var memberName in method.NotNullWhenFalseMembers) - { - enforceMemberNotNullWhenIfAffected(returnStatement.Syntax, sense: false, method.ContainingType.GetMembers(memberName), pendingReturn.StateWhenFalse, pendingReturn.StateWhenTrue); - } - } - } - else if (returnStatement.ExpressionOpt is { ConstantValueOpt: { IsBoolean: true, BooleanValue: bool value } }) - { - enforceMemberNotNullWhen(returnStatement.Syntax, sense: value, pendingReturn.State); - } - } - - void enforceMemberNotNullWhenIfAffected(SyntaxNode? syntaxOpt, bool sense, ImmutableArray members, LocalState state, LocalState otherState) - { - foreach (var member in members) - { - // For non-constant values, only complain if we were able to analyze a difference for this member between two branches - if (memberHasBadState(member, state) != memberHasBadState(member, otherState)) - { - reportMemberIfBadConditionalState(syntaxOpt, sense, member, state); - } - } - } - - void enforceMemberNotNullWhen(SyntaxNode? syntaxOpt, bool sense, LocalState state) - { - if (_symbol is MethodSymbol method) - { - var notNullMembers = sense ? method.NotNullWhenTrueMembers : method.NotNullWhenFalseMembers; - foreach (var memberName in notNullMembers) - { - foreach (var member in method.ContainingType.GetMembers(memberName)) - { - reportMemberIfBadConditionalState(syntaxOpt, sense, member, state); - } - } - } - } - - void reportMemberIfBadConditionalState(SyntaxNode? syntaxOpt, bool sense, Symbol member, LocalState state) - { - if (memberHasBadState(member, state)) - { - // Member '{name}' must have a non-null value when exiting with '{sense}'. - Diagnostics.Add(ErrorCode.WRN_MemberNotNullWhen, syntaxOpt?.GetLocation() ?? methodMainNode.Syntax.GetLastToken().GetLocation(), member.Name, sense ? "true" : "false"); - } - } - - bool memberHasBadState(Symbol member, LocalState state) - { - switch (member.Kind) - { - case SymbolKind.Field: - case SymbolKind.Property: - if (getSlotForFieldOrPropertyOrEvent(member) is int memberSlot && - memberSlot > 0) - { - var parameterState = GetState(ref state, memberSlot); - return !parameterState.IsNotNull(); - } - else - { - return false; - } - - case SymbolKind.Event: - case SymbolKind.Method: - break; - } - - return false; - } - void makeNotNullMembersMaybeNull() { if (_symbol is MethodSymbol method) @@ -939,7 +832,7 @@ void makeNotNullMembersMaybeNull() default: break; } - var memberSlot = getSlotForFieldOrPropertyOrEvent(memberToInitialize); + var memberSlot = GetSlotForMemberPostCondition(memberToInitialize); if (memberSlot > 0) { var type = memberToInitialize.GetTypeOrReturnType(); @@ -954,9 +847,9 @@ void makeNotNullMembersMaybeNull() { do { - makeMembersMaybeNull(method, method.NotNullMembers); - makeMembersMaybeNull(method, method.NotNullWhenTrueMembers); - makeMembersMaybeNull(method, method.NotNullWhenFalseMembers); + MakeMembersMaybeNull(method, method.NotNullMembers); + MakeMembersMaybeNull(method, method.NotNullWhenTrueMembers); + MakeMembersMaybeNull(method, method.NotNullWhenFalseMembers); method = method.OverriddenMethod; } while (method != null); @@ -1082,54 +975,170 @@ static Symbol getFieldSymbolToBeInitialized(Symbol requiredMember) } } } + } - void makeMembersMaybeNull(MethodSymbol method, ImmutableArray members) + private void EnforceMemberNotNullOnMember(SyntaxNode? syntaxOpt, LocalState state, MethodSymbol method, string memberName) + { + foreach (var member in method.ContainingType.GetMembers(memberName)) { - foreach (var memberName in members) + if (FailsMemberNotNullExpectation(member, state)) { - makeMemberMaybeNull(method, memberName); + SyntaxNodeOrToken syntax = syntaxOpt switch + { + BlockSyntax blockSyntax => blockSyntax.CloseBraceToken, + LocalFunctionStatementSyntax localFunctionSyntax => localFunctionSyntax.GetLastToken(), + _ => syntaxOpt ?? (SyntaxNodeOrToken)methodMainNode.Syntax.GetLastToken() + }; + + // Member '{name}' must have a non-null value when exiting. + Diagnostics.Add(ErrorCode.WRN_MemberNotNull, syntax.GetLocation(), member.Name); } } + } - void makeMemberMaybeNull(MethodSymbol method, string memberName) + private void EnforceMemberNotNullWhenForPendingReturn(PendingBranch pendingReturn, BoundReturnStatement returnStatement) + { + if (pendingReturn.IsConditionalState) { - var type = method.ContainingType; - foreach (var member in type.GetMembers(memberName)) + if (returnStatement.ExpressionOpt is { ConstantValueOpt: { IsBoolean: true, BooleanValue: bool value } }) { - if (getSlotForFieldOrPropertyOrEvent(member) is int memberSlot && - memberSlot > 0) + enforceMemberNotNullWhen(returnStatement.Syntax, sense: value, pendingReturn.State); + return; + } + + if (!pendingReturn.StateWhenTrue.Reachable || !pendingReturn.StateWhenFalse.Reachable) + { + return; + } + + if (_symbol is MethodSymbol method) + { + foreach (var memberName in method.NotNullWhenTrueMembers) { - SetState(ref this.State, memberSlot, NullableFlowState.MaybeNull); + enforceMemberNotNullWhenIfAffected(returnStatement.Syntax, sense: true, members: method.ContainingType.GetMembers(memberName), state: pendingReturn.StateWhenTrue, otherState: pendingReturn.StateWhenFalse); + } + + foreach (var memberName in method.NotNullWhenFalseMembers) + { + enforceMemberNotNullWhenIfAffected(returnStatement.Syntax, sense: false, members: method.ContainingType.GetMembers(memberName), state: pendingReturn.StateWhenFalse, otherState: pendingReturn.StateWhenTrue); } } } - int getSlotForFieldOrPropertyOrEvent(Symbol member) + return; + + void enforceMemberNotNullWhenIfAffected(SyntaxNode? syntaxOpt, bool sense, ImmutableArray members, LocalState state, LocalState otherState) { - if (member.Kind != SymbolKind.Field && - member.Kind != SymbolKind.Property && - member.Kind != SymbolKind.Event) + foreach (var member in members) { - return -1; + // For non-constant values, only complain if we were able to analyze a difference for this member between two branches + if (FailsMemberNotNullExpectation(member, state) != FailsMemberNotNullExpectation(member, otherState)) + { + ReportFailedMemberNotNullIfNeeded(syntaxOpt, sense, member, state); + } } + } - int containingSlot = 0; - if (!member.IsStatic) + void enforceMemberNotNullWhen(SyntaxNode? syntaxOpt, bool sense, LocalState state) + { + if (_symbol is MethodSymbol method) { - if (MethodThisParameter is null) + var notNullMembers = sense ? method.NotNullWhenTrueMembers : method.NotNullWhenFalseMembers; + foreach (var memberName in notNullMembers) + { + foreach (var member in method.ContainingType.GetMembers(memberName)) + { + ReportFailedMemberNotNullIfNeeded(syntaxOpt, sense, member, state); + } + } + } + } + } + + private void ReportFailedMemberNotNullIfNeeded(SyntaxNode? syntaxOpt, bool sense, Symbol member, LocalState state) + { + if (FailsMemberNotNullExpectation(member, state)) + { + // Member '{name}' must have a non-null value when exiting with '{sense}'. + Diagnostics.Add(ErrorCode.WRN_MemberNotNullWhen, syntaxOpt?.GetLocation() ?? methodMainNode.Syntax.GetLastToken().GetLocation(), member.Name, sense ? "true" : "false"); + } + } + + private bool FailsMemberNotNullExpectation(Symbol member, LocalState state) + { + switch (member.Kind) + { + case SymbolKind.Field: + case SymbolKind.Property: + if (GetSlotForMemberPostCondition(member) is int memberSlot && + memberSlot > 0) { - return -1; + var parameterState = GetState(ref state, memberSlot); + return !parameterState.IsNotNull(); } - containingSlot = GetOrCreateSlot(MethodThisParameter); - if (containingSlot < 0) + else { - return -1; + return false; + } + + case SymbolKind.Event: + case SymbolKind.Method: + break; + } + + return false; + } + + private void MakeMembersMaybeNull(MethodSymbol method, ImmutableArray members) + { + foreach (var memberName in members) + { + makeMemberMaybeNull(method, memberName); + } + return; + + void makeMemberMaybeNull(MethodSymbol method, string memberName) + { + var type = method.ContainingType; + foreach (var member in type.GetMembers(memberName)) + { + if (GetSlotForMemberPostCondition(member) is int memberSlot && + memberSlot > 0) + { + SetState(ref this.State, memberSlot, NullableFlowState.MaybeNull); } - Debug.Assert(containingSlot > 0); } + } + } + + private int GetSlotForMemberPostCondition(Symbol member) + { + if (member.Kind != SymbolKind.Field && + member.Kind != SymbolKind.Property && + member.Kind != SymbolKind.Event) + { + return -1; + } + + int containingSlot = GetReceiverSlotForMemberPostConditions(_symbol as MethodSymbol); + + if (containingSlot < 0) + { + return -1; + } - return GetOrCreateSlot(member, containingSlot); + if (member.IsStatic) + { + // Trying to access a static member from a non-static context + containingSlot = 0; } + else if (containingSlot == 0) + { + // Trying to access an instance member from a static context + return -1; + } + + return GetOrCreateSlot(member, containingSlot); } /// @@ -3256,6 +3265,14 @@ private void AnalyzeLocalFunctionOrLambda( EnterParameters(); + bool isLocalFunction = lambdaOrFunctionSymbol is LocalFunctionSymbol; + if (isLocalFunction) + { + MakeMembersMaybeNull(lambdaOrFunctionSymbol, lambdaOrFunctionSymbol.NotNullMembers); + MakeMembersMaybeNull(lambdaOrFunctionSymbol, lambdaOrFunctionSymbol.NotNullWhenTrueMembers); + MakeMembersMaybeNull(lambdaOrFunctionSymbol, lambdaOrFunctionSymbol.NotNullWhenFalseMembers); + } + var oldPending2 = SavePending(); // If this is an iterator, there's an implicit branch before the first statement @@ -3267,6 +3284,10 @@ private void AnalyzeLocalFunctionOrLambda( VisitAlways(lambdaOrFunction.Body); EnforceDoesNotReturn(syntaxOpt: null); + if (isLocalFunction) + { + enforceMemberNotNull(((LocalFunctionSymbol)lambdaOrFunctionSymbol).Syntax, this.State); + } EnforceParameterNotNullOnExit(null, this.State); RestorePending(oldPending2); // process any forward branches within the lambda body @@ -3274,10 +3295,19 @@ private void AnalyzeLocalFunctionOrLambda( ImmutableArray pendingReturns = RemoveReturns(); foreach (var pendingReturn in pendingReturns) { + if (isLocalFunction) + { + enforceMemberNotNull(syntax: pendingReturn.Branch?.Syntax, pendingReturn.State); + } + if (pendingReturn.Branch is BoundReturnStatement returnStatement) { EnforceParameterNotNullOnExit(returnStatement.Syntax, pendingReturn.State); EnforceNotNullWhenForPendingReturn(pendingReturn, returnStatement); + if (isLocalFunction) + { + EnforceMemberNotNullWhenForPendingReturn(pendingReturn, returnStatement); + } } } @@ -3299,6 +3329,19 @@ private void AnalyzeLocalFunctionOrLambda( _delegateInvokeMethod = oldDelegateInvokeMethod; this.CurrentSymbol = oldCurrentSymbol; this._symbol = oldSymbol; + return; + + void enforceMemberNotNull(SyntaxNode? syntax, LocalState state) + { + if (!state.Reachable) + return; + + var method = (LocalFunctionSymbol)_symbol; + foreach (var memberName in method.NotNullMembers) + { + EnforceMemberNotNullOnMember(syntax, state, method, memberName); + } + } } protected override void VisitLocalFunctionUse( @@ -7047,10 +7090,9 @@ private void ApplyMemberPostConditions(BoundExpression? receiverOpt, MethodSymbo return; } - int receiverSlot = - method.IsStatic ? 0 : - receiverOpt is null ? -1 : - MakeSlot(receiverOpt); + int receiverSlot = receiverOpt is not null && !method.IsStatic + ? MakeSlot(receiverOpt) + : GetReceiverSlotForMemberPostConditions(method); if (receiverSlot < 0) { @@ -7060,6 +7102,42 @@ private void ApplyMemberPostConditions(BoundExpression? receiverOpt, MethodSymbo ApplyMemberPostConditions(receiverSlot, method); } + // For an instance method, or a non-static local function in an instance method, returns the slot for the `this` parameter + // For a static method, or a static local function, or a local function in a static method, returns 0 + // Otherwise, returns -1 + private int GetReceiverSlotForMemberPostConditions(MethodSymbol? method) + { + if (method is null) + { + return -1; + } + + if (method.IsStatic) + { + return 0; + } + + MethodSymbol? current = method; + while (current.ContainingSymbol is MethodSymbol container) + { + current = container; + if (container.IsStatic) + { + return 0; + } + } + + if (current.TryGetThisParameter(out var thisParameter) && thisParameter is not null) + { + return GetOrCreateSlot(thisParameter); + } + + // The ContainingSymbol on a substituted local function is incorrect (returns the containing type instead of the containing method) + // so we can't find the proper `this` receiver to apply the member post-conditions to. + // Tracked by https://github.com/dotnet/roslyn/issues/75543 + return -1; + } + private void ApplyMemberPostConditions(int receiverSlot, MethodSymbol method) { Debug.Assert(receiverSlot >= 0); @@ -7111,8 +7189,14 @@ void markMembersAsNotNull(int receiverSlot, TypeSymbol type, string memberName, { if (member.IsStatic) { + // Trying to access a static member from a non-static context receiverSlot = 0; } + else if (receiverSlot == 0) + { + // Trying to access an instance member from a static context + continue; + } switch (member.Kind) { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index 600b2098f4800..dccdb2cf46086 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -20937,10 +20937,91 @@ public C() ); } - [Fact] - public void MemberNotNull_LocalFunction() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_WithinInstanceConstructor_InstanceFields() { - var c = CreateNullableCompilation(new[] { @" + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1; + public string? field2; + public string field3; + public string? field4; + + public C() + { + init(); + field1.ToString(); + field2.ToString(); + field3.ToString(); // 1 + field4.ToString(); // 2 + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() => throw null!; + } +} +""", MemberNotNullAttributeDefinition], parseOptions: TestOptions.Regular9); + + c.VerifyDiagnostics( + // (14,9): warning CS8602: Dereference of a possibly null reference. + // field3.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field3").WithLocation(14, 9), + // (15,9): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(15, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_WithinInstanceConstructor_StaticFields() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public static string field1; // 1 + public static string? field2; + public static string field3; // 2 + public static string? field4; + + public C() + { + init(); + field1.ToString(); + field2.ToString(); + field3.ToString(); + field4.ToString(); // 3 + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() + { + } // 4, 5 + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (4,26): warning CS8618: Non-nullable field 'field1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. + // public static string field1; // 1 + Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "field1").WithArguments("field", "field1").WithLocation(4, 26), + // (6,26): warning CS8618: Non-nullable field 'field3' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. + // public static string field3; // 2 + Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "field3").WithArguments("field", "field3").WithLocation(6, 26), + // (15,9): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(15, 9), + // (20,9): warning CS8774: Member 'field1' must have a non-null value when exiting. + // } // 4, 5 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field1").WithLocation(20, 9), + // (20,9): warning CS8774: Member 'field2' must have a non-null value when exiting. + // } // 4, 5 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field2").WithLocation(20, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_StaticLocalFunction_WithinInstanceConstructor_InstanceFields() + { + var c = CreateNullableCompilation([""" using System.Diagnostics.CodeAnalysis; public class C { @@ -20957,27 +21038,1716 @@ public C() field3.ToString(); // 3 field4.ToString(); // 4 + [MemberNotNull(nameof(field1), nameof(field2))] + static void init() + { + } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (12,9): warning CS8602: Dereference of a possibly null reference. + // field1.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field1").WithLocation(12, 9), + // (13,9): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(13, 9), + // (14,9): warning CS8602: Dereference of a possibly null reference. + // field3.ToString(); // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field3").WithLocation(14, 9), + // (15,9): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 4 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(15, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_StaticLocalFunction_WithinInstanceConstructor_StaticFields() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public static string field1; // 1 + public static string? field2; + public static string field3; // 2 + public static string? field4; + + public C() + { + init(); + field1.ToString(); + field2.ToString(); + field3.ToString(); + field4.ToString(); // 3 + + [MemberNotNull(nameof(field1), nameof(field2))] + static void init() + { + } // 4, 5 + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (4,26): warning CS8618: Non-nullable field 'field1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. + // public static string field1; // 1 + Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "field1").WithArguments("field", "field1").WithLocation(4, 26), + // (6,26): warning CS8618: Non-nullable field 'field3' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. + // public static string field3; // 2 + Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "field3").WithArguments("field", "field3").WithLocation(6, 26), + // (15,9): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(15, 9), + // (20,9): warning CS8774: Member 'field1' must have a non-null value when exiting. + // } // 4, 5 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field1").WithLocation(20, 9), + // (20,9): warning CS8774: Member 'field2' must have a non-null value when exiting. + // } // 4, 5 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field2").WithLocation(20, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_StaticLocalFunction_WithinStaticConstructor_StaticFields() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public static string field1; + public static string? field2; + public static string field3; + public static string? field4; + + static C() + { + init(); + field1.ToString(); + field2.ToString(); + field3.ToString(); // 1 + field4.ToString(); // 2 + + [MemberNotNull(nameof(field1), nameof(field2))] + static void init() + { + } // 3, 4 + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (14,9): warning CS8602: Dereference of a possibly null reference. + // field3.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field3").WithLocation(14, 9), + // (15,9): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(15, 9), + // (20,9): warning CS8774: Member 'field1' must have a non-null value when exiting. + // } // 3, 4 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field1").WithLocation(20, 9), + // (20,9): warning CS8774: Member 'field2' must have a non-null value when exiting. + // } // 3, 4 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field2").WithLocation(20, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_MissingArgument() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2 = null; + public int capture; + + public void M() + { + init(); + field1.ToString(); + field2.ToString(); + + [MemberNotNull(nameof(field1), nameof(field2))] + void init(int a) { System.Console.Write(capture); field1 = ""; field2 = ""; } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyEmitDiagnostics( + // (10,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'init(int)' + // init(); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "init").WithArguments("a", "init(int)").WithLocation(10, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_BaseField() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class Base +{ + public string field1; + public string? field2; + public string field3; + public string? field4; +} +public class C : Base +{ + public C() + { + init(); + field1.ToString(); + field2.ToString(); + field3.ToString(); + field4.ToString(); + [MemberNotNull(nameof(field1), nameof(field2))] void init() => throw null!; } } -", MemberNotNullAttributeDefinition }, parseOptions: TestOptions.Regular9); +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (4,19): warning CS8618: Non-nullable field 'field1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. + // public string field1; + Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "field1").WithArguments("field", "field1").WithLocation(4, 19), + // (6,19): warning CS8618: Non-nullable field 'field3' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. + // public string field3; + Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "field3").WithArguments("field", "field3").WithLocation(6, 19), + // (15,9): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(15, 9), + // (17,9): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(17, 9), + // (19,10): warning CS8776: Member 'field1' cannot be used in this attribute. + // [MemberNotNull(nameof(field1), nameof(field2))] + Diagnostic(ErrorCode.WRN_MemberNotNullBadMember, "MemberNotNull(nameof(field1), nameof(field2))").WithArguments("field1").WithLocation(19, 10), + // (19,10): warning CS8776: Member 'field2' cannot be used in this attribute. + // [MemberNotNull(nameof(field1), nameof(field2))] + Diagnostic(ErrorCode.WRN_MemberNotNullBadMember, "MemberNotNull(nameof(field1), nameof(field2))").WithArguments("field2").WithLocation(19, 10)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_WithinInstanceMethod() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + init(); + field1.ToString(); + field2.ToString(); + field3.ToString(); + field4.ToString(); // 1 + + [MemberNotNull(nameof(field1), nameof(field2))] + bool init() => throw null!; + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (15,9): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(15, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_WithinInstanceMethod_Generic() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + init(new object()); + field1.ToString(); + field2.ToString(); + field3.ToString(); + field4.ToString(); // 1 + + [MemberNotNull(nameof(field1), nameof(field2))] + bool init(T t) => throw null!; + } +} +""", MemberNotNullAttributeDefinition]); - // Note: the local function is not invoked on this or base + // The ContainingSymbol on a substituted local function is incorrect (returns the containing type instead of the containing method) + // so we can't find the proper `this` receiver to apply the member post-conditions to. + // Tracked by https://github.com/dotnet/roslyn/issues/75543 c.VerifyDiagnostics( // (13,9): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(13, 9), + // (15,9): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(15, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_WithinInstanceMethod_Nested() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + local(); + + void local() + { + init(); + field1.ToString(); + field2.ToString(); + field3.ToString(); + field4.ToString(); // 1 + + [MemberNotNull(nameof(field1), nameof(field2))] + bool init() => throw null!; + } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (19,13): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(19, 13)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_WithinInstanceMethod_Nested_WithinStaticLocalFunction() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + local(); + + static void local() + { + init(); + field1.ToString(); + field2.ToString(); + field3.ToString(); + field4.ToString(); + + [MemberNotNull(nameof(field1), nameof(field2))] + bool init() => throw null!; + } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (16,13): error CS8422: A static local function cannot contain a reference to 'this' or 'base'. + // field1.ToString(); + Diagnostic(ErrorCode.ERR_StaticLocalFunctionCannotCaptureThis, "field1").WithLocation(16, 13), + // (17,13): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(17, 13), + // (17,13): error CS8422: A static local function cannot contain a reference to 'this' or 'base'. + // field2.ToString(); + Diagnostic(ErrorCode.ERR_StaticLocalFunctionCannotCaptureThis, "field2").WithLocation(17, 13), + // (18,13): error CS8422: A static local function cannot contain a reference to 'this' or 'base'. + // field3.ToString(); + Diagnostic(ErrorCode.ERR_StaticLocalFunctionCannotCaptureThis, "field3").WithLocation(18, 13), + // (19,13): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(19, 13), + // (19,13): error CS8422: A static local function cannot contain a reference to 'this' or 'base'. + // field4.ToString(); + Diagnostic(ErrorCode.ERR_StaticLocalFunctionCannotCaptureThis, "field4").WithLocation(19, 13)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_WithinInstanceMethod_Nested_WithinStaticLocalFunction_NotSet() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + local(); + + static void local() + { + init(); + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() + { + } + } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_WithinInstanceMethod_Nested_WithinLocalFunctionWithoutMemberNotNull() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + + public void M() + { + field1.ToString(); + field2.ToString(); // 1 + local(); + + void local() + { + init(); + + [MemberNotNull(nameof(field1), nameof(field2))] + bool init() => throw null!; + } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (10,9): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(10, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_StaticLocalFunction_WithinInstanceMethod() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + + public void M() + { + init(); + field1.ToString(); + field2.ToString(); // 1 + + [MemberNotNull(nameof(field1), nameof(field2))] + static bool init() => throw null!; + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (11,9): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(11, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_StaticLocalFunction_WithinInstanceMethod_NotSet() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + + public void M() + { + init(); + + [MemberNotNull(nameof(field1), nameof(field2))] + static void init() + { + } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_StaticLocalFunction_WithinStaticMethod() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public static class C +{ + public static string field1 = ""; + public static string? field2; + public static string field3 = ""; + public static string? field4; + + public static void M() + { + init(); + field1.ToString(); + field2.ToString(); + field3.ToString(); + field4.ToString(); // 1 + + [MemberNotNull(nameof(field1), nameof(field2))] + static bool init() => throw null!; + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (15,9): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(15, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_StaticLocalFunction_WithinInstanceMethod_StaticFields() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public static string field1 = ""; + public static string? field2; + + public void M() + { + init(); + field1.ToString(); + field2.ToString(); + + [MemberNotNull(nameof(field1), nameof(field2))] + static bool init() => throw null!; + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_StaticLocalFunction_WithinInstanceMethod_InstanceFields() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + + public void M() + { + init(); + field1.ToString(); + field2.ToString(); // 1 + + [MemberNotNull(nameof(field1), nameof(field2))] + static bool init() => throw null!; + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (11,9): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(11, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_StaticLocalFunction_WithinInstanceMethod_InstanceFields_NotSet() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + + public void M() + { + init(); + + [MemberNotNull(nameof(field1), nameof(field2))] + static void init() + { + } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_WithinInstanceMethod_StaticFields() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public static string field1 = ""; + public static string? field2; + + public void M() + { + init(); + field1.ToString(); + field2.ToString(); + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() => throw null!; + } + + public void M2() + { + init(); + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() + { + field1.ToString(); // 1 + field2.ToString(); // 2 + } + } + + public void M3() + { + init(); + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() + { + } // 3, 4 + } + + public void M4() + { + init(); + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() + { + field1 = ""; + field2 = ""; + } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (24,13): warning CS8602: Dereference of a possibly null reference. + // field1.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field1").WithLocation(24, 13), + // (25,13): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(25, 13), + // (36,9): warning CS8774: Member 'field1' must have a non-null value when exiting. + // } // 3, 4 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field1").WithLocation(36, 9), + // (36,9): warning CS8774: Member 'field2' must have a non-null value when exiting. + // } // 3, 4 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field2").WithLocation(36, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_WithinInstanceAccessor_StaticFields() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public static string field1 = ""; + public static string? field2; + + public int Property1 + { + get + { + init(); + field1.ToString(); + field2.ToString(); + return 0; + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() => throw null!; + } + } + + public int Property2 + { + get + { + init(); + return 0; + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() + { + field1.ToString(); // 1 + field2.ToString(); // 2 + } + } + } + + public int Property3 + { + get + { + init(); + return 0; + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() + { + } // 3, 4 + } + } + + public int Property4 + { + get + { + init(); + return 0; + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() + { + field1 = ""; + field2 = ""; + } + } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (31,17): warning CS8602: Dereference of a possibly null reference. + // field1.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field1").WithLocation(31, 17), + // (32,17): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(32, 17), + // (47,13): warning CS8774: Member 'field1' must have a non-null value when exiting. + // } // 3, 4 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field1").WithLocation(47, 13), + // (47,13): warning CS8774: Member 'field2' must have a non-null value when exiting. + // } // 3, 4 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field2").WithLocation(47, 13)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_WithinStaticMethod() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public static string field1 = ""; + public static string? field2; + + public static void M() + { + init(); + field1.ToString(); + field2.ToString(); + + [MemberNotNull(nameof(field1), nameof(field2))] + bool init() => throw null!; + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_WithinLambda() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1; + public string? field2; + public string field3; + public string? field4; + + public C() + { + System.Action a = () => + { + init(); + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() => throw null!; + }; + + field1.ToString(); // 1 + field2.ToString(); // 2 + field3.ToString(); // 3 + field4.ToString(); // 4 + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (19,9): warning CS8602: Dereference of a possibly null reference. // field1.ToString(); // 1 - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field1").WithLocation(13, 9), - // (14,9): warning CS8602: Dereference of a possibly null reference. + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field1").WithLocation(19, 9), + // (20,9): warning CS8602: Dereference of a possibly null reference. // field2.ToString(); // 2 - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(14, 9), - // (15,9): warning CS8602: Dereference of a possibly null reference. + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(20, 9), + // (21,9): warning CS8602: Dereference of a possibly null reference. // field3.ToString(); // 3 - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field3").WithLocation(15, 9), - // (16,9): warning CS8602: Dereference of a possibly null reference. + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field3").WithLocation(21, 9), + // (22,9): warning CS8602: Dereference of a possibly null reference. // field4.ToString(); // 4 - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(16, 9) - ); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(22, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_WithinAnonymousFunction() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1; + public string? field2; + public string field3; + public string? field4; + + public C() + { + System.Action a = delegate() + { + init(); + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() => throw null!; + }; + + field1.ToString(); // 1 + field2.ToString(); // 2 + field3.ToString(); // 3 + field4.ToString(); // 4 + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (19,9): warning CS8602: Dereference of a possibly null reference. + // field1.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field1").WithLocation(19, 9), + // (20,9): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(20, 9), + // (21,9): warning CS8602: Dereference of a possibly null reference. + // field3.ToString(); // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field3").WithLocation(21, 9), + // (22,9): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 4 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(22, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_TopLevel() + { + var c = CreateCompilation([""" +#nullable enable +using System.Diagnostics.CodeAnalysis; + +init(); + +[MemberNotNull(nameof(field1))] +void init() => throw null!; +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (6,23): error CS0103: The name 'field1' does not exist in the current context + // [MemberNotNull(nameof(field1))] + Diagnostic(ErrorCode.ERR_NameNotInContext, "field1").WithArguments("field1").WithLocation(6, 23)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_TopLevel_WithField() + { + var c = CreateCompilation([""" +#nullable enable +using System.Diagnostics.CodeAnalysis; + +init(); +field1.ToString(); + +[MemberNotNull(nameof(field1))] +void init() => throw null!; + +partial class Program +{ + public static object? field1 = null; +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_StaticLocalFunction_TopLevel() + { + var c = CreateCompilation([""" +#nullable enable +using System.Diagnostics.CodeAnalysis; + +init(); + +[MemberNotNull(nameof(field1))] +static void init() => throw null!; +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (6,23): error CS0103: The name 'field1' does not exist in the current context + // [MemberNotNull(nameof(field1))] + Diagnostic(ErrorCode.ERR_NameNotInContext, "field1").WithArguments("field1").WithLocation(6, 23)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_StaticLocalFunction_TopLevel_WithField() + { + var c = CreateCompilation([""" +#nullable enable +using System.Diagnostics.CodeAnalysis; + +init(); +field1.ToString(); +field1 = ""; + +[MemberNotNull(nameof(field1))] +static void init() => throw null!; + +partial class Program +{ + public static object? field1; +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_EnforcedInLocalFunction_SetToMaybeNull() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + init(); + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() + { + field1.ToString(); // 1 + field2.ToString(); // 2 + field3.ToString(); + field4.ToString(); // 3 + } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyEmitDiagnostics( + // (16,13): warning CS8602: Dereference of a possibly null reference. + // field1.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field1").WithLocation(16, 13), + // (17,13): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(17, 13), + // (19,13): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(19, 13)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NotEnforcedInLambda_SetToMaybeNull() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + var lambda = [MemberNotNull(nameof(field1), nameof(field2))] void () => + { + field1.ToString(); + field2.ToString(); // 1 + field3.ToString(); + field4.ToString(); // 2 + }; + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyEmitDiagnostics( + // (14,13): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(14, 13), + // (16,13): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(16, 13)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_EnforcedInLocalFunction_Property_SetToMaybeNull() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string P1 { get; set; } = ""; + public string? P2 { get; set; } + public string P3 { get; set; } = ""; + public string? P4 { get; set; } + + public void M() + { + init(); + + [MemberNotNull(nameof(P1), nameof(P2))] + void init() + { + P1.ToString(); // 1 + P2.ToString(); // 2 + P3.ToString(); + P4.ToString(); // 3 + } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyEmitDiagnostics( + // (16,13): warning CS8602: Dereference of a possibly null reference. + // P1.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "P1").WithLocation(16, 13), + // (17,13): warning CS8602: Dereference of a possibly null reference. + // P2.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "P2").WithLocation(17, 13), + // (19,13): warning CS8602: Dereference of a possibly null reference. + // P4.ToString(); // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "P4").WithLocation(19, 13)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_EnforcedInLocalFunction_CheckNotNullOnExit() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + init(); + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() + { + } // 1 + } +} +""", MemberNotNullAttributeDefinition]); + + // Note: we add a synthesized BoundReturnStatement in BindLocalFunctionStatement + // using the block body as syntax, but we report the diagnostic on the closing brace in that case + c.VerifyEmitDiagnostics( + // (16,9): warning CS8774: Member 'field1' must have a non-null value when exiting. + // } // 1 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field1").WithLocation(16, 9), + // (16,9): warning CS8774: Member 'field2' must have a non-null value when exiting. + // } // 1 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field2").WithLocation(16, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_EnforcedInLocalFunction_CheckNotNullOnExit_WithExplicitReturn() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + init(); + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() + { + return; + } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyEmitDiagnostics( + // (16,13): warning CS8774: Member 'field1' must have a non-null value when exiting. + // return; + Diagnostic(ErrorCode.WRN_MemberNotNull, "return;").WithArguments("field1").WithLocation(16, 13), + // (16,13): warning CS8774: Member 'field2' must have a non-null value when exiting. + // return; + Diagnostic(ErrorCode.WRN_MemberNotNull, "return;").WithArguments("field2").WithLocation(16, 13)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_EnforcedInLocalFunction_CheckNotNullOnExit_InOneBranch() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + init(true); + + [MemberNotNull(nameof(field1), nameof(field2))] + void init(bool b) + { + if (b) + { + field1 = ""; + field2 = ""; + return; + } + else + { + return; // 1 + } + } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyEmitDiagnostics( + // (24,17): warning CS8774: Member 'field1' must have a non-null value when exiting. + // return; // 1 + Diagnostic(ErrorCode.WRN_MemberNotNull, "return;").WithArguments("field1").WithLocation(24, 17), + // (24,17): warning CS8774: Member 'field2' must have a non-null value when exiting. + // return; // 1 + Diagnostic(ErrorCode.WRN_MemberNotNull, "return;").WithArguments("field2").WithLocation(24, 17)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_EnforcedInLocalFunction_CheckNotNullOnExit_MissingReturn() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + init(); + + [MemberNotNull(nameof(field1), nameof(field2))] + int init() + { + } // 1 + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyEmitDiagnostics( + // (14,13): error CS0161: 'init()': not all code paths return a value + // int init() + Diagnostic(ErrorCode.ERR_ReturnExpected, "init").WithArguments("init()").WithLocation(14, 13), + // (16,9): warning CS8774: Member 'field1' must have a non-null value when exiting. + // } // 1 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field1").WithLocation(16, 9), + // (16,9): warning CS8774: Member 'field2' must have a non-null value when exiting. + // } // 1 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field2").WithLocation(16, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NotEnforcedInLambda_CheckNullOnExit() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + var lambda = [MemberNotNull(nameof(field1), nameof(field2))] void () => { }; + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyEmitDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_InLocalFunction_BadFields() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class Base +{ + public object? field3; +} +public class C : Base +{ + [MemberNotNull("field1")] + public void M() + { + init(); + + [MemberNotNull("field2", nameof(field3))] + void init() { } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyEmitDiagnostics( + // (8,6): warning CS8776: Member 'field1' cannot be used in this attribute. + // [MemberNotNull("field1")] + Diagnostic(ErrorCode.WRN_MemberNotNullBadMember, @"MemberNotNull(""field1"")").WithArguments("field1").WithLocation(8, 6), + // (13,10): warning CS8776: Member 'field2' cannot be used in this attribute. + // [MemberNotNull("field2", nameof(field3))] + Diagnostic(ErrorCode.WRN_MemberNotNullBadMember, @"MemberNotNull(""field2"", nameof(field3))").WithArguments("field2").WithLocation(13, 10), + // (13,10): warning CS8776: Member 'field3' cannot be used in this attribute. + // [MemberNotNull("field2", nameof(field3))] + Diagnostic(ErrorCode.WRN_MemberNotNullBadMember, @"MemberNotNull(""field2"", nameof(field3))").WithArguments("field3").WithLocation(13, 10)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNullWhenTrue_NonStaticLocalFunction() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1; + public string? field2; + public string field3; + public string? field4; + + public C() + { + if (init()) + { + field1.ToString(); + field2.ToString(); + field3.ToString(); // 1 + field4.ToString(); // 2 + } + else + { + field1.ToString(); // 3 + field2.ToString(); // 4 + field3.ToString(); // 5 + field4.ToString(); // 6 + } + + [MemberNotNullWhen(true, nameof(field1), nameof(field2))] + bool init() => throw null!; + } +} +""", MemberNotNullWhenAttributeDefinition]); + + c.VerifyDiagnostics( + // (15,13): warning CS8602: Dereference of a possibly null reference. + // field3.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field3").WithLocation(15, 13), + // (16,13): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(16, 13), + // (20,13): warning CS8602: Dereference of a possibly null reference. + // field1.ToString(); // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field1").WithLocation(20, 13), + // (21,13): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 4 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(21, 13), + // (22,13): warning CS8602: Dereference of a possibly null reference. + // field3.ToString(); // 5 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field3").WithLocation(22, 13), + // (23,13): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 6 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(23, 13)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNullWhenTrue_StaticLocalFunction() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public static class C +{ + public static string field1; + public static string? field2; + public static string field3; + public static string? field4; + + static C() + { + if (init()) + { + field1.ToString(); + field2.ToString(); + field3.ToString(); // 1 + field4.ToString(); // 2 + } + else + { + field1.ToString(); // 3 + field2.ToString(); // 4 + field3.ToString(); // 5 + field4.ToString(); // 6 + } + + [MemberNotNullWhen(true, nameof(field1), nameof(field2))] + static bool init() => throw null!; + } +} +""", MemberNotNullWhenAttributeDefinition]); + + c.VerifyDiagnostics( + // (15,13): warning CS8602: Dereference of a possibly null reference. + // field3.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field3").WithLocation(15, 13), + // (16,13): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(16, 13), + // (20,13): warning CS8602: Dereference of a possibly null reference. + // field1.ToString(); // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field1").WithLocation(20, 13), + // (21,13): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 4 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(21, 13), + // (22,13): warning CS8602: Dereference of a possibly null reference. + // field3.ToString(); // 5 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field3").WithLocation(22, 13), + // (23,13): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 6 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(23, 13)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNullWhenFalse_NonStaticLocalFunction() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1; + public string? field2; + public string field3; + public string? field4; + + public C() + { + if (init()) + { + field1.ToString(); // 1 + field2.ToString(); // 2 + field3.ToString(); // 3 + field4.ToString(); // 4 + } + else + { + field1.ToString(); + field2.ToString(); + field3.ToString(); // 5 + field4.ToString(); // 6 + } + + [MemberNotNullWhen(false, nameof(field1), nameof(field2))] + bool init() => throw null!; + } +} +""", MemberNotNullWhenAttributeDefinition]); + + c.VerifyDiagnostics( + // (13,13): warning CS8602: Dereference of a possibly null reference. + // field1.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field1").WithLocation(13, 13), + // (14,13): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(14, 13), + // (15,13): warning CS8602: Dereference of a possibly null reference. + // field3.ToString(); // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field3").WithLocation(15, 13), + // (16,13): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 4 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(16, 13), + // (22,13): warning CS8602: Dereference of a possibly null reference. + // field3.ToString(); // 5 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field3").WithLocation(22, 13), + // (23,13): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 6 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(23, 13)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNullWhenTrue_EnforcedInLocalFunction_SetToMaybeNull() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + init(); + + [MemberNotNullWhen(true, nameof(field1), nameof(field2))] + bool init() + { + field1.ToString(); // 1 + field2.ToString(); // 2 + field3.ToString(); + field4.ToString(); // 3 + return true; + } + } +} +""", MemberNotNullWhenAttributeDefinition]); + + c.VerifyEmitDiagnostics( + // (16,13): warning CS8602: Dereference of a possibly null reference. + // field1.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field1").WithLocation(16, 13), + // (17,13): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(17, 13), + // (19,13): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(19, 13)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNullWhenFalse_EnforcedInLocalFunction_SetToMaybeNull() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + init(); + + [MemberNotNullWhen(false, nameof(field1), nameof(field2))] + bool init() + { + field1.ToString(); // 1 + field2.ToString(); // 2 + field3.ToString(); + field4.ToString(); // 3 + return true; + } + } +} +""", MemberNotNullWhenAttributeDefinition]); + + c.VerifyEmitDiagnostics( + // (16,13): warning CS8602: Dereference of a possibly null reference. + // field1.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field1").WithLocation(16, 13), + // (17,13): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(17, 13), + // (19,13): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(19, 13)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNullWhenTrue_EnforcedInLocalFunction_CheckNotNullOnExit() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + init(true); + + [MemberNotNullWhen(true, nameof(field1), nameof(field2))] + bool init(bool b) + { + if (b) + { + return true; // 1 + } + + return false; + } + } +} +""", MemberNotNullWhenAttributeDefinition]); + + c.VerifyEmitDiagnostics( + // (18,17): warning CS8775: Member 'field1' must have a non-null value when exiting with 'true'. + // return true; // 1 + Diagnostic(ErrorCode.WRN_MemberNotNullWhen, "return true;").WithArguments("field1", "true").WithLocation(18, 17), + // (18,17): warning CS8775: Member 'field2' must have a non-null value when exiting with 'true'. + // return true; // 1 + Diagnostic(ErrorCode.WRN_MemberNotNullWhen, "return true;").WithArguments("field2", "true").WithLocation(18, 17)); + } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNullWhenTrueOrFalse_EnforcedInLocalFunction_CheckNotNullOnExit_Set(bool sense) + { + var c = CreateNullableCompilation([$$""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + + public void M() + { + init(true); + + [MemberNotNullWhen({{(sense ? "true" : "false")}}, nameof(field1), nameof(field2))] + bool init(bool b) + { + if (b) + { + field1 = ""; + field2 = ""; + return true; + } + + field1 = ""; + field2 = ""; + return false; + } + } +} +""", MemberNotNullWhenAttributeDefinition]); + + c.VerifyEmitDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNullWhenFalse_EnforcedInLocalFunction_CheckNotNullOnExit() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + init(true); + + [MemberNotNullWhen(false, nameof(field1), nameof(field2))] + bool init(bool b) + { + if (b) + { + return true; + } + + return false; // 1 + } + } +} +""", MemberNotNullWhenAttributeDefinition]); + + c.VerifyEmitDiagnostics( + // (21,13): warning CS8775: Member 'field1' must have a non-null value when exiting with 'false'. + // return false; // 1 + Diagnostic(ErrorCode.WRN_MemberNotNullWhen, "return false;").WithArguments("field1", "false").WithLocation(21, 13), + // (21,13): warning CS8775: Member 'field2' must have a non-null value when exiting with 'false'. + // return false; // 1 + Diagnostic(ErrorCode.WRN_MemberNotNullWhen, "return false;").WithArguments("field2", "false").WithLocation(21, 13)); + } + + [Fact] + public void MemberNotNullWhenTrue_EnforcedInLocalFunction_CheckNotNullOnExit_NonConstant() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1; + public string field3; + + C() + { + if (!init()) + throw null!; + + [MemberNotNullWhen(true, nameof(field1), nameof(field3))] + bool init() + { + bool b = true; + if (b) + { + return field1 == null; // 1 + } + if (b) + { + return field1 != null; + } + if (b) + { + return init(); + } + return !init(); // 2, 3 + } + } +} +""", MemberNotNullWhenAttributeDefinition]); + + c.VerifyDiagnostics( + // (18,17): warning CS8775: Member 'field1' must have a non-null value when exiting with 'true'. + // return field1 == null; // 1 + Diagnostic(ErrorCode.WRN_MemberNotNullWhen, "return field1 == null;").WithArguments("field1", "true").WithLocation(18, 17), + // (28,13): warning CS8775: Member 'field1' must have a non-null value when exiting with 'true'. + // return !init(); // 2, 3 + Diagnostic(ErrorCode.WRN_MemberNotNullWhen, "return !init();").WithArguments("field1", "true").WithLocation(28, 13), + // (28,13): warning CS8775: Member 'field3' must have a non-null value when exiting with 'true'. + // return !init(); // 2, 3 + Diagnostic(ErrorCode.WRN_MemberNotNullWhen, "return !init();").WithArguments("field3", "true").WithLocation(28, 13)); + } + + [Fact] + public void MemberNotNullWhenFalse_EnforcedInLocalFunction_CheckNotNullOnExit_NonConstant() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1; + public string field3; + + C() + { + if (init()) + throw null!; + + [MemberNotNullWhen(false, nameof(field1), nameof(field3))] + bool init() + { + bool b = true; + if (b) + { + return field1 == null; + } + if (b) + { + return field1 != null; // 1 + } + if (b) + { + return init(); + } + return !init(); // 2, 3 + } + } +} +""", MemberNotNullWhenAttributeDefinition]); + + c.VerifyDiagnostics( + // (22,17): warning CS8775: Member 'field1' must have a non-null value when exiting with 'false'. + // return field1 != null; // 1 + Diagnostic(ErrorCode.WRN_MemberNotNullWhen, "return field1 != null;").WithArguments("field1", "false").WithLocation(22, 17), + // (28,13): warning CS8775: Member 'field1' must have a non-null value when exiting with 'false'. + // return !init(); // 2, 3 + Diagnostic(ErrorCode.WRN_MemberNotNullWhen, "return !init();").WithArguments("field1", "false").WithLocation(28, 13), + // (28,13): warning CS8775: Member 'field3' must have a non-null value when exiting with 'false'. + // return !init(); // 2, 3 + Diagnostic(ErrorCode.WRN_MemberNotNullWhen, "return !init();").WithArguments("field3", "false").WithLocation(28, 13)); } [Fact] @@ -23216,6 +24986,29 @@ void M2(C c) ); } + [Fact, WorkItem(47667, "https://github.com/dotnet/roslyn/issues/47667")] + public void MemberNotNull_Extension_02() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; + +public class C { } + +public static class Ext +{ + public static string? _field; + + [MemberNotNull("_field")] + public static void AssertFieldNotNull(this C c) { } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (10,55): warning CS8774: Member '_field' must have a non-null value when exiting. + // public static void AssertFieldNotNull(this C c) { } + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("_field").WithLocation(10, 55)); + } + [Fact, WorkItem(47667, "https://github.com/dotnet/roslyn/issues/47667")] public void MemberNotNullWhen_Extension_01() { @@ -154252,7 +156045,7 @@ public void MemberNotNull_InstanceMemberOnStaticMethod() class C { public string? field; - + [MemberNotNull(""field"")] public static void M() { @@ -154278,6 +156071,67 @@ public void Test() Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field").WithLocation(15, 9)); } + [Fact] + public void MemberNotNull_StaticMemberOnInstanceMethod() + { + var source = +@"using System.Diagnostics.CodeAnalysis; + +class C +{ + public static string? field; + + [MemberNotNull(""field"")] + public void M() + { + } + + public void Test() + { + M(); + field.ToString(); + } +}"; + var comp = CreateCompilation([source], options: WithNullableEnable(), targetFramework: TargetFramework.NetCoreApp); + comp.VerifyDiagnostics( + // (5,27): warning CS0649: Field 'C.field' is never assigned to, and will always have its default value null + // public static string? field; + Diagnostic(ErrorCode.WRN_UnassignedInternalField, "field").WithArguments("C.field", "null").WithLocation(5, 27), + // (10,5): warning CS8774: Member 'field' must have a non-null value when exiting. + // } + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field").WithLocation(10, 5)); + } + + [Fact] + public void MemberNotNull_StaticMemberOnInstanceMethod_Assigned() + { + var source = """ +using System.Diagnostics.CodeAnalysis; + +class C +{ + public static string? field; + + [MemberNotNull("field")] + public void M() + { + field = ""; + } + + [MemberNotNull("field")] + public void M2() + { + field.ToString(); // 1 + } +} +"""; + var comp = CreateCompilation([source], options: WithNullableEnable(), targetFramework: TargetFramework.NetCoreApp); + comp.VerifyDiagnostics( + // (16,9): warning CS8602: Dereference of a possibly null reference. + // field.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field").WithLocation(16, 9)); + } + [Fact, WorkItem(39417, "https://github.com/dotnet/roslyn/issues/39417")] public void CustomAwaitable_DetectSettingNullableToNonNullableType() { From 9bb57bf3b4a88a3d3c1fabb95e7b34d03da1478c Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2024 17:11:10 -0700 Subject: [PATCH 139/508] [main] Update dependencies from dotnet/command-line-api (#75600) * Update dependencies from https://github.com/dotnet/command-line-api build 20241017.1 Microsoft.SourceBuild.Intermediate.command-line-api , System.CommandLine From Version 0.1.532403 -> To Version 0.1.551701 * Failed to perform coherency update for one or more dependencies. * Update dependencies from https://github.com/dotnet/command-line-api build 20241028.1 Microsoft.SourceBuild.Intermediate.command-line-api , System.CommandLine From Version 0.1.532403 -> To Version 0.1.552801 * Failed to perform coherency update for one or more dependencies. --------- Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index b707b02ed0dd6..2b377f4a8a3e4 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -13,14 +13,14 @@ ccd0927e3823fb178c7151594f5d2eaba81bba81 - + https://github.com/dotnet/command-line-api - 803d8598f98fb4efd94604b32627ee9407f246db + feb61c7f328a2401d74f4317b39d02126cfdfe24 - + https://github.com/dotnet/command-line-api - 803d8598f98fb4efd94604b32627ee9407f246db + feb61c7f328a2401d74f4317b39d02126cfdfe24 diff --git a/eng/Versions.props b/eng/Versions.props index 0da02a083ab20..e4dc164395e31 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -20,7 +20,7 @@ Versions managed by Arcade (see Versions.Details.xml) --> - 2.0.0-beta4.24324.3 + 2.0.0-beta4.24528.1 8.0.0 8.0.0 8.0.0 From 6217b5c7e33c562117429ae2e64bc128f5ca104d Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Fri, 1 Nov 2024 09:50:47 -0700 Subject: [PATCH 140/508] Reduce memory and CPU costs due to SegmentedList usage (#75661) * Reduce memory and CPU costs due to SegmentedList usage Currently, the SegmentedList class suffers from two inefficiencies: 1) Upon growth, it doubles the SegmentedArray size. This is necessary for normal List like collections to get constant time amortized growth, but isn't necessary (or desirable) for segmented data structures. 2) Upon growth, it reallocates and copies over the existing pages. Instead, if we only allocate the modified/new pages and the array holding the pages, we can save significant CPU and allocation costs. --- .../SegmentedList.Generic.Tests.Capacity.cs | 140 ++++++++++++++++++ .../SegmentedArray`1+PrivateMarshal.cs | 3 + .../SegmentedCollectionsMarshal.cs | 19 +++ .../Collections/SegmentedList`1.cs | 48 ++++-- .../SegmentedListBenchmarks_Add.cs | 79 ++++++++++ 5 files changed, 275 insertions(+), 14 deletions(-) create mode 100644 src/Compilers/Core/CodeAnalysisTest/Collections/List/SegmentedList.Generic.Tests.Capacity.cs create mode 100644 src/Tools/IdeCoreBenchmarks/SegmentedListBenchmarks_Add.cs diff --git a/src/Compilers/Core/CodeAnalysisTest/Collections/List/SegmentedList.Generic.Tests.Capacity.cs b/src/Compilers/Core/CodeAnalysisTest/Collections/List/SegmentedList.Generic.Tests.Capacity.cs new file mode 100644 index 0000000000000..1eb3fad3c8089 --- /dev/null +++ b/src/Compilers/Core/CodeAnalysisTest/Collections/List/SegmentedList.Generic.Tests.Capacity.cs @@ -0,0 +1,140 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using Microsoft.CodeAnalysis.Collections; +using Xunit; + +namespace Microsoft.CodeAnalysis.UnitTests.Collections +{ + /// + /// Contains tests that ensure the correctness of the List class. + /// + public abstract partial class SegmentedList_Generic_Tests : IList_Generic_Tests + where T : notnull + { + public static IEnumerable TestLengthsAndSegmentCounts + { + get + { + for (var segmentsToAdd = 1; segmentsToAdd < 4; segmentsToAdd++) + { + yield return new object[] { 1, segmentsToAdd }; + yield return new object[] { 10, segmentsToAdd }; + yield return new object[] { 100, segmentsToAdd }; + yield return new object[] { SegmentedArray.TestAccessor.SegmentSize / 2, segmentsToAdd }; + yield return new object[] { SegmentedArray.TestAccessor.SegmentSize, segmentsToAdd }; + yield return new object[] { SegmentedArray.TestAccessor.SegmentSize * 2, segmentsToAdd }; + yield return new object[] { 100000, segmentsToAdd }; + } + } + } + + [Theory] + [MemberData(nameof(ValidCollectionSizes))] + public void Capacity_ArgumentValidity(int initialCapacity) + { + var list = new SegmentedList(initialCapacity); + + for (var i = 0; i < initialCapacity; i++) + list.Add(CreateT(i)); + + Assert.Throws(() => list.Capacity = initialCapacity - 1); + } + + [Theory] + [InlineData(0, 1)] + [InlineData(0, 10)] + [InlineData(4, 6)] + [InlineData(4, 10)] + [InlineData(4, 100_000)] + public void Capacity_MatchesSizeRequested(int initialCapacity, int requestedCapacity) + { + var list = new SegmentedList(initialCapacity); + + list.Capacity = requestedCapacity; + + Assert.Equal(requestedCapacity, list.Capacity); + } + + [Theory] + [MemberData(nameof(TestLengthsAndSegmentCounts))] + public void Capacity_ReusesSegments(int initialCapacity, int segmentCountToAdd) + { + var elementCountToAdd = segmentCountToAdd * SegmentedArray.TestAccessor.SegmentSize; + + var segmented = new SegmentedList(initialCapacity); + + var oldSegments = SegmentedCollectionsMarshal.AsSegments(segmented.GetTestAccessor().Items); + var oldSegmentCount = oldSegments.Length; + + segmented.Capacity = initialCapacity + elementCountToAdd; + + var resizedSegments = SegmentedCollectionsMarshal.AsSegments(segmented.GetTestAccessor().Items); + var resizedSegmentCount = resizedSegments.Length; + + Assert.Equal(oldSegmentCount + segmentCountToAdd, resizedSegmentCount); + + for (var i = 0; i < oldSegmentCount - 1; i++) + Assert.Same(resizedSegments[i], oldSegments[i]); + + for (var i = oldSegmentCount - 1; i < resizedSegmentCount - 1; i++) + Assert.Equal(resizedSegments[i].Length, SegmentedArray.TestAccessor.SegmentSize); + + Assert.NotSame(resizedSegments[resizedSegmentCount - 1], oldSegments[oldSegmentCount - 1]); + Assert.Equal(resizedSegments[resizedSegmentCount - 1].Length, oldSegments[oldSegmentCount - 1].Length); + } + + [Theory] + [CombinatorialData] + public void Capacity_InOnlySingleSegment( + [CombinatorialValues(1, 2, 10, 100)] int initialCapacity, + [CombinatorialValues(1, 2, 10, 100)] int addItemCount) + { + var segmented = new SegmentedList(initialCapacity); + + var oldSegments = SegmentedCollectionsMarshal.AsSegments(segmented.GetTestAccessor().Items); + + segmented.Capacity = initialCapacity + addItemCount; + + var resizedSegments = SegmentedCollectionsMarshal.AsSegments(segmented.GetTestAccessor().Items); + + Assert.Equal(1, oldSegments.Length); + Assert.Equal(1, resizedSegments.Length); + Assert.Same(resizedSegments[0], oldSegments[0]); + Assert.Equal(segmented.Capacity, resizedSegments[0].Length); + } + + [Theory] + [InlineData(0, 1, 4)] + [InlineData(0, 10, 10)] + [InlineData(4, 6, 8)] + [InlineData(4, 10, 10)] + public void EnsureCapacity_ResizesAppropriately(int initialCapacity, int requestedCapacity, int expectedCapacity) + { + var list = new SegmentedList(initialCapacity); + + list.EnsureCapacity(requestedCapacity); + + Assert.Equal(expectedCapacity, list.Capacity); + } + + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(4)] + public void EnsureCapacity_MatchesSizeWithLargeCapacityRequest(int segmentCount) + { + var elementCount = segmentCount * SegmentedArray.TestAccessor.SegmentSize; + var list = new SegmentedList(elementCount); + + Assert.Equal(elementCount, list.Capacity); + + var requestedCapacity = 2 * elementCount + 10; + list.EnsureCapacity(requestedCapacity); + Assert.Equal(requestedCapacity, list.Capacity); + } + } +} diff --git a/src/Dependencies/Collections/SegmentedArray`1+PrivateMarshal.cs b/src/Dependencies/Collections/SegmentedArray`1+PrivateMarshal.cs index d4ad3359886dc..20433a3451bda 100644 --- a/src/Dependencies/Collections/SegmentedArray`1+PrivateMarshal.cs +++ b/src/Dependencies/Collections/SegmentedArray`1+PrivateMarshal.cs @@ -14,5 +14,8 @@ internal static class PrivateMarshal /// public static T[][] AsSegments(SegmentedArray array) => array._items; + + public static SegmentedArray AsSegmentedArray(int length, T[][] segments) + => new SegmentedArray(length, segments); } } diff --git a/src/Dependencies/Collections/SegmentedCollectionsMarshal.cs b/src/Dependencies/Collections/SegmentedCollectionsMarshal.cs index cb4c90b3f8c53..d8a9ebeef1ab6 100644 --- a/src/Dependencies/Collections/SegmentedCollectionsMarshal.cs +++ b/src/Dependencies/Collections/SegmentedCollectionsMarshal.cs @@ -22,6 +22,25 @@ internal static class SegmentedCollectionsMarshal public static T[][] AsSegments(SegmentedArray array) => SegmentedArray.PrivateMarshal.AsSegments(array); + /// + /// Gets a value wrapping the input T[][]. + /// + /// The type of elements in the input. + /// The combined length of the input arrays + /// The input array to wrap in the returned value. + /// A value wrapping . + /// + /// + /// When using this method, callers should take extra care to ensure that they're the sole owners of the input + /// array, and that it won't be modified once the returned value starts + /// being used. Doing so might cause undefined behavior in code paths which don't expect the contents of a given + /// values to change outside their control. + /// + /// + /// Thrown when is + public static SegmentedArray AsSegmentedArray(int length, T[][] segments) + => SegmentedArray.PrivateMarshal.AsSegmentedArray(length, segments); + /// /// Gets either a ref to a in the or a /// ref null if it does not exist in the . diff --git a/src/Dependencies/Collections/SegmentedList`1.cs b/src/Dependencies/Collections/SegmentedList`1.cs index 4a5604cbefb39..5a7ffb9202924 100644 --- a/src/Dependencies/Collections/SegmentedList`1.cs +++ b/src/Dependencies/Collections/SegmentedList`1.cs @@ -131,21 +131,41 @@ public int Capacity ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_SmallCapacity); } - if (value != _items.Length) + if (value == _items.Length) + return; + + if (value <= 0) { - if (value > 0) - { - var newItems = new SegmentedArray(value); - if (_size > 0) - { - SegmentedArray.Copy(_items, newItems, _size); - } - _items = newItems; - } - else - { - _items = s_emptyArray; - } + _items = s_emptyArray; + return; + } + + if (_items.Length == 0) + { + // No data from existing array to reuse, just create a new one. + _items = new SegmentedArray(value); + } + else + { + // Rather than creating a copy of _items, instead reuse as much of it's data as possible. + var segments = SegmentedCollectionsMarshal.AsSegments(_items); + + var oldSegmentCount = segments.Length; + var newSegmentCount = (value + SegmentedArrayHelper.GetSegmentSize() - 1) >> SegmentedArrayHelper.GetSegmentShift(); + + // Grow the array of segments, if necessary + Array.Resize(ref segments, newSegmentCount); + + // Resize all segments to full segment size from the last old segment to the next to last + // new segment. + for (var i = oldSegmentCount - 1; i < newSegmentCount - 1; i++) + Array.Resize(ref segments[i], SegmentedArrayHelper.GetSegmentSize()); + + // Resize the last segment + var lastSegmentSize = value - ((newSegmentCount - 1) << SegmentedArrayHelper.GetSegmentShift()); + Array.Resize(ref segments[newSegmentCount - 1], lastSegmentSize); + + _items = SegmentedCollectionsMarshal.AsSegmentedArray(value, segments); } } } diff --git a/src/Tools/IdeCoreBenchmarks/SegmentedListBenchmarks_Add.cs b/src/Tools/IdeCoreBenchmarks/SegmentedListBenchmarks_Add.cs new file mode 100644 index 0000000000000..ff9629c2302ac --- /dev/null +++ b/src/Tools/IdeCoreBenchmarks/SegmentedListBenchmarks_Add.cs @@ -0,0 +1,79 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using BenchmarkDotNet.Attributes; +using Microsoft.CodeAnalysis.Collections; + +namespace IdeCoreBenchmarks +{ + [MemoryDiagnoser] + public class SegmentedListBenchmarks_Add + { + [Params(1_000, 10_000, 100_000, 1_000_000)] + public int Count { get; set; } + + [Benchmark] + public void AddIntToList() + => AddToList(1); + + [Benchmark] + public void AddObjectToList() + => AddToList(new object()); + + [Benchmark] + public void AddLargeStructToList() + => AddToList(new LargeStruct()); + + [Benchmark] + public void AddEnormousStructToList() + => AddToList(new EnormousStruct()); + + private void AddToList(T item) + { + var array = new SegmentedList(); + var iterations = Count; + + for (var i = 0; i < iterations; i++) + array.Add(item); + } + + private struct LargeStruct + { + public int i1 { get; set; } + public int i2 { get; set; } + public int i3 { get; set; } + public int i4 { get; set; } + public int i5 { get; set; } + public int i6 { get; set; } + public int i7 { get; set; } + public int i8 { get; set; } + public int i9 { get; set; } + public int i10 { get; set; } + public int i11 { get; set; } + public int i12 { get; set; } + public int i13 { get; set; } + public int i14 { get; set; } + public int i15 { get; set; } + public int i16 { get; set; } + public int i17 { get; set; } + public int i18 { get; set; } + public int i19 { get; set; } + public int i20 { get; set; } + } + + private struct EnormousStruct + { + public LargeStruct s1 { get; set; } + public LargeStruct s2 { get; set; } + public LargeStruct s3 { get; set; } + public LargeStruct s4 { get; set; } + public LargeStruct s5 { get; set; } + public LargeStruct s6 { get; set; } + public LargeStruct s7 { get; set; } + public LargeStruct s8 { get; set; } + public LargeStruct s9 { get; set; } + public LargeStruct s10 { get; set; } + } + } +} From cbb2d124058fc80051c9d5e9d798d22e8d5aaab3 Mon Sep 17 00:00:00 2001 From: Ankita Khera <40616383+akhera99@users.noreply.github.com> Date: Fri, 1 Nov 2024 13:42:23 -0700 Subject: [PATCH 141/508] Update PublishData.json (#75704) --- eng/config/PublishData.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/config/PublishData.json b/eng/config/PublishData.json index c72e477d3db8f..a621e8447545d 100644 --- a/eng/config/PublishData.json +++ b/eng/config/PublishData.json @@ -220,7 +220,7 @@ "Shipping", "NonShipping" ], - "vsBranch": "main", + "vsBranch": "rel/d17.13", "vsMajorVersion": 17, "insertionTitlePrefix": "[d17.13 P1]" }, @@ -231,7 +231,7 @@ ], "vsBranch": "main", "vsMajorVersion": 17, - "insertionCreateDraftPR": true, + "insertionCreateDraftPR": false, "insertionTitlePrefix": "[d17.13 P2]" }, "dev/andrha/telemetry": { From b5d73c3a52bd4cdb146a3ef14d4740da9e6dd0d4 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Fri, 1 Nov 2024 19:03:18 -0700 Subject: [PATCH 142/508] Allow inlay hints to be re-resolved if cache misses --- .../Handler/InlayHint/InlayHintCache.cs | 2 +- .../Handler/InlayHint/InlayHintHandler.cs | 4 +- .../Handler/InlayHint/InlayHintResolveData.cs | 3 +- .../InlayHint/InlayHintResolveHandler.cs | 62 ++++++++++++------- .../InlayHintResolveHandlerFactory.cs | 26 -------- .../InlayHint/CSharpInlayHintTests.cs | 11 ++-- .../Razor/Cohost/Handlers/InlayHints.cs | 26 +++++--- 7 files changed, 68 insertions(+), 66 deletions(-) delete mode 100644 src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandlerFactory.cs diff --git a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintCache.cs b/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintCache.cs index c15f24183032d..037224a028503 100644 --- a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintCache.cs +++ b/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintCache.cs @@ -17,5 +17,5 @@ public InlayHintCache() : base(maxCacheSize: 3) /// /// Cached data need to resolve a specific inlay hint item. /// - internal record InlayHintCacheEntry(ImmutableArray InlayHintMembers, VersionStamp SyntaxVersion); + internal record InlayHintCacheEntry(ImmutableArray InlayHintMembers); } diff --git a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs b/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs index 8ce98a031b551..6edd35bf1cabe 100644 --- a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs +++ b/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs @@ -59,7 +59,7 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(InlayHintParams request) // Store the members in the resolve cache so that when we get a resolve request for a particular // member we can re-use the inline hint. - var resultId = inlayHintCache.UpdateCache(new InlayHintCache.InlayHintCacheEntry(hints, syntaxVersion)); + var resultId = inlayHintCache.UpdateCache(new InlayHintCache.InlayHintCacheEntry(hints)); var inlayHints = new LSP.InlayHint[hints.Length]; for (var i = 0; i < hints.Length; i++) @@ -89,7 +89,7 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(InlayHintParams request) ToolTip = null, PaddingLeft = leftPadding, PaddingRight = rightPadding, - Data = new InlayHintResolveData(resultId, i, textDocumentIdentifier) + Data = new InlayHintResolveData(resultId, i, textDocumentIdentifier, syntaxVersion.ToString(), range, displayAllOverride) }; inlayHints[i] = inlayHint; diff --git a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveData.cs b/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveData.cs index 0024e8b8e3d18..37b4504ef9440 100644 --- a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveData.cs +++ b/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveData.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.CodeAnalysis.Text; using Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint; @@ -12,4 +13,4 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint; /// the resultId associated with the inlay hint created on original request. /// the index of the specific inlay hint item in the original list. /// the text document associated with the inlay hint to resolve. -internal sealed record InlayHintResolveData(long ResultId, int ListIndex, TextDocumentIdentifier TextDocument) : DocumentResolveData(TextDocument); +internal sealed record InlayHintResolveData(long ResultId, int ListIndex, TextDocumentIdentifier TextDocument, string SyntaxVersion, Range Range, bool DisplayAllOverride) : DocumentResolveData(TextDocument); diff --git a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs b/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs index 468e908a507e6..39be9f69d66c5 100644 --- a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs +++ b/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs @@ -2,10 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Composition; using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.InlineHints; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.LanguageServer.Protocol; using Roslyn.Utilities; using StreamJsonRpc; @@ -13,16 +18,12 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint { + [ExportCSharpVisualBasicStatelessLspService(typeof(InlayHintResolveHandler)), Shared] [Method(Methods.InlayHintResolveName)] - internal sealed class InlayHintResolveHandler : ILspServiceDocumentRequestHandler + [method: ImportingConstructor] + [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + internal sealed class InlayHintResolveHandler(IGlobalOptionService globalOptions) : ILspServiceDocumentRequestHandler { - private readonly InlayHintCache _inlayHintCache; - - public InlayHintResolveHandler(InlayHintCache inlayHintCache) - { - _inlayHintCache = inlayHintCache; - } - public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; @@ -33,39 +34,58 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.InlayHint request) public Task HandleRequestAsync(LSP.InlayHint request, RequestContext context, CancellationToken cancellationToken) { var document = context.GetRequiredDocument(); - return ResolveInlayHintAsync(document, request, _inlayHintCache, cancellationToken); + var options = globalOptions.GetInlineHintsOptions(document.Project.Language); + var inlayHintCache = context.GetRequiredService(); + var resolveData = GetInlayHintResolveData(request); + return ResolveInlayHintAsync(document, request, inlayHintCache, resolveData, options, cancellationToken); } - internal static async Task ResolveInlayHintAsync(Document document, LSP.InlayHint request, InlayHintCache inlayHintCache, CancellationToken cancellationToken) + internal static async Task ResolveInlayHintAsync( + Document document, + LSP.InlayHint request, + InlayHintCache inlayHintCache, + InlayHintResolveData resolveData, + InlineHintsOptions options, + CancellationToken cancellationToken) { - var resolveData = GetInlayHintResolveData(request); - var (cacheEntry, inlineHintToResolve) = GetCacheEntry(resolveData, inlayHintCache); - var currentSyntaxVersion = await document.GetSyntaxVersionAsync(cancellationToken).ConfigureAwait(false); - var cachedSyntaxVersion = cacheEntry.SyntaxVersion; + var resolveSyntaxVersion = resolveData.SyntaxVersion; - if (currentSyntaxVersion != cachedSyntaxVersion) + if (currentSyntaxVersion.ToString() != resolveSyntaxVersion) { - throw new LocalRpcException($"Cached resolve version {cachedSyntaxVersion} does not match current version {currentSyntaxVersion}") + throw new LocalRpcException($"Request resolve version {resolveSyntaxVersion} does not match current version {currentSyntaxVersion}") { ErrorCode = LspErrorCodes.ContentModified }; } - var taggedText = await inlineHintToResolve.GetDescriptionAsync(document, cancellationToken).ConfigureAwait(false); + var inlineHintToResolve = GetCacheEntry(resolveData, inlayHintCache); + if (inlineHintToResolve is null) + { + // It is very possible that the cache no longer contains the hint being resolved (for example, multiple documents open side by side). + // Instead of throwing, we can recompute the hints since we've already verified above that the version has not changed. + + var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); + var textSpan = ProtocolConversions.RangeToTextSpan(resolveData.Range, text); + + var inlineHintService = document.GetRequiredLanguageService(); + var hints = await inlineHintService.GetInlineHintsAsync(document, textSpan, options, resolveData.DisplayAllOverride, cancellationToken).ConfigureAwait(false); + inlineHintToResolve = hints[resolveData.ListIndex]; + } + + var taggedText = await inlineHintToResolve.Value.GetDescriptionAsync(document, cancellationToken).ConfigureAwait(false); request.ToolTip = ProtocolConversions.GetDocumentationMarkupContent(taggedText, document, true); return request; } - private static (InlayHintCache.InlayHintCacheEntry CacheEntry, InlineHint InlineHintToResolve) GetCacheEntry(InlayHintResolveData resolveData, InlayHintCache inlayHintCache) + private static InlineHint? GetCacheEntry(InlayHintResolveData resolveData, InlayHintCache inlayHintCache) { var cacheEntry = inlayHintCache.GetCachedEntry(resolveData.ResultId); - Contract.ThrowIfNull(cacheEntry, "Missing cache entry for inlay hint resolve request"); - return (cacheEntry, cacheEntry.InlayHintMembers[resolveData.ListIndex]); + return cacheEntry is null ? null : cacheEntry.InlayHintMembers[resolveData.ListIndex]; } - private static InlayHintResolveData GetInlayHintResolveData(LSP.InlayHint inlayHint) + internal static InlayHintResolveData GetInlayHintResolveData(LSP.InlayHint inlayHint) { Contract.ThrowIfNull(inlayHint.Data); var resolveData = JsonSerializer.Deserialize((JsonElement)inlayHint.Data, ProtocolConversions.LspJsonSerializerOptions); diff --git a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandlerFactory.cs b/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandlerFactory.cs deleted file mode 100644 index 1ef5fb2048c6e..0000000000000 --- a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandlerFactory.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Composition; -using Microsoft.CodeAnalysis.Host.Mef; - -namespace Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint -{ - [ExportCSharpVisualBasicLspServiceFactory(typeof(InlayHintResolveHandler)), Shared] - internal sealed class InlayHintResolveHandlerFactory : ILspServiceFactory - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public InlayHintResolveHandlerFactory() - { - } - - public ILspService CreateILspService(LspServices lspServices, WellKnownLspServerKinds serverKind) - { - var inlayHintCache = lspServices.GetRequiredService(); - return new InlayHintResolveHandler(inlayHintCache); - } - } -} diff --git a/src/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs b/src/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs index 432e223c90903..5968d8f97ca07 100644 --- a/src/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs @@ -105,7 +105,7 @@ void X((int, bool) d) } [Theory, CombinatorialData] - public async Task TestDoesNotShutdownServerIfCacheEntryMissing(bool mutatingLspWorkspace) + public async Task TestReturnsInlayHintsEvenIfCacheMisses(bool mutatingLspWorkspace) { var markup = @"class A @@ -148,12 +148,9 @@ void M() // Assert that the first result id is no longer in the cache. Assert.Null(cache.GetCachedEntry(firstResultId)); - // Assert that the request throws because the item no longer exists in the cache. - await Assert.ThrowsAsync(async () => await testLspServer.ExecuteRequestAsync(LSP.Methods.InlayHintResolveName, firstInlayHint, CancellationToken.None)); - - // Assert that the server did not shutdown and that we can resolve the latest inlay hint request we made. - var lastInlayHint = await testLspServer.ExecuteRequestAsync(LSP.Methods.InlayHintResolveName, lastInlayHints.First(), CancellationToken.None); - Assert.NotNull(lastInlayHint?.ToolTip); + // Assert that the resolve request returns the inlay hint even if not in the cache. + var firstResolvedHint = await testLspServer.ExecuteRequestAsync(LSP.Methods.InlayHintResolveName, firstInlayHint, CancellationToken.None); + Assert.NotNull(firstResolvedHint?.ToolTip); } private async Task RunVerifyInlayHintAsync(string markup, bool mutatingLspWorkspace, bool hasTextEdits = true) diff --git a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/InlayHints.cs b/src/Tools/ExternalAccess/Razor/Cohost/Handlers/InlayHints.cs index 1c7fe47a2ea68..d9dfd3bac8f64 100644 --- a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/InlayHints.cs +++ b/src/Tools/ExternalAccess/Razor/Cohost/Handlers/InlayHints.cs @@ -26,6 +26,23 @@ internal static class InlayHints // always just result in the defaults, which for inline hints are to not show anything. However, the editor has a // setting for LSP inlay hints, so we can assume that if we get a request from the client, the user wants hints. // When overriding however, Roslyn does a nicer job if type hints are off. + var options = GetOptions(displayAllOverride); + + return InlayHintHandler.GetInlayHintsAsync(document, textDocumentIdentifier, range, options, displayAllOverride, s_resolveCache, cancellationToken); + } + + public static Task ResolveInlayHintAsync(Document document, InlayHint request, CancellationToken cancellationToken) + { + Contract.ThrowIfNull(s_resolveCache, "Cache should never be null for resolve, since it should have been created by the original request"); + + + var data = InlayHintResolveHandler.GetInlayHintResolveData(request); + var options = GetOptions(data.DisplayAllOverride); + return InlayHintResolveHandler.ResolveInlayHintAsync(document, request, s_resolveCache, data, options, cancellationToken); + } + + private static InlineHintsOptions GetOptions(bool displayAllOverride) + { var options = InlineHintsOptions.Default; if (!displayAllOverride) { @@ -36,14 +53,7 @@ internal static class InlayHints }; } - return InlayHintHandler.GetInlayHintsAsync(document, textDocumentIdentifier, range, options, displayAllOverride, s_resolveCache, cancellationToken); - } - - public static Task ResolveInlayHintAsync(Document document, InlayHint request, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(s_resolveCache, "Cache should never be null for resolve, since it should have been created by the original request"); - - return InlayHintResolveHandler.ResolveInlayHintAsync(document, request, s_resolveCache, cancellationToken); + return options; } } } From c24e6d5304502ad5a5a36ec18b81d4fd056aeede Mon Sep 17 00:00:00 2001 From: David Barbet Date: Fri, 1 Nov 2024 19:30:59 -0700 Subject: [PATCH 143/508] Delete unnecessary lsp factory --- .../CodeLens/CodeLensResolveHandler.cs | 12 +++++---- .../CodeLens/CodeLensResolveHandlerFactory.cs | 25 ------------------- 2 files changed, 7 insertions(+), 30 deletions(-) delete mode 100644 src/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandlerFactory.cs diff --git a/src/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs b/src/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs index a36bc0f6d91e9..ba44e29e156f0 100644 --- a/src/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs +++ b/src/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs @@ -10,21 +10,23 @@ using LSP = Roslyn.LanguageServer.Protocol; using System.Text.Json; using StreamJsonRpc; +using System.Composition; +using System; +using Microsoft.CodeAnalysis.Host.Mef; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.CodeLens; +[ExportCSharpVisualBasicStatelessLspService(typeof(CodeLensResolveHandler)), Shared] [Method(LSP.Methods.CodeLensResolveName)] -internal sealed class CodeLensResolveHandler : ILspServiceDocumentRequestHandler +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CodeLensResolveHandler() : ILspServiceDocumentRequestHandler { /// /// Command name implemented by the client and invoked when the references code lens is selected. /// private const string ClientReferencesCommand = "roslyn.client.peekReferences"; - public CodeLensResolveHandler() - { - } - public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; diff --git a/src/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandlerFactory.cs b/src/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandlerFactory.cs deleted file mode 100644 index f067e3aa69bf3..0000000000000 --- a/src/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandlerFactory.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Composition; -using Microsoft.CodeAnalysis.Host.Mef; - -namespace Microsoft.CodeAnalysis.LanguageServer.Handler.CodeLens; - -[ExportCSharpVisualBasicLspServiceFactory(typeof(CodeLensResolveHandler)), Shared] -internal sealed class CodeLensResolveHandlerFactory : ILspServiceFactory -{ - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CodeLensResolveHandlerFactory() - { - } - - public ILspService CreateILspService(LspServices lspServices, WellKnownLspServerKinds serverKind) - { - return new CodeLensResolveHandler(); - } -} - From 18779c125323c8fe04ef8a3f2a1bae63c5b82b19 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Mon, 4 Nov 2024 15:59:37 +1100 Subject: [PATCH 144/508] Expose code actions to Razor cohosting --- .../Razor/FormatNewFileHandler.cs | 8 +- .../Razor/SimplifyMethodHandler.cs | 9 ++- .../CodeActions/CodeActionResolveHandler.cs | 3 +- .../CodeActions/CodeActionResolveHelper.cs | 24 ++++-- .../Razor/Cohost/Handlers/CodeActions.cs | 81 +++++++++++++++++++ 5 files changed, 113 insertions(+), 12 deletions(-) create mode 100644 src/Tools/ExternalAccess/Razor/Cohost/Handlers/CodeActions.cs diff --git a/src/LanguageServer/Protocol/ExternalAccess/Razor/FormatNewFileHandler.cs b/src/LanguageServer/Protocol/ExternalAccess/Razor/FormatNewFileHandler.cs index 1036b6cafa5dc..fb8bdf4533e68 100644 --- a/src/LanguageServer/Protocol/ExternalAccess/Razor/FormatNewFileHandler.cs +++ b/src/LanguageServer/Protocol/ExternalAccess/Razor/FormatNewFileHandler.cs @@ -59,6 +59,12 @@ public FormatNewFileHandler(IGlobalOptionService globalOptions) var document = solution.GetRequiredDocument(documentId); + return await GetFormattedNewFileContentAsync(document, cancellationToken).ConfigureAwait(false); + } + + internal static async Task GetFormattedNewFileContentAsync(Document document, CancellationToken cancellationToken) + { + var project = document.Project; // Run the new document formatting service, to make sure the right namespace type is used, among other things var formattingService = document.GetLanguageService(); if (formattingService is not null) @@ -79,7 +85,7 @@ public FormatNewFileHandler(IGlobalOptionService globalOptions) // Now format the document so indentation etc. is correct var tree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false); - root = Formatter.Format(root, solution.Services, syntaxFormattingOptions, cancellationToken); + root = Formatter.Format(root, project.Solution.Services, syntaxFormattingOptions, cancellationToken); return root.ToFullString(); } diff --git a/src/LanguageServer/Protocol/ExternalAccess/Razor/SimplifyMethodHandler.cs b/src/LanguageServer/Protocol/ExternalAccess/Razor/SimplifyMethodHandler.cs index 7d547f461e083..f9eae08e54024 100644 --- a/src/LanguageServer/Protocol/ExternalAccess/Razor/SimplifyMethodHandler.cs +++ b/src/LanguageServer/Protocol/ExternalAccess/Razor/SimplifyMethodHandler.cs @@ -43,9 +43,16 @@ public SimplifyMethodHandler() if (originalDocument is null) return null; + var textEdit = request.TextEdit; + + return await GetSimplifiedEditsAsync(originalDocument, textEdit, cancellationToken).ConfigureAwait(false); + } + + internal static async Task GetSimplifiedEditsAsync(Document originalDocument, TextEdit textEdit, CancellationToken cancellationToken) + { // Create a temporary syntax tree that includes the text edit. var originalSourceText = await originalDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); - var pendingChange = ProtocolConversions.TextEditToTextChange(request.TextEdit, originalSourceText); + var pendingChange = ProtocolConversions.TextEditToTextChange(textEdit, originalSourceText); var newSourceText = originalSourceText.WithChanges(pendingChange); var originalTree = await originalDocument.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var newTree = originalTree.WithChangedText(newSourceText); diff --git a/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHandler.cs b/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHandler.cs index be6077f5ed3b7..06a2c81bc987d 100644 --- a/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHandler.cs +++ b/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHandler.cs @@ -7,7 +7,6 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Host.Mef; @@ -97,7 +96,7 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.CodeAction request) return codeAction; } - private static CodeActionResolveData GetCodeActionResolveData(LSP.CodeAction request) + internal static CodeActionResolveData GetCodeActionResolveData(LSP.CodeAction request) { var resolveData = JsonSerializer.Deserialize((JsonElement)request.Data!, ProtocolConversions.LspJsonSerializerOptions); Contract.ThrowIfNull(resolveData, "Missing data for code action resolve request"); diff --git a/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHelper.cs b/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHelper.cs index cec1f54f679ab..88b7247a1cd16 100644 --- a/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHelper.cs +++ b/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHelper.cs @@ -20,11 +20,22 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions { internal class CodeActionResolveHelper { - public static async Task GetCodeActionResolveEditsAsync(RequestContext context, CodeActionResolveData data, ImmutableArray operations, CancellationToken cancellationToken) + public static Task GetCodeActionResolveEditsAsync(RequestContext context, CodeActionResolveData data, ImmutableArray operations, CancellationToken cancellationToken) { var solution = context.Solution; Contract.ThrowIfNull(solution); + return GetCodeActionResolveEditsAsync( + solution, + data, + operations, + context.GetRequiredClientCapabilities().Workspace?.WorkspaceEdit?.ResourceOperations ?? [], + context.TraceInformation, + cancellationToken); + } + + public static async Task GetCodeActionResolveEditsAsync(Solution solution, CodeActionResolveData data, ImmutableArray operations, ResourceOperationKind[] resourceOperations, Action logFunction, CancellationToken cancellationToken) + { // TO-DO: We currently must execute code actions which add new documents on the server as commands, // since there is no LSP support for adding documents yet. In the future, we should move these actions // to execute on the client. @@ -45,7 +56,7 @@ internal class CodeActionResolveHelper // only apply the portions of their work that updates documents, and nothing else. if (option is not ApplyChangesOperation applyChangesOperation) { - context.TraceInformation($"Skipping code action operation for '{data.UniqueIdentifier}'. It was a '{option.GetType().FullName}'"); + logFunction($"Skipping code action operation for '{data.UniqueIdentifier}'. It was a '{option.GetType().FullName}'"); continue; } @@ -79,8 +90,7 @@ internal class CodeActionResolveHelper || projectChange.GetRemovedAdditionalDocuments().Any() || projectChange.GetRemovedAnalyzerConfigDocuments().Any()) { - if (context.GetRequiredClientCapabilities() is not { Workspace.WorkspaceEdit.ResourceOperations: { } resourceOperations } - || !resourceOperations.Contains(ResourceOperationKind.Delete)) + if (!resourceOperations.Contains(ResourceOperationKind.Delete)) { // Removing documents is not supported by this workspace return new LSP.WorkspaceEdit { DocumentChanges = Array.Empty() }; @@ -91,8 +101,7 @@ internal class CodeActionResolveHelper || projectChange.GetAddedAdditionalDocuments().Any() || projectChange.GetAddedAnalyzerConfigDocuments().Any()) { - if (context.GetRequiredClientCapabilities() is not { Workspace.WorkspaceEdit.ResourceOperations: { } resourceOperations } - || !resourceOperations.Contains(ResourceOperationKind.Create)) + if (!resourceOperations.Contains(ResourceOperationKind.Create)) { // Adding documents is not supported by this workspace return new LSP.WorkspaceEdit { DocumentChanges = Array.Empty() }; @@ -103,8 +112,7 @@ internal class CodeActionResolveHelper || projectChange.GetChangedAdditionalDocuments().Any(docId => HasDocumentNameChange(docId, newSolution, solution) || projectChange.GetChangedAnalyzerConfigDocuments().Any(docId => HasDocumentNameChange(docId, newSolution, solution)))) { - if (context.GetRequiredClientCapabilities() is not { Workspace.WorkspaceEdit.ResourceOperations: { } resourceOperations } - || !resourceOperations.Contains(ResourceOperationKind.Rename)) + if (!resourceOperations.Contains(ResourceOperationKind.Rename)) { // Rename documents is not supported by this workspace return new LSP.WorkspaceEdit { DocumentChanges = Array.Empty() }; diff --git a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/CodeActions.cs b/src/Tools/ExternalAccess/Razor/Cohost/Handlers/CodeActions.cs new file mode 100644 index 0000000000000..1fe5e62b48853 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Cohost/Handlers/CodeActions.cs @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Razor; +using Microsoft.CodeAnalysis.LanguageServer.Handler; +using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions; +using Roslyn.LanguageServer.Protocol; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost.Handlers; + +internal static class CodeActions +{ + public static Task GetCodeActionsAsync( + Document document, + CodeActionParams request, + bool supportsVSExtensions, + CancellationToken cancellationToken) + { + var solution = document.Project.Solution; + + var codeFixService = solution.Services.ExportProvider.GetService(); + var codeRefactoringService = solution.Services.ExportProvider.GetService(); + + return CodeActionHelpers.GetVSCodeActionsAsync(request, document, codeFixService, codeRefactoringService, supportsVSExtensions, cancellationToken); + } + + public static async Task ResolveCodeActionAsync(Document document, CodeAction codeAction, ResourceOperationKind[] resourceOperations, CancellationToken cancellationToken) + { + Contract.ThrowIfNull(codeAction.Data); + var data = CodeActionResolveHandler.GetCodeActionResolveData(codeAction); + Assumes.Present(data); + + // We don't need to resolve a top level code action that has nested actions - it requires further action + // on the client to pick which of the nested actions to actually apply. + if (data.NestedCodeActions.HasValue && data.NestedCodeActions.Value.Length > 0) + { + return codeAction; + } + + var solution = document.Project.Solution; + + var codeFixService = solution.Services.ExportProvider.GetService(); + var codeRefactoringService = solution.Services.ExportProvider.GetService(); + + var codeActions = await CodeActionHelpers.GetCodeActionsAsync( + document, + data.Range, + codeFixService, + codeRefactoringService, + fixAllScope: null, + cancellationToken).ConfigureAwait(false); + + Contract.ThrowIfNull(data.CodeActionPath); + var codeActionToResolve = CodeActionHelpers.GetCodeActionToResolve(data.CodeActionPath, codeActions, isFixAllAction: false); + + var operations = await codeActionToResolve.GetOperationsAsync(solution, CodeAnalysisProgress.None, cancellationToken).ConfigureAwait(false); + + var edit = await CodeActionResolveHelper.GetCodeActionResolveEditsAsync( + solution, + data, + operations, + resourceOperations, + logFunction: static s => { }, + cancellationToken).ConfigureAwait(false); + + codeAction.Edit = edit; + return codeAction; + } + + public static Task GetFormattedNewFileContentAsync(Document document, CancellationToken cancellationToken) + => FormatNewFileHandler.GetFormattedNewFileContentAsync(document, cancellationToken); + + public static Task GetSimplifiedEditsAsync(Document document, TextEdit textEdit, CancellationToken cancellationToken) + => SimplifyMethodHandler.GetSimplifiedEditsAsync(document, textEdit, cancellationToken); +} From 3922582e27db83401be1fdbafb17961ddfff3dde Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Mon, 4 Nov 2024 16:29:21 +0100 Subject: [PATCH 145/508] Unpin S.T.J Similar to https://github.com/dotnet/roslyn/pull/75528. --- .../Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Workspaces/Core/MSBuild.BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj b/src/Workspaces/Core/MSBuild.BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj index e07fe16e8fa57..96805e041d8cb 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj +++ b/src/Workspaces/Core/MSBuild.BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj @@ -43,7 +43,6 @@ - From 36e1fe3c27adb70b3ad49c9d51d7cc19d88e656e Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 16:33:14 +0100 Subject: [PATCH 146/508] [main] Update dependencies from dotnet/source-build-reference-packages (#75663) * Update dependencies from https://github.com/dotnet/source-build-reference-packages build 20241028.1 Microsoft.SourceBuild.Intermediate.source-build-reference-packages From Version 10.0.0-alpha.1.24521.1 -> To Version 10.0.0-alpha.1.24528.1 * Update dependencies from https://github.com/dotnet/source-build-reference-packages build 20241030.1 Microsoft.SourceBuild.Intermediate.source-build-reference-packages From Version 10.0.0-alpha.1.24521.1 -> To Version 10.0.0-alpha.1.24530.1 * Failed to perform coherency update for one or more dependencies. --------- Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 2b377f4a8a3e4..3cbf1c8666460 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -8,9 +8,9 @@ - + https://github.com/dotnet/source-build-reference-packages - ccd0927e3823fb178c7151594f5d2eaba81bba81 + 136e43e45e20bd58bf86eeabba0a0fa7e1a4b3ae From 14fd1b9349e1e2635c4e8c851f50ae6315c6b192 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Mon, 4 Nov 2024 10:14:46 -0800 Subject: [PATCH 147/508] remove multiple blank lines --- src/Tools/ExternalAccess/Razor/Cohost/Handlers/InlayHints.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/InlayHints.cs b/src/Tools/ExternalAccess/Razor/Cohost/Handlers/InlayHints.cs index d9dfd3bac8f64..c04cc390f5090 100644 --- a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/InlayHints.cs +++ b/src/Tools/ExternalAccess/Razor/Cohost/Handlers/InlayHints.cs @@ -34,8 +34,6 @@ internal static class InlayHints public static Task ResolveInlayHintAsync(Document document, InlayHint request, CancellationToken cancellationToken) { Contract.ThrowIfNull(s_resolveCache, "Cache should never be null for resolve, since it should have been created by the original request"); - - var data = InlayHintResolveHandler.GetInlayHintResolveData(request); var options = GetOptions(data.DisplayAllOverride); return InlayHintResolveHandler.ResolveInlayHintAsync(document, request, s_resolveCache, data, options, cancellationToken); From 74aba692a6e3491c7ef3f9df859d568a04361758 Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Mon, 4 Nov 2024 10:47:00 -0800 Subject: [PATCH 148/508] Reduce allocations in AbstractAsynchronousTaggerProvider.ComputeDifference (#75639) This method is showing up in a scrolling and typing profile I'm looking at. This method computes the difference between two TagSpanIntervalTrees given an updated snapshot. To do so, it currently creates a new heap allocated TagSpan for each item in both given interval trees. However, it need not do that, as that TagSpan isn't passed out of this method. Instead, TagSpanIntervalTree now has an AddAllSpans method that populates to a collection of value tuples, which ComputeDifference can now use. --- .../Tagging/Utilities/TagSpanIntervalTree.cs | 8 ++++++++ ...ronousTaggerProvider.TagSource_ProduceTags.cs | 16 ++++++++-------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/EditorFeatures/Core/Shared/Tagging/Utilities/TagSpanIntervalTree.cs b/src/EditorFeatures/Core/Shared/Tagging/Utilities/TagSpanIntervalTree.cs index 85948abd1fd45..5ca068091ef55 100644 --- a/src/EditorFeatures/Core/Shared/Tagging/Utilities/TagSpanIntervalTree.cs +++ b/src/EditorFeatures/Core/Shared/Tagging/Utilities/TagSpanIntervalTree.cs @@ -118,6 +118,14 @@ public void AddAllSpans(ITextSnapshot textSnapshot, SegmentedList> tagSpans.Add(GetTranslatedTagSpan(tagSpan, textSnapshot)); } + /// + /// Spans will be added in sorted order + public void AddAllSpans(ITextSnapshot textSnapshot, SegmentedList<(SnapshotSpan, TTag)> tagSpans) + { + foreach (var tagSpan in _tree) + tagSpans.Add((GetTranslatedSpan(tagSpan, textSnapshot, _spanTrackingMode), tagSpan.Tag)); + } + /// /// Removes from all the tags spans in that intersect with any of /// the spans in . diff --git a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs index 0cc95245b81f2..f97fa6470a41e 100644 --- a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs +++ b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs @@ -629,8 +629,8 @@ private DiffResult ComputeDifference( TagSpanIntervalTree latestTree, TagSpanIntervalTree previousTree) { - using var _1 = SegmentedListPool.GetPooledList>(out var latestSpans); - using var _2 = SegmentedListPool.GetPooledList>(out var previousSpans); + using var _1 = SegmentedListPool.GetPooledList<(SnapshotSpan, TTag)>(out var latestSpans); + using var _2 = SegmentedListPool.GetPooledList<(SnapshotSpan, TTag)>(out var previousSpans); using var _3 = ArrayBuilder.GetInstance(out var added); using var _4 = ArrayBuilder.GetInstance(out var removed); @@ -646,8 +646,8 @@ private DiffResult ComputeDifference( while (latest != null && previous != null) { - var latestSpan = latest.Span; - var previousSpan = previous.Span; + var latestSpan = latest.Value.Span; + var previousSpan = previous.Value.Span; if (latestSpan.Start < previousSpan.Start) { @@ -675,7 +675,7 @@ private DiffResult ComputeDifference( } else { - if (!_dataSource.TagEquals(latest.Tag, previous.Tag)) + if (!_dataSource.TagEquals(latest.Value.Tag, previous.Value.Tag)) added.Add(latestSpan); latest = NextOrNull(ref latestEnumerator); @@ -686,19 +686,19 @@ private DiffResult ComputeDifference( while (latest != null) { - added.Add(latest.Span); + added.Add(latest.Value.Span); latest = NextOrNull(ref latestEnumerator); } while (previous != null) { - removed.Add(previous.Span); + removed.Add(previous.Value.Span); previous = NextOrNull(ref previousEnumerator); } return new DiffResult(new(added), new(removed)); - static TagSpan? NextOrNull(ref SegmentedList>.Enumerator enumerator) + static (SnapshotSpan Span, TTag Tag)? NextOrNull(ref SegmentedList<(SnapshotSpan, TTag)>.Enumerator enumerator) => enumerator.MoveNext() ? enumerator.Current : null; } From 00b8c9e89e65cc5446aa32c5a61aaa8c70887c96 Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Mon, 4 Nov 2024 10:47:25 -0800 Subject: [PATCH 149/508] Reduce allocations in ProjectSystemProjectFactory.ConvertMetadataReferencesToProjectReferences_NoLock (#75646) This method is pretty high traffic and it's use of the OfType Linq method is showing up as 0.2% of allocations in the speedometer profile I'm looking at of a solution open. Instead of using linq here, it's just as easy to do the ""is" check inline avoid the yield return state machinery. --- .../ProjectSystem/ProjectSystemProjectFactory.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectFactory.cs b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectFactory.cs index be792ae5846cf..3bf8563fdb923 100644 --- a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectFactory.cs +++ b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectFactory.cs @@ -555,22 +555,23 @@ private static ProjectUpdateState ConvertMetadataReferencesToProjectReferences_N // PERF: call GetRequiredProjectState instead of GetRequiredProject, otherwise creating a new project // might force all Project instances to get created. var projectState = solutionChanges.Solution.GetRequiredProjectState(projectIdToRetarget); - foreach (var reference in projectState.MetadataReferences.OfType()) + foreach (var reference in projectState.MetadataReferences) { - if (string.Equals(reference.FilePath, outputPath, StringComparison.OrdinalIgnoreCase)) + if (reference is PortableExecutableReference peReference + && string.Equals(peReference.FilePath, outputPath, StringComparison.OrdinalIgnoreCase)) { - projectUpdateState = projectUpdateState.WithIncrementalMetadataReferenceRemoved(reference); + projectUpdateState = projectUpdateState.WithIncrementalMetadataReferenceRemoved(peReference); - var projectReference = new ProjectReference(projectIdToReference, reference.Properties.Aliases, reference.Properties.EmbedInteropTypes); + var projectReference = new ProjectReference(projectIdToReference, peReference.Properties.Aliases, peReference.Properties.EmbedInteropTypes); var newSolution = solutionChanges.Solution - .RemoveMetadataReference(projectIdToRetarget, reference) + .RemoveMetadataReference(projectIdToRetarget, peReference) .AddProjectReference(projectIdToRetarget, projectReference); solutionChanges.UpdateSolutionForProjectAction(projectIdToRetarget, newSolution); projectUpdateState = GetReferenceInformation(projectIdToRetarget, projectUpdateState, out var projectInfo); projectUpdateState = projectUpdateState.WithProjectReferenceInfo(projectIdToRetarget, - projectInfo.WithConvertedProjectReference(reference.FilePath!, projectReference)); + projectInfo.WithConvertedProjectReference(peReference.FilePath!, projectReference)); // We have converted one, but you could have more than one reference with different aliases that // we need to convert, so we'll keep going From 203c6fa82eb4806d3799d0a400038a8e9e7db0ab Mon Sep 17 00:00:00 2001 From: David Barbet Date: Mon, 4 Nov 2024 10:50:44 -0800 Subject: [PATCH 150/508] Add wrapper service broker to allow for easy MEF consumption --- .../BrokeredServices/ServiceBrokerFactory.cs | 13 ++--- .../BrokeredServices/WrappedServiceBroker.cs | 48 +++++++++++++++++++ 2 files changed, 53 insertions(+), 8 deletions(-) create mode 100644 src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/WrappedServiceBroker.cs diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerFactory.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerFactory.cs index 8ad3f78c6dac2..30de915c94972 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerFactory.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerFactory.cs @@ -30,6 +30,7 @@ internal class ServiceBrokerFactory { private BrokeredServiceContainer? _container; private readonly ExportProvider _exportProvider; + private readonly WrappedServiceBroker _wrappedServiceBroker; private Task _bridgeCompletionTask; private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); private readonly ImmutableArray _onServiceBrokerInitialized; @@ -37,19 +38,15 @@ internal class ServiceBrokerFactory [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public ServiceBrokerFactory([ImportMany] IEnumerable onServiceBrokerInitialized, - ExportProvider exportProvider) + ExportProvider exportProvider, + WrappedServiceBroker wrappedServiceBroker) { _exportProvider = exportProvider; _bridgeCompletionTask = Task.CompletedTask; _onServiceBrokerInitialized = onServiceBrokerInitialized.ToImmutableArray(); + _wrappedServiceBroker = wrappedServiceBroker; } - /// - /// Returns a full-access service broker, but will throw if we haven't yet connected to the Dev Kit broker. - /// - [Export(typeof(SVsFullAccessServiceBroker))] - public IServiceBroker FullAccessServiceBroker => this.GetRequiredServiceBrokerContainer().GetFullAccessServiceBroker(); - /// /// Returns a full-access service broker, but will return null if we haven't yet connected to the Dev Kit broker. /// @@ -69,6 +66,7 @@ public async Task CreateAsync() Contract.ThrowIfFalse(_container == null, "We should only create one container."); _container = await BrokeredServiceContainer.CreateAsync(_exportProvider, _cancellationTokenSource.Token); + _wrappedServiceBroker.SetServiceBroker(_container.GetFullAccessServiceBroker()); foreach (var onInitialized in _onServiceBrokerInitialized) { @@ -99,4 +97,3 @@ public Task ShutdownAndWaitForCompletionAsync() return _bridgeCompletionTask; } } -#pragma warning restore RS0030 // Do not used banned APIs diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/WrappedServiceBroker.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/WrappedServiceBroker.cs new file mode 100644 index 0000000000000..6429c2e00270f --- /dev/null +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/WrappedServiceBroker.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Composition; +using System.IO.Pipelines; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.ServiceHub.Framework; +using Microsoft.VisualStudio.Composition; +using Microsoft.VisualStudio.Shell.ServiceBroker; + +namespace Microsoft.CodeAnalysis.LanguageServer.BrokeredServices; +#pragma warning restore RS0030 // Do not used banned APIs + +/// +/// Implements a wrapper around that allows clients to MEF import the service broker. +/// This wrapper will wait for the service broker to be available before invoking the requested method. +/// +[Export(typeof(SVsFullAccessServiceBroker)), Shared] +[Export(typeof(WrappedServiceBroker))] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal class WrappedServiceBroker() : IServiceBroker +{ + private readonly TaskCompletionSource _serviceBrokerTask = new(); + + internal void SetServiceBroker(IServiceBroker serviceBroker) + { + serviceBroker.AvailabilityChanged += (s, e) => AvailabilityChanged?.Invoke(s, e); + _serviceBrokerTask.SetResult(serviceBroker); + } + + public event EventHandler? AvailabilityChanged; + + public async ValueTask GetPipeAsync(ServiceMoniker serviceMoniker, ServiceActivationOptions options = default, CancellationToken cancellationToken = default) + { + var serviceBroker = await _serviceBrokerTask.Task; + return await serviceBroker.GetPipeAsync(serviceMoniker, options, cancellationToken); + } + + public async ValueTask GetProxyAsync(ServiceRpcDescriptor serviceDescriptor, ServiceActivationOptions options = default, CancellationToken cancellationToken = default) where T : class + { + var serviceBroker = await _serviceBrokerTask.Task; +#pragma warning disable ISB001 // Dispose of proxies - caller is responsible for disposing the proxy. + return await serviceBroker.GetProxyAsync(serviceDescriptor, options, cancellationToken); +#pragma warning restore ISB001 // Dispose of proxies + } +} From c00aff03c077cfb7ee017c689ab76e63580d6797 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Mon, 4 Nov 2024 11:13:37 -0800 Subject: [PATCH 151/508] Review feedback --- .../Handler/CodeLens/CodeLensResolveHandler.cs | 12 ++++++------ .../Handler/InlayHint/InlayHintHandler.cs | 16 +++++++++++----- .../Handler/InlayHint/InlayHintResolveHandler.cs | 10 ++-------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs b/src/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs index ba44e29e156f0..0064c37cc88ba 100644 --- a/src/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs +++ b/src/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs @@ -2,17 +2,17 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Composition; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeLens; -using Roslyn.Utilities; +using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Extensions; -using LSP = Roslyn.LanguageServer.Protocol; -using System.Text.Json; +using Roslyn.Utilities; using StreamJsonRpc; -using System.Composition; -using System; -using Microsoft.CodeAnalysis.Host.Mef; +using LSP = Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.CodeLens; diff --git a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs b/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs index 6edd35bf1cabe..3822fa6400adb 100644 --- a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs +++ b/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs @@ -50,11 +50,7 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(InlayHintParams request) internal static async Task GetInlayHintsAsync(Document document, TextDocumentIdentifier textDocumentIdentifier, LSP.Range range, InlineHintsOptions options, bool displayAllOverride, InlayHintCache inlayHintCache, CancellationToken cancellationToken) { var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); - var textSpan = ProtocolConversions.RangeToTextSpan(range, text); - - var inlineHintService = document.GetRequiredLanguageService(); - var hints = await inlineHintService.GetInlineHintsAsync(document, textSpan, options, displayAllOverride, cancellationToken).ConfigureAwait(false); - + var hints = await CalculateInlayHintsAsync(document, range, options, displayAllOverride, cancellationToken).ConfigureAwait(false); var syntaxVersion = await document.GetSyntaxVersionAsync(cancellationToken).ConfigureAwait(false); // Store the members in the resolve cache so that when we get a resolve request for a particular @@ -98,6 +94,16 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(InlayHintParams request) return inlayHints; } + internal static async Task> CalculateInlayHintsAsync(Document document, LSP.Range range, InlineHintsOptions options, bool displayAllOverride, CancellationToken cancellationToken) + { + var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); + var textSpan = ProtocolConversions.RangeToTextSpan(range, text); + + var inlineHintService = document.GetRequiredLanguageService(); + var hints = await inlineHintService.GetInlineHintsAsync(document, textSpan, options, displayAllOverride, cancellationToken).ConfigureAwait(false); + return hints; + } + /// /// Goes through the tagged text of the hint and trims off leading and trailing spaces. /// If there is leading or trailing space, then we want to add padding to the left and right accordingly. diff --git a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs b/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs index 39be9f69d66c5..08cc5c33bd95e 100644 --- a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs +++ b/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.InlineHints; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.LanguageServer.Protocol; using Roslyn.Utilities; using StreamJsonRpc; @@ -64,12 +63,7 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.InlayHint request) { // It is very possible that the cache no longer contains the hint being resolved (for example, multiple documents open side by side). // Instead of throwing, we can recompute the hints since we've already verified above that the version has not changed. - - var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); - var textSpan = ProtocolConversions.RangeToTextSpan(resolveData.Range, text); - - var inlineHintService = document.GetRequiredLanguageService(); - var hints = await inlineHintService.GetInlineHintsAsync(document, textSpan, options, resolveData.DisplayAllOverride, cancellationToken).ConfigureAwait(false); + var hints = await InlayHintHandler.CalculateInlayHintsAsync(document, resolveData.Range, options, resolveData.DisplayAllOverride, cancellationToken).ConfigureAwait(false); inlineHintToResolve = hints[resolveData.ListIndex]; } @@ -82,7 +76,7 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.InlayHint request) private static InlineHint? GetCacheEntry(InlayHintResolveData resolveData, InlayHintCache inlayHintCache) { var cacheEntry = inlayHintCache.GetCachedEntry(resolveData.ResultId); - return cacheEntry is null ? null : cacheEntry.InlayHintMembers[resolveData.ListIndex]; + return cacheEntry?.InlayHintMembers[resolveData.ListIndex]; } internal static InlayHintResolveData GetInlayHintResolveData(LSP.InlayHint inlayHint) From 9a64851f112854af50fef8fd7f206887d09b032b Mon Sep 17 00:00:00 2001 From: David Barbet Date: Mon, 4 Nov 2024 11:25:52 -0800 Subject: [PATCH 152/508] Do not include source generated documents in related document results --- .../AbstractRelatedDocumentsService.cs | 2 +- .../RelatedDocuments/RelatedDocumentsTests.cs | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/RelatedDocuments/AbstractRelatedDocumentsService.cs b/src/Features/Core/Portable/RelatedDocuments/AbstractRelatedDocumentsService.cs index 42a8920a612fd..3e103bc466b37 100644 --- a/src/Features/Core/Portable/RelatedDocuments/AbstractRelatedDocumentsService.cs +++ b/src/Features/Core/Portable/RelatedDocuments/AbstractRelatedDocumentsService.cs @@ -135,7 +135,7 @@ void ProduceItems( foreach (var syntaxReference in symbol.DeclaringSyntaxReferences) { var documentId = solution.GetDocument(syntaxReference.SyntaxTree)?.Id; - if (documentId != null && seenDocumentIds.Add(documentId)) + if (documentId != null && !documentId.IsSourceGenerated && seenDocumentIds.Add(documentId)) callback(documentId); } } diff --git a/src/LanguageServer/ProtocolUnitTests/RelatedDocuments/RelatedDocumentsTests.cs b/src/LanguageServer/ProtocolUnitTests/RelatedDocuments/RelatedDocumentsTests.cs index 2cc292339e553..b1706da0ca08f 100644 --- a/src/LanguageServer/ProtocolUnitTests/RelatedDocuments/RelatedDocumentsTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/RelatedDocuments/RelatedDocumentsTests.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.LanguageServer.Handler; using Roslyn.LanguageServer.Protocol; using Roslyn.Test.Utilities; +using Roslyn.Test.Utilities.TestGenerators; using Roslyn.Utilities; using Xunit; using Xunit.Abstractions; @@ -174,4 +175,39 @@ class Y AssertJsonEquals(results2, expectedResult); } + + [Theory, CombinatorialData] + public async Task DoesNotIncludeSourceGeneratedDocuments(bool mutatingLspWorkspace, bool useProgress) + { + var source = + """ + namespace M + { + class A + { + public {|caret:|}B b; + } + } + """; + var generated = + """ + namespace M + { + class B + { + } + } + """; + + await using var testLspServer = await CreateTestLspServerAsync(source, mutatingLspWorkspace); + await AddGeneratorAsync(new SingleFileTestGenerator(generated), testLspServer.TestWorkspace); + + var project = testLspServer.TestWorkspace.CurrentSolution.Projects.Single(); + var results = await RunGetRelatedDocumentsAsync( + testLspServer, + project.Documents.First().GetURI(), + useProgress: useProgress); + + Assert.Empty(results); + } } From ba3372b4d8158301ccbef4d7d8964ed885acd417 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Mon, 4 Nov 2024 11:59:53 -0800 Subject: [PATCH 153/508] fix indentation --- .../RelatedDocuments/RelatedDocumentsTests.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/LanguageServer/ProtocolUnitTests/RelatedDocuments/RelatedDocumentsTests.cs b/src/LanguageServer/ProtocolUnitTests/RelatedDocuments/RelatedDocumentsTests.cs index b1706da0ca08f..6679d9089c729 100644 --- a/src/LanguageServer/ProtocolUnitTests/RelatedDocuments/RelatedDocumentsTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/RelatedDocuments/RelatedDocumentsTests.cs @@ -180,15 +180,15 @@ class Y public async Task DoesNotIncludeSourceGeneratedDocuments(bool mutatingLspWorkspace, bool useProgress) { var source = - """ - namespace M + """ + namespace M + { + class A { - class A - { - public {|caret:|}B b; - } + public {|caret:|}B b; } - """; + } + """; var generated = """ namespace M From 0a74c5e9c1e688e30f138e0a2d836e5216a88ceb Mon Sep 17 00:00:00 2001 From: David Barbet Date: Mon, 4 Nov 2024 13:06:29 -0800 Subject: [PATCH 154/508] Also export as IServiceBroker to allow mefv2 imports --- .../BrokeredServices/WrappedServiceBroker.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/WrappedServiceBroker.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/WrappedServiceBroker.cs index 6429c2e00270f..c44806bde0377 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/WrappedServiceBroker.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/WrappedServiceBroker.cs @@ -17,6 +17,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.BrokeredServices; /// This wrapper will wait for the service broker to be available before invoking the requested method. /// [Export(typeof(SVsFullAccessServiceBroker)), Shared] +[Export(typeof(IServiceBroker))] [Export(typeof(WrappedServiceBroker))] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] From b317e3136cde8afc4d27eef758d3c04076b79944 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 4 Nov 2024 13:14:48 -0800 Subject: [PATCH 155/508] Fixup tests --- .../CSharp/Portable/Parser/LanguageParser.cs | 15 +- .../CSharp/Portable/Parser/SyntaxParser.cs | 9 +- .../Semantics/PatternMatchingTestBase.cs | 9 + .../Semantics/PatternMatchingTests_Scope.cs | 163 ++++++++++++------ 4 files changed, 138 insertions(+), 58 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index d137c7f522e2c..24e7222e612f6 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -15,6 +15,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax { + using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Syntax.InternalSyntax; internal sealed partial class LanguageParser : SyntaxParser @@ -9145,7 +9146,6 @@ private ForStatementSyntax ParseForStatement(SyntaxList att // Pulled out as we need to track this when parsing incrementors to place skipped tokens. SyntaxToken secondSemicolonToken; - var forStatement = _syntaxFactory.ForStatement( attributes, forToken, @@ -9160,7 +9160,7 @@ private ForStatementSyntax ParseForStatement(SyntaxList att incrementors: this.CurrentToken.Kind != SyntaxKind.CloseParenToken ? this.ParseForStatementExpressionList(ref secondSemicolonToken) : default, - this.EatToken(SyntaxKind.CloseParenToken), + eatUnexpectedTokensAndCloseParenToken(), ParseEmbeddedStatement()); _termState = saveTerm; @@ -9256,6 +9256,17 @@ SyntaxToken eatCommaOrSemicolon() return this.EatToken(SyntaxKind.SemicolonToken); } } + + SyntaxToken eatUnexpectedTokensAndCloseParenToken() + { + var skippedTokens = _pool.Allocate(); + + while (this.CurrentToken.Kind is SyntaxKind.SemicolonToken or SyntaxKind.CommaToken) + skippedTokens.Add(this.EatTokenWithPrejudice(SyntaxKind.CloseParenToken)); + + var result = this.EatToken(SyntaxKind.CloseParenToken); + return AddLeadingSkippedSyntax(result, _pool.ToTokenListAndFree(skippedTokens).Node); + } } private bool IsEndOfForStatementArgument() diff --git a/src/Compilers/CSharp/Portable/Parser/SyntaxParser.cs b/src/Compilers/CSharp/Portable/Parser/SyntaxParser.cs index 71b2a9c52bc36..4c9e5502030c4 100644 --- a/src/Compilers/CSharp/Portable/Parser/SyntaxParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/SyntaxParser.cs @@ -890,13 +890,20 @@ protected static SyntaxDiagnosticInfo MakeError(ErrorCode code, params object[] return new SyntaxDiagnosticInfo(code, args); } - protected TNode AddLeadingSkippedSyntax(TNode node, GreenNode skippedSyntax) where TNode : CSharpSyntaxNode +#nullable enable + + protected TNode AddLeadingSkippedSyntax(TNode node, GreenNode? skippedSyntax) where TNode : CSharpSyntaxNode { + if (skippedSyntax is null) + return node; + var oldToken = node as SyntaxToken ?? node.GetFirstToken(); var newToken = AddSkippedSyntax(oldToken, skippedSyntax, trailing: false); return SyntaxFirstTokenReplacer.Replace(node, oldToken, newToken, skippedSyntax.FullWidth); } +#nullable disable + protected void AddTrailingSkippedSyntax(SyntaxListBuilder list, GreenNode skippedSyntax) { list[list.Count - 1] = AddTrailingSkippedSyntax((CSharpSyntaxNode)list[list.Count - 1], skippedSyntax); diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTestBase.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTestBase.cs index 8d77f638ce690..d37a1bb88a5b7 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTestBase.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTestBase.cs @@ -350,6 +350,9 @@ protected static void AssertContainedInDeclaratorArguments(SingleVariableDesigna Assert.True(decl.Ancestors().OfType().First().ArgumentList.Contains(decl)); } + protected static void AssertNotContainedInDeclaratorArguments(SingleVariableDesignationSyntax decl) + => Assert.Empty(decl.Ancestors().OfType()); + protected static void AssertContainedInDeclaratorArguments(params SingleVariableDesignationSyntax[] decls) { foreach (var decl in decls) @@ -358,6 +361,12 @@ protected static void AssertContainedInDeclaratorArguments(params SingleVariable } } + protected static void AssertNotContainedInDeclaratorArguments(params SingleVariableDesignationSyntax[] decls) + { + foreach (var decl in decls) + AssertNotContainedInDeclaratorArguments(decl); + } + protected static void VerifyModelNotSupported( SemanticModel model, SingleVariableDesignationSyntax designation, diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests_Scope.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests_Scope.cs index 3f5a3e860c02c..7e5edc20234a4 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests_Scope.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests_Scope.cs @@ -12394,48 +12394,81 @@ void Test14() } "; var compilation = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular); - int[] exclude = new int[] { (int)ErrorCode.ERR_BadVarDecl, - (int)ErrorCode.ERR_SyntaxError, - (int)ErrorCode.ERR_UnexpectedToken, - (int)ErrorCode.WRN_UnreferencedVar, - (int)ErrorCode.ERR_UseDefViolation - }; + int[] exclude = [ + (int)ErrorCode.ERR_BadVarDecl, + (int)ErrorCode.ERR_SyntaxError, + (int)ErrorCode.ERR_UnexpectedToken, + (int)ErrorCode.WRN_UnreferencedVar, + (int)ErrorCode.ERR_UseDefViolation, + (int)ErrorCode.ERR_AbstractAndExtern, + (int)ErrorCode.ERR_SemicolonExpected, + (int)ErrorCode.ERR_CloseParenExpected, + ]; compilation.GetDiagnostics().Where(d => !exclude.Contains(d.Code)).Verify( - // (109,13): error CS1023: Embedded statement cannot be a declaration or labeled statement - // var y12 = 12; - Diagnostic(ErrorCode.ERR_BadEmbeddedStmt, "var y12 = 12;").WithLocation(109, 13), + // (12,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(12, 22), + // (22,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(22, 22), + // (33,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(33, 22), // (34,32): error CS0136: A local or parameter named 'x4' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // Dummy(true is var x4 && x4) Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x4").WithArguments("x4").WithLocation(34, 32), + // (41,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(41, 22), // (42,20): error CS0841: Cannot use local variable 'x6' before it is declared // Dummy(x6 && true is var x6) Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x6").WithArguments("x6").WithLocation(42, 20), + // (49,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(49, 22), // (53,17): error CS0136: A local or parameter named 'x7' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // var x7 = 12; Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x7").WithArguments("x7").WithLocation(53, 17), + // (60,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(60, 22), // (65,34): error CS0103: The name 'x8' does not exist in the current context // System.Console.WriteLine(x8); Diagnostic(ErrorCode.ERR_NameNotInContext, "x8").WithArguments("x8").WithLocation(65, 34), - // (65,9): warning CS0162: Unreachable code detected - // System.Console.WriteLine(x8); - Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(65, 9), + // (70,23): error CS0103: The name 'b1' does not exist in the current context + // for (bool a1, b1( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b1").WithArguments("b1").WithLocation(70, 23), + // (75,27): error CS0103: The name 'b2' does not exist in the current context + // for (bool a2, b2( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b2").WithArguments("b2").WithLocation(75, 27), // (76,36): error CS0136: A local or parameter named 'x9' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // Dummy(true is var x9 && x9) // 2 Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x9").WithArguments("x9").WithLocation(76, 36), + // (84,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(84, 22), // (85,20): error CS0103: The name 'y10' does not exist in the current context // Dummy(y10 is var x10) Diagnostic(ErrorCode.ERR_NameNotInContext, "y10").WithArguments("y10").WithLocation(85, 20), + // (106,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(106, 22), // (107,20): error CS0103: The name 'y12' does not exist in the current context // Dummy(y12 is var x12) Diagnostic(ErrorCode.ERR_NameNotInContext, "y12").WithArguments("y12").WithLocation(107, 20), + // (109,13): error CS1023: Embedded statement cannot be a declaration or labeled statement + // var y12 = 12; + Diagnostic(ErrorCode.ERR_BadEmbeddedStmt, "var y12 = 12;").WithLocation(109, 13), // (109,17): warning CS0219: The variable 'y12' is assigned but its value is never used // var y12 = 12; Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "y12").WithArguments("y12").WithLocation(109, 17), + // (122,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(122, 22), // (124,29): error CS0128: A local variable or function named 'x14' is already defined in this scope // 2 is var x14, - Diagnostic(ErrorCode.ERR_LocalDuplicate, "x14").WithArguments("x14").WithLocation(124, 29) - ); + Diagnostic(ErrorCode.ERR_LocalDuplicate, "x14").WithArguments("x14").WithLocation(124, 29)); var tree = compilation.SyntaxTrees.Single(); var model = compilation.GetSemanticModel(tree); @@ -12443,39 +12476,39 @@ void Test14() var x1Decl = GetPatternDeclarations(tree, "x1").Single(); var x1Ref = GetReferences(tree, "x1").ToArray(); Assert.Equal(2, x1Ref.Length); - AssertContainedInDeclaratorArguments(x1Decl); + AssertNotContainedInDeclaratorArguments(x1Decl); VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x1Decl, x1Ref); var x2Decl = GetPatternDeclarations(tree, "x2").Single(); var x2Ref = GetReferences(tree, "x2").ToArray(); Assert.Equal(2, x2Ref.Length); - AssertContainedInDeclaratorArguments(x2Decl); + AssertNotContainedInDeclaratorArguments(x2Decl); VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x2Decl, x2Ref); var x4Decl = GetPatternDeclarations(tree, "x4").Single(); var x4Ref = GetReferences(tree, "x4").ToArray(); Assert.Equal(3, x4Ref.Length); - AssertContainedInDeclaratorArguments(x4Decl); + AssertNotContainedInDeclaratorArguments(x4Decl); VerifyNotAPatternLocal(model, x4Ref[0]); VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x4Decl, x4Ref[1], x4Ref[2]); var x6Decl = GetPatternDeclarations(tree, "x6").Single(); var x6Ref = GetReferences(tree, "x6").ToArray(); Assert.Equal(2, x6Ref.Length); - AssertContainedInDeclaratorArguments(x6Decl); + AssertNotContainedInDeclaratorArguments(x6Decl); VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x6Decl, x6Ref); var x7Decl = GetPatternDeclarations(tree, "x7").Single(); var x7Ref = GetReferences(tree, "x7").ToArray(); Assert.Equal(2, x7Ref.Length); - AssertContainedInDeclaratorArguments(x7Decl); + AssertNotContainedInDeclaratorArguments(x7Decl); VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x7Decl, x7Ref[0]); VerifyNotAPatternLocal(model, x7Ref[1]); var x8Decl = GetPatternDeclarations(tree, "x8").Single(); var x8Ref = GetReferences(tree, "x8").ToArray(); Assert.Equal(3, x8Ref.Length); - AssertContainedInDeclaratorArguments(x8Decl); + AssertNotContainedInDeclaratorArguments(x8Decl); VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x8Decl, x8Ref[0], x8Ref[1]); VerifyNotInScope(model, x8Ref[2]); @@ -12483,7 +12516,7 @@ void Test14() var x9Ref = GetReferences(tree, "x9").ToArray(); Assert.Equal(2, x9Decl.Length); Assert.Equal(4, x9Ref.Length); - AssertContainedInDeclaratorArguments(x9Decl); + AssertNotContainedInDeclaratorArguments(x9Decl); VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x9Decl[0], x9Ref[0], x9Ref[1]); VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x9Decl[1], x9Ref[2], x9Ref[3]); @@ -12499,7 +12532,7 @@ void Test14() var x14Ref = GetReferences(tree, "x14").ToArray(); Assert.Equal(2, x14Decl.Length); Assert.Equal(2, x14Ref.Length); - AssertContainedInDeclaratorArguments(x14Decl); + AssertNotContainedInDeclaratorArguments(x14Decl); VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x14Decl[0], x14Ref); VerifyModelForDeclarationOrVarPatternDuplicateInSameScope(model, x14Decl[1]); } @@ -12553,66 +12586,86 @@ void Test9() } "; var compilation = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular); - int[] exclude = new int[] { (int)ErrorCode.ERR_BadVarDecl, - (int)ErrorCode.ERR_SyntaxError, - (int)ErrorCode.ERR_UnexpectedToken, - (int)ErrorCode.WRN_UnreferencedVar, - (int)ErrorCode.ERR_CloseParenExpected - }; + int[] exclude = [ + (int)ErrorCode.ERR_BadVarDecl, + (int)ErrorCode.ERR_SyntaxError, + (int)ErrorCode.ERR_UnexpectedToken, + (int)ErrorCode.WRN_UnreferencedVar, + (int)ErrorCode.ERR_CloseParenExpected, + (int)ErrorCode.ERR_SemicolonExpected, + ]; compilation.GetDiagnostics().Where(d => !exclude.Contains(d.Code)).Verify( - // (13,32): error CS0128: A local variable or function named 'x4' is already defined in this scope - // Dummy(true is var x4 && x4) - Diagnostic(ErrorCode.ERR_LocalDuplicate, "x4").WithArguments("x4").WithLocation(13, 32), - // (21,32): error CS0128: A local variable or function named 'x7' is already defined in this scope + // (31,41): error CS1513: } expected + // Dummy(true is var x8 && x8)) + Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(31, 41), + // (40,41): error CS1513: } expected + // Dummy(true is var x9 && x9)) + Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(40, 41), + // (12,22): error CS0841: Cannot use local variable 'x4' before it is declared + // for (bool d, x4( + Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x4").WithArguments("x4").WithLocation(12, 22), + // (20,30): error CS0103: The name 'b' does not exist in the current context + // for (bool x7 = true, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(20, 30), + // (21,32): error CS0136: A local or parameter named 'x7' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // Dummy(true is var x7 && x7) - Diagnostic(ErrorCode.ERR_LocalDuplicate, "x7").WithArguments("x7").WithLocation(21, 32), + Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x7").WithArguments("x7").WithLocation(21, 32), // (20,19): warning CS0219: The variable 'x7' is assigned but its value is never used // for (bool x7 = true, b( Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "x7").WithArguments("x7").WithLocation(20, 19), - // (29,37): error CS0128: A local variable or function named 'x8' is already defined in this scope + // (28,21): error CS0103: The name 'b1' does not exist in the current context + // for (bool d,b1(Dummy(true is var x8 && x8)], + Diagnostic(ErrorCode.ERR_NameNotInContext, "b1").WithArguments("b1").WithLocation(28, 21), + // (28,42): error CS0136: A local or parameter named 'x8' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter + // for (bool d,b1(Dummy(true is var x8 && x8)], + Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x8").WithArguments("x8").WithLocation(28, 42), + // (29,16): error CS0103: The name 'b2' does not exist in the current context // b2(Dummy(true is var x8 && x8)); - Diagnostic(ErrorCode.ERR_LocalDuplicate, "x8").WithArguments("x8").WithLocation(29, 37), + Diagnostic(ErrorCode.ERR_NameNotInContext, "b2").WithArguments("b2").WithLocation(29, 16), + // (29,37): error CS0136: A local or parameter named 'x8' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter + // b2(Dummy(true is var x8 && x8)); + Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x8").WithArguments("x8").WithLocation(29, 37), // (30,32): error CS0136: A local or parameter named 'x8' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // Dummy(true is var x8 && x8); Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x8").WithArguments("x8").WithLocation(30, 32), - // (31,32): error CS0136: A local or parameter named 'x8' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter - // Dummy(true is var x8 && x8)) - Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x8").WithArguments("x8").WithLocation(31, 32), - // (37,23): error CS0841: Cannot use local variable 'x9' before it is declared + // (37,23): error CS0103: The name 'x9' does not exist in the current context // for (bool b = x9, - Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x9").WithArguments("x9").WithLocation(37, 23), + Diagnostic(ErrorCode.ERR_NameNotInContext, "x9").WithArguments("x9").WithLocation(37, 23), + // (38,16): error CS0103: The name 'b2' does not exist in the current context + // b2(Dummy(true is var x9 && x9)); + Diagnostic(ErrorCode.ERR_NameNotInContext, "b2").WithArguments("b2").WithLocation(38, 16), // (39,32): error CS0136: A local or parameter named 'x9' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // Dummy(true is var x9 && x9); Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x9").WithArguments("x9").WithLocation(39, 32), // (40,32): error CS0136: A local or parameter named 'x9' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // Dummy(true is var x9 && x9)) - Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x9").WithArguments("x9").WithLocation(40, 32) - ); + Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x9").WithArguments("x9").WithLocation(40, 32)); var tree = compilation.SyntaxTrees.Single(); var model = compilation.GetSemanticModel(tree); var x4Decl = GetPatternDeclarations(tree, "x4").Single(); - var x4Ref = GetReferences(tree, "x4").Single(); - AssertContainedInDeclaratorArguments(x4Decl); - VerifyModelForDeclarationOrVarPatternDuplicateInSameScope(model, x4Decl); - VerifyNotAPatternLocal(model, x4Ref); + var x4Ref = GetReferences(tree, "x4").ToArray(); + Assert.Equal(2, x4Ref.Length); + AssertNotContainedInDeclaratorArguments(x4Decl); + VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x4Decl, x4Ref[0]); + VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x4Decl, x4Ref[1]); var x7Decl = GetPatternDeclarations(tree, "x7").Single(); var x7Ref = GetReferences(tree, "x7").Single(); - AssertContainedInDeclaratorArguments(x7Decl); - VerifyModelForDeclarationOrVarPatternDuplicateInSameScope(model, x7Decl); - VerifyNotAPatternLocal(model, x7Ref); + AssertNotContainedInDeclaratorArguments(x7Decl); + VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x7Decl); + VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x7Decl, x7Ref); var x8Decl = GetPatternDeclarations(tree, "x8").ToArray(); var x8Ref = GetReferences(tree, "x8").ToArray(); Assert.Equal(4, x8Decl.Length); Assert.Equal(4, x8Ref.Length); - AssertContainedInDeclaratorArguments(x8Decl[0]); - AssertContainedInDeclaratorArguments(x8Decl[1]); - VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x8Decl[0], x8Ref[0], x8Ref[1]); - VerifyModelForDeclarationOrVarPatternDuplicateInSameScope(model, x8Decl[1]); + AssertNotContainedInDeclaratorArguments(x8Decl[0]); + AssertNotContainedInDeclaratorArguments(x8Decl[1]); + VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x8Decl[0], x8Ref[0]); + VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x8Decl[1], x8Ref[1]); VerifyModelForDeclarationOrVarSimplePattern(model, x8Decl[2], x8Ref[2]); VerifyModelForDeclarationOrVarSimplePattern(model, x8Decl[3], x8Ref[3]); @@ -12620,8 +12673,8 @@ void Test9() var x9Ref = GetReferences(tree, "x9").ToArray(); Assert.Equal(3, x9Decl.Length); Assert.Equal(4, x9Ref.Length); - AssertContainedInDeclaratorArguments(x9Decl[0]); - VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x9Decl[0], x9Ref[0], x9Ref[1]); + AssertNotContainedInDeclaratorArguments(x9Decl[0]); + VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x9Decl[0], x9Ref[1]); VerifyModelForDeclarationOrVarSimplePattern(model, x9Decl[1], x9Ref[2]); VerifyModelForDeclarationOrVarSimplePattern(model, x9Decl[2], x9Ref[3]); } From 0e581c421f52ec5eefb3cf73ca117d0497bedc4a Mon Sep 17 00:00:00 2001 From: David Barbet Date: Mon, 4 Nov 2024 13:28:30 -0800 Subject: [PATCH 156/508] remove broken export --- .../BrokeredServices/WrappedServiceBroker.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/WrappedServiceBroker.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/WrappedServiceBroker.cs index c44806bde0377..6429c2e00270f 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/WrappedServiceBroker.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/WrappedServiceBroker.cs @@ -17,7 +17,6 @@ namespace Microsoft.CodeAnalysis.LanguageServer.BrokeredServices; /// This wrapper will wait for the service broker to be available before invoking the requested method. /// [Export(typeof(SVsFullAccessServiceBroker)), Shared] -[Export(typeof(IServiceBroker))] [Export(typeof(WrappedServiceBroker))] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] From 1a1addf95c2c50593126517247d847dcd0c9b252 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 4 Nov 2024 13:33:53 -0800 Subject: [PATCH 157/508] Fixup tests --- .../Test/Emit3/Semantics/OutVarTests.cs | 164 ++++++++++++------ 1 file changed, 108 insertions(+), 56 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/OutVarTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/OutVarTests.cs index 9ee3226572928..7dd853f221117 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/OutVarTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/OutVarTests.cs @@ -20954,6 +20954,9 @@ private static void AssertContainedInDeclaratorArguments(DeclarationExpressionSy Assert.True(decl.Ancestors().OfType().First().ArgumentList.Contains(decl)); } + private static void AssertNotContainedInDeclaratorArguments(DeclarationExpressionSyntax decl) + => Assert.Empty(decl.Ancestors().OfType()); + private static void AssertContainedInDeclaratorArguments(params DeclarationExpressionSyntax[] decls) { foreach (var decl in decls) @@ -21562,48 +21565,80 @@ static bool TakeOutParam(object y, out bool x) } "; var compilation = CreateCompilationWithMscorlib461(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular); - int[] exclude = new int[] { (int)ErrorCode.ERR_BadVarDecl, - (int)ErrorCode.ERR_SyntaxError, - (int)ErrorCode.ERR_UnexpectedToken, - (int)ErrorCode.WRN_UnreferencedVar, - (int)ErrorCode.ERR_UseDefViolation - }; + int[] exclude = [ + (int)ErrorCode.ERR_BadVarDecl, + (int)ErrorCode.ERR_SyntaxError, + (int)ErrorCode.ERR_UnexpectedToken, + (int)ErrorCode.WRN_UnreferencedVar, + (int)ErrorCode.ERR_UseDefViolation, + (int)ErrorCode.ERR_SemicolonExpected, + (int)ErrorCode.ERR_CloseParenExpected, + ]; compilation.GetDiagnostics().Where(d => !exclude.Contains(d.Code)).Verify( - // (109,13): error CS1023: Embedded statement cannot be a declaration or labeled statement - // var y12 = 12; - Diagnostic(ErrorCode.ERR_BadEmbeddedStmt, "var y12 = 12;").WithLocation(109, 13), + // (12,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(12, 22), + // (22,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(22, 22), + // (33,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(33, 22), // (34,47): error CS0136: A local or parameter named 'x4' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // Dummy(TakeOutParam(true, out var x4) && x4) Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x4").WithArguments("x4").WithLocation(34, 47), + // (41,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(41, 22), // (42,20): error CS0841: Cannot use local variable 'x6' before it is declared // Dummy(x6 && TakeOutParam(true, out var x6)) Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x6").WithArguments("x6").WithLocation(42, 20), + // (49,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(49, 22), // (53,17): error CS0136: A local or parameter named 'x7' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // var x7 = 12; Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x7").WithArguments("x7").WithLocation(53, 17), + // (60,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(60, 22), // (65,34): error CS0103: The name 'x8' does not exist in the current context // System.Console.WriteLine(x8); Diagnostic(ErrorCode.ERR_NameNotInContext, "x8").WithArguments("x8").WithLocation(65, 34), - // (65,9): warning CS0162: Unreachable code detected - // System.Console.WriteLine(x8); - Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(65, 9), + // (70,23): error CS0103: The name 'b1' does not exist in the current context + // for (bool a1, b1( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b1").WithArguments("b1").WithLocation(70, 23), + // (75,27): error CS0103: The name 'b2' does not exist in the current context + // for (bool a2, b2( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b2").WithArguments("b2").WithLocation(75, 27), // (76,51): error CS0136: A local or parameter named 'x9' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // Dummy(TakeOutParam(true, out var x9) && x9) // 2 Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x9").WithArguments("x9").WithLocation(76, 51), + // (84,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(84, 22), // (85,33): error CS0103: The name 'y10' does not exist in the current context // Dummy(TakeOutParam(y10, out var x10)) Diagnostic(ErrorCode.ERR_NameNotInContext, "y10").WithArguments("y10").WithLocation(85, 33), + // (106,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(106, 22), // (107,33): error CS0103: The name 'y12' does not exist in the current context // Dummy(TakeOutParam(y12, out var x12)) Diagnostic(ErrorCode.ERR_NameNotInContext, "y12").WithArguments("y12").WithLocation(107, 33), + // (109,13): error CS1023: Embedded statement cannot be a declaration or labeled statement + // var y12 = 12; + Diagnostic(ErrorCode.ERR_BadEmbeddedStmt, "var y12 = 12;").WithLocation(109, 13), // (109,17): warning CS0219: The variable 'y12' is assigned but its value is never used // var y12 = 12; Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "y12").WithArguments("y12").WithLocation(109, 17), - // (124,44): error CS0128: A local variable named 'x14' is already defined in this scope + // (122,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(122, 22), + // (124,44): error CS0128: A local variable or function named 'x14' is already defined in this scope // TakeOutParam(2, out var x14), - Diagnostic(ErrorCode.ERR_LocalDuplicate, "x14").WithArguments("x14").WithLocation(124, 44) - ); + Diagnostic(ErrorCode.ERR_LocalDuplicate, "x14").WithArguments("x14").WithLocation(124, 44)); var tree = compilation.SyntaxTrees.Single(); var model = compilation.GetSemanticModel(tree); @@ -21611,39 +21646,39 @@ static bool TakeOutParam(object y, out bool x) var x1Decl = GetOutVarDeclarations(tree, "x1").Single(); var x1Ref = GetReferences(tree, "x1").ToArray(); Assert.Equal(2, x1Ref.Length); - AssertContainedInDeclaratorArguments(x1Decl); + AssertNotContainedInDeclaratorArguments(x1Decl); VerifyModelForOutVarWithoutDataFlow(model, x1Decl, x1Ref); var x2Decl = GetOutVarDeclarations(tree, "x2").Single(); var x2Ref = GetReferences(tree, "x2").ToArray(); Assert.Equal(2, x2Ref.Length); - AssertContainedInDeclaratorArguments(x2Decl); + AssertNotContainedInDeclaratorArguments(x2Decl); VerifyModelForOutVarWithoutDataFlow(model, x2Decl, x2Ref); var x4Decl = GetOutVarDeclarations(tree, "x4").Single(); var x4Ref = GetReferences(tree, "x4").ToArray(); Assert.Equal(3, x4Ref.Length); - AssertContainedInDeclaratorArguments(x4Decl); + AssertNotContainedInDeclaratorArguments(x4Decl); VerifyNotAnOutLocal(model, x4Ref[0]); VerifyModelForOutVarWithoutDataFlow(model, x4Decl, x4Ref[1], x4Ref[2]); var x6Decl = GetOutVarDeclarations(tree, "x6").Single(); var x6Ref = GetReferences(tree, "x6").ToArray(); Assert.Equal(2, x6Ref.Length); - AssertContainedInDeclaratorArguments(x6Decl); + AssertNotContainedInDeclaratorArguments(x6Decl); VerifyModelForOutVarWithoutDataFlow(model, x6Decl, x6Ref); var x7Decl = GetOutVarDeclarations(tree, "x7").Single(); var x7Ref = GetReferences(tree, "x7").ToArray(); Assert.Equal(2, x7Ref.Length); - AssertContainedInDeclaratorArguments(x7Decl); + AssertNotContainedInDeclaratorArguments(x7Decl); VerifyModelForOutVarWithoutDataFlow(model, x7Decl, x7Ref[0]); VerifyNotAnOutLocal(model, x7Ref[1]); var x8Decl = GetOutVarDeclarations(tree, "x8").Single(); var x8Ref = GetReferences(tree, "x8").ToArray(); Assert.Equal(3, x8Ref.Length); - AssertContainedInDeclaratorArguments(x8Decl); + AssertNotContainedInDeclaratorArguments(x8Decl); VerifyModelForOutVarWithoutDataFlow(model, x8Decl, x8Ref[0], x8Ref[1]); VerifyNotInScope(model, x8Ref[2]); @@ -21651,7 +21686,7 @@ static bool TakeOutParam(object y, out bool x) var x9Ref = GetReferences(tree, "x9").ToArray(); Assert.Equal(2, x9Decl.Length); Assert.Equal(4, x9Ref.Length); - AssertContainedInDeclaratorArguments(x9Decl); + Assert.All(x9Decl, x9Decl => AssertNotContainedInDeclaratorArguments(x9Decl)); VerifyModelForOutVarWithoutDataFlow(model, x9Decl[0], x9Ref[0], x9Ref[1]); VerifyModelForOutVarWithoutDataFlow(model, x9Decl[1], x9Ref[2], x9Ref[3]); @@ -21667,7 +21702,7 @@ static bool TakeOutParam(object y, out bool x) var x14Ref = GetReferences(tree, "x14").ToArray(); Assert.Equal(2, x14Decl.Length); Assert.Equal(2, x14Ref.Length); - AssertContainedInDeclaratorArguments(x14Decl); + Assert.All(x14Decl, x14Decl => AssertNotContainedInDeclaratorArguments(x14Decl)); VerifyModelForOutVarWithoutDataFlow(model, x14Decl[0], x14Ref); VerifyModelForOutVarDuplicateInSameScope(model, x14Decl[1]); } @@ -21727,66 +21762,83 @@ static bool TakeOutParam(object y, out bool x) } "; var compilation = CreateCompilationWithMscorlib461(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular); - int[] exclude = new int[] { (int)ErrorCode.ERR_BadVarDecl, - (int)ErrorCode.ERR_SyntaxError, - (int)ErrorCode.ERR_UnexpectedToken, - (int)ErrorCode.WRN_UnreferencedVar, - (int)ErrorCode.ERR_CloseParenExpected - }; + int[] exclude = [ + (int)ErrorCode.ERR_BadVarDecl, + (int)ErrorCode.ERR_SyntaxError, + (int)ErrorCode.ERR_UnexpectedToken, + (int)ErrorCode.WRN_UnreferencedVar, + (int)ErrorCode.ERR_CloseParenExpected, + (int)ErrorCode.ERR_SemicolonExpected, + ]; compilation.GetDiagnostics().Where(d => !exclude.Contains(d.Code)).Verify( - // (13,47): error CS0128: A local variable or function named 'x4' is already defined in this scope - // Dummy(TakeOutParam(true, out var x4) && x4) - Diagnostic(ErrorCode.ERR_LocalDuplicate, "x4").WithArguments("x4").WithLocation(13, 47), - // (21,47): error CS0128: A local variable or function named 'x7' is already defined in this scope + // (31,57): error CS1513: } expected + // Dummy(TakeOutParam(true, out var x8) && x8)) + Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(31, 57), + // (40,57): error CS1513: } expected + // Dummy(TakeOutParam(true, out var x9) && x9)) + Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(40, 57), + // (12,22): error CS0841: Cannot use local variable 'x4' before it is declared + // for (bool d, x4( + Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x4").WithArguments("x4").WithLocation(12, 22), + // (20,30): error CS0103: The name 'b' does not exist in the current context + // for (bool x7 = true, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(20, 30), + // (21,47): error CS0136: A local or parameter named 'x7' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // Dummy(TakeOutParam(true, out var x7) && x7) - Diagnostic(ErrorCode.ERR_LocalDuplicate, "x7").WithArguments("x7").WithLocation(21, 47), + Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x7").WithArguments("x7").WithLocation(21, 47), // (20,19): warning CS0219: The variable 'x7' is assigned but its value is never used // for (bool x7 = true, b( Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "x7").WithArguments("x7").WithLocation(20, 19), - // (29,52): error CS0128: A local variable or function named 'x8' is already defined in this scope + // (28,21): error CS0103: The name 'b1' does not exist in the current context + // for (bool d,b1(Dummy(TakeOutParam(true, out var x8) && x8)], + Diagnostic(ErrorCode.ERR_NameNotInContext, "b1").WithArguments("b1").WithLocation(28, 21), + // (28,57): error CS0136: A local or parameter named 'x8' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter + // for (bool d,b1(Dummy(TakeOutParam(true, out var x8) && x8)], + Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x8").WithArguments("x8").WithLocation(28, 57), + // (29,16): error CS0103: The name 'b2' does not exist in the current context // b2(Dummy(TakeOutParam(true, out var x8) && x8)); - Diagnostic(ErrorCode.ERR_LocalDuplicate, "x8").WithArguments("x8").WithLocation(29, 52), + Diagnostic(ErrorCode.ERR_NameNotInContext, "b2").WithArguments("b2").WithLocation(29, 16), + // (29,52): error CS0136: A local or parameter named 'x8' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter + // b2(Dummy(TakeOutParam(true, out var x8) && x8)); + Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x8").WithArguments("x8").WithLocation(29, 52), // (30,47): error CS0136: A local or parameter named 'x8' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // Dummy(TakeOutParam(true, out var x8) && x8); Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x8").WithArguments("x8").WithLocation(30, 47), - // (31,47): error CS0136: A local or parameter named 'x8' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter - // Dummy(TakeOutParam(true, out var x8) && x8)) - Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x8").WithArguments("x8").WithLocation(31, 47), - // (37,23): error CS0841: Cannot use local variable 'x9' before it is declared + // (37,23): error CS0103: The name 'x9' does not exist in the current context // for (bool b = x9, - Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x9").WithArguments("x9").WithLocation(37, 23), + Diagnostic(ErrorCode.ERR_NameNotInContext, "x9").WithArguments("x9").WithLocation(37, 23), + // (38,16): error CS0103: The name 'b2' does not exist in the current context + // b2(Dummy(TakeOutParam(true, out var x9) && x9)); + Diagnostic(ErrorCode.ERR_NameNotInContext, "b2").WithArguments("b2").WithLocation(38, 16), // (39,47): error CS0136: A local or parameter named 'x9' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // Dummy(TakeOutParam(true, out var x9) && x9); Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x9").WithArguments("x9").WithLocation(39, 47), // (40,47): error CS0136: A local or parameter named 'x9' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // Dummy(TakeOutParam(true, out var x9) && x9)) - Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x9").WithArguments("x9").WithLocation(40, 47) - ); + Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x9").WithArguments("x9").WithLocation(40, 47)); var tree = compilation.SyntaxTrees.Single(); var model = compilation.GetSemanticModel(tree); var x4Decl = GetOutVarDeclarations(tree, "x4").Single(); - var x4Ref = GetReferences(tree, "x4").Single(); - AssertContainedInDeclaratorArguments(x4Decl); - VerifyModelForOutVarDuplicateInSameScope(model, x4Decl); - VerifyNotAnOutLocal(model, x4Ref); + var x4Ref = GetReferences(tree, "x4").ToArray(); + AssertNotContainedInDeclaratorArguments(x4Decl); + VerifyModelForOutVarWithoutDataFlow(model, x4Decl, x4Ref); var x7Decl = GetOutVarDeclarations(tree, "x7").Single(); var x7Ref = GetReferences(tree, "x7").Single(); - AssertContainedInDeclaratorArguments(x7Decl); - VerifyModelForOutVarDuplicateInSameScope(model, x7Decl); - VerifyNotAnOutLocal(model, x7Ref); + AssertNotContainedInDeclaratorArguments(x7Decl); + VerifyModelForOutVarWithoutDataFlow(model, x7Decl, x7Ref); var x8Decl = GetOutVarDeclarations(tree, "x8").ToArray(); var x8Ref = GetReferences(tree, "x8").ToArray(); Assert.Equal(4, x8Decl.Length); Assert.Equal(4, x8Ref.Length); - AssertContainedInDeclaratorArguments(x8Decl[0]); - AssertContainedInDeclaratorArguments(x8Decl[1]); - VerifyModelForOutVarWithoutDataFlow(model, x8Decl[0], x8Ref[0], x8Ref[1]); - VerifyModelForOutVarDuplicateInSameScope(model, x8Decl[1]); + AssertNotContainedInDeclaratorArguments(x8Decl[0]); + AssertNotContainedInDeclaratorArguments(x8Decl[1]); + VerifyModelForOutVar(model, x8Decl[0], x8Ref[0]); + VerifyModelForOutVar(model, x8Decl[1], x8Ref[1]); VerifyModelForOutVar(model, x8Decl[2], x8Ref[2]); VerifyModelForOutVar(model, x8Decl[3], x8Ref[3]); @@ -21794,8 +21846,8 @@ static bool TakeOutParam(object y, out bool x) var x9Ref = GetReferences(tree, "x9").ToArray(); Assert.Equal(3, x9Decl.Length); Assert.Equal(4, x9Ref.Length); - AssertContainedInDeclaratorArguments(x9Decl[0]); - VerifyModelForOutVarWithoutDataFlow(model, x9Decl[0], x9Ref[0], x9Ref[1]); + AssertNotContainedInDeclaratorArguments(x9Decl[0]); + VerifyModelForOutVar(model, x9Decl[0], x9Ref[1]); VerifyModelForOutVar(model, x9Decl[1], x9Ref[2]); VerifyModelForOutVar(model, x9Decl[2], x9Ref[3]); } From 961b913bea27b036c073b82155e64ca19c26a6e5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 4 Nov 2024 13:52:52 -0800 Subject: [PATCH 158/508] Improve recovery --- .../CSharp/Portable/Parser/LanguageParser.cs | 20 ++++++---- .../IOperationTests_IForLoopStatement.cs | 21 +++++++++- .../Semantic/Semantics/ForLoopErrorTests.cs | 38 ++++++++++--------- 3 files changed, 53 insertions(+), 26 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 24e7222e612f6..704092ca0ee33 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -5032,9 +5032,14 @@ private void ParseVariableDeclarators( { // If we see `for (int i = 0, j < ...` then we do not want to consume j as the next declarator. // - // Legal forms here are `for (int i = 0, j; ...` or `for (int i = 0, j = ...` or `for (int i = 0, - // j). Anything else we'll treat as as more likely to be the following - if (flags.HasFlag(VariableFlags.ForStatement)) + // Legal forms here are `for (int i = 0, j; ...` or `for (int i = 0, j = ...` or `for (int i = 0, j)`. + // + // We also accept: `for (int i = 0, ;` as that's likely an intermediary state prior to writing the next + // variable. + // + // Anything else we'll treat as as more likely to be the following conditional. + + if (flags.HasFlag(VariableFlags.ForStatement) && this.PeekToken(1).Kind != SyntaxKind.SemicolonToken) { if (!IsTrueIdentifier(this.PeekToken(1)) || this.PeekToken(2).Kind is not (SyntaxKind.SemicolonToken or SyntaxKind.EqualsToken or SyntaxKind.CloseParenToken)) @@ -9158,7 +9163,7 @@ private ForStatementSyntax ParseForStatement(SyntaxList att : null, secondSemicolonToken = eatCommaOrSemicolon(), incrementors: this.CurrentToken.Kind != SyntaxKind.CloseParenToken - ? this.ParseForStatementExpressionList(ref secondSemicolonToken) + ? this.ParseForStatementExpressionList(ref secondSemicolonToken, allowSemicolonAsSeparator: true) : default, eatUnexpectedTokensAndCloseParenToken(), ParseEmbeddedStatement()); @@ -9232,7 +9237,7 @@ private ForStatementSyntax ParseForStatement(SyntaxList att else if (this.CurrentToken.Kind != SyntaxKind.SemicolonToken) { // Not a type followed by an identifier, so it must be an expression list. - return (null, this.ParseForStatementExpressionList(ref openParen)); + return (null, this.ParseForStatementExpressionList(ref openParen, allowSemicolonAsSeparator: false)); } else { @@ -9274,7 +9279,8 @@ private bool IsEndOfForStatementArgument() return this.CurrentToken.Kind is SyntaxKind.SemicolonToken or SyntaxKind.CloseParenToken or SyntaxKind.OpenBraceToken; } - private SeparatedSyntaxList ParseForStatementExpressionList(ref SyntaxToken startToken) + private SeparatedSyntaxList ParseForStatementExpressionList( + ref SyntaxToken startToken, bool allowSemicolonAsSeparator) { return ParseCommaSeparatedSyntaxList( ref startToken, @@ -9284,7 +9290,7 @@ private SeparatedSyntaxList ParseForStatementExpressionList(re skipBadForStatementExpressionListTokens, allowTrailingSeparator: false, requireOneElement: false, - allowSemicolonAsSeparator: false); + allowSemicolonAsSeparator); static PostSkipAction skipBadForStatementExpressionListTokens( LanguageParser @this, ref SyntaxToken startToken, SeparatedSyntaxListBuilder list, SyntaxKind expectedKind, SyntaxKind closeKind) diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IForLoopStatement.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IForLoopStatement.cs index b6177d5b04494..542c760499414 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IForLoopStatement.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IForLoopStatement.cs @@ -2678,7 +2678,7 @@ static void Main(string[] args) [CompilerTrait(CompilerFeature.IOperation)] [Fact, WorkItem(17602, "https://github.com/dotnet/roslyn/issues/17602")] - public void IForLoopStatement_InvalidExpression() + public void IForLoopStatement_InvalidExpression1() { string source = @" class C @@ -2690,6 +2690,25 @@ static void Main(string[] args) } } } +"; + var tree = GetOperationTreeForTest(source); + Assert.Null(tree); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17602")] + public void IForLoopStatement_InvalidExpression2() + { + string source = @" +class C +{ + static void Main(string[] args) + { + /**/for (int k = 0, j = 0; k < 100, j > 5; k++) + { + }/**/ + } +} "; string expectedOperationTree = @" IForLoopOperation (LoopKind.For, Continue Label Id: 0, Exit Label Id: 1) (OperationKind.Loop, Type: null, IsInvalid) (Syntax: 'for (int k ... 100, j > 5;') diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/ForLoopErrorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/ForLoopErrorTests.cs index 19146c1c9979a..43bf0dfdc598b 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/ForLoopErrorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/ForLoopErrorTests.cs @@ -2,9 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - -using Microsoft.CodeAnalysis.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics @@ -27,16 +24,16 @@ static void Main(string[] args) } } "; - CreateCompilation(text). - VerifyDiagnostics( - Diagnostic(ErrorCode.ERR_SemicolonExpected, ","), - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ",").WithArguments(","), - Diagnostic(ErrorCode.ERR_CloseParenExpected, ";"), - Diagnostic(ErrorCode.ERR_SemicolonExpected, ")"), - Diagnostic(ErrorCode.ERR_RbraceExpected, ")"), - Diagnostic(ErrorCode.ERR_IllegalStatement, "j > 5"), - Diagnostic(ErrorCode.ERR_NameNotInContext, "k").WithArguments("k") - ); + CreateCompilation(text).VerifyDiagnostics( + // (6,39): error CS1002: ; expected + // for (int k = 0, j = 0; k < 100, j > 5; k++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, ",").WithLocation(6, 39), + // (6,46): error CS1003: Syntax error, ',' expected + // for (int k = 0, j = 0; k < 100, j > 5; k++) + Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(",").WithLocation(6, 46), + // (6,41): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // for (int k = 0, j = 0; k < 100, j > 5; k++) + Diagnostic(ErrorCode.ERR_IllegalStatement, "j > 5").WithLocation(6, 41)); } // Condition expression must be bool type @@ -94,11 +91,16 @@ static void Main(string[] args) } } "; - CreateCompilation(text). - VerifyDiagnostics( - Diagnostic(ErrorCode.ERR_CloseParenExpected, ";"), - Diagnostic(ErrorCode.ERR_RbraceExpected, ")") - ); + CreateCompilation(text).VerifyDiagnostics( + // (6,34): error CS1525: Invalid expression term ';' + // for (int i = 10; i < 100;;); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ";").WithArguments(";").WithLocation(6, 34), + // (6,34): error CS1003: Syntax error, ',' expected + // for (int i = 10; i < 100;;); + Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(",").WithLocation(6, 34), + // (6,35): error CS1525: Invalid expression term ')' + // for (int i = 10; i < 100;;); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(6, 35)); text = @" From fbd98e9e33de7aa5c5b97db0f819036695228b13 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 4 Nov 2024 13:57:05 -0800 Subject: [PATCH 159/508] Docs --- .../CSharp/Portable/Parser/LanguageParser.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 704092ca0ee33..38e6f19207a3a 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -9162,6 +9162,8 @@ private ForStatementSyntax ParseForStatement(SyntaxList att ? this.ParseExpressionCore() : null, secondSemicolonToken = eatCommaOrSemicolon(), + // Do allow semicolons (with diagnostics) in the incrementors list. This allows us to consume + // accidental extra incrementors that should have been separated by commas. incrementors: this.CurrentToken.Kind != SyntaxKind.CloseParenToken ? this.ParseForStatementExpressionList(ref secondSemicolonToken, allowSemicolonAsSeparator: true) : default, @@ -9172,7 +9174,7 @@ private ForStatementSyntax ParseForStatement(SyntaxList att return forStatement; - (VariableDeclarationSyntax variableDeclaration, SeparatedSyntaxList) eatVariableDeclarationOrInitializers() + (VariableDeclarationSyntax variableDeclaration, SeparatedSyntaxList initializers) eatVariableDeclarationOrInitializers() { using var resetPoint = this.GetDisposableResetPoint(resetOnDispose: false); @@ -9232,12 +9234,15 @@ private ForStatementSyntax ParseForStatement(SyntaxList att decl = decl.Update(declType, decl.Variables); } - return (decl, default); + return (decl, initializers: default); } else if (this.CurrentToken.Kind != SyntaxKind.SemicolonToken) { - // Not a type followed by an identifier, so it must be an expression list. - return (null, this.ParseForStatementExpressionList(ref openParen, allowSemicolonAsSeparator: false)); + // Not a type followed by an identifier, so it must be the initializer expression list. + // + // Do not consume semicolons here as they are used to separate the initializers out from the + // condition of the for loop. + return (variableDeclaration: null, this.ParseForStatementExpressionList(ref openParen, allowSemicolonAsSeparator: false)); } else { @@ -9279,6 +9284,11 @@ private bool IsEndOfForStatementArgument() return this.CurrentToken.Kind is SyntaxKind.SemicolonToken or SyntaxKind.CloseParenToken or SyntaxKind.OpenBraceToken; } + /// + /// Parses out a sequence of expressions. Both for the initializer section (the `for (initializer1, + /// initializer2, ...` section), as well as the incrementor section (the `for (;; incrementor1, incrementor2, + /// ...` section). + /// private SeparatedSyntaxList ParseForStatementExpressionList( ref SyntaxToken startToken, bool allowSemicolonAsSeparator) { From f446d4e8416704d247bbcfaefb094e322e3cf6d8 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 4 Nov 2024 14:10:51 -0800 Subject: [PATCH 160/508] Update test --- .../IOperationTests_IForLoopStatement.cs | 79 ++++++++++--------- 1 file changed, 40 insertions(+), 39 deletions(-) diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IForLoopStatement.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IForLoopStatement.cs index 542c760499414..f0b3121c917de 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IForLoopStatement.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IForLoopStatement.cs @@ -2710,45 +2710,46 @@ static void Main(string[] args) } } "; - string expectedOperationTree = @" -IForLoopOperation (LoopKind.For, Continue Label Id: 0, Exit Label Id: 1) (OperationKind.Loop, Type: null, IsInvalid) (Syntax: 'for (int k ... 100, j > 5;') - Locals: Local_1: System.Int32 k - Local_2: System.Int32 j - Condition: - IBinaryOperation (BinaryOperatorKind.LessThan) (OperationKind.Binary, Type: System.Boolean, IsInvalid) (Syntax: 'k < 100') - Left: - ILocalReferenceOperation: k (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'k') - Right: - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 100, IsInvalid) (Syntax: '100') - Before: - IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsImplicit) (Syntax: 'int k = 0, j = 0') - IVariableDeclarationOperation (2 declarators) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'int k = 0, j = 0') - Declarators: - IVariableDeclaratorOperation (Symbol: System.Int32 k) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'k = 0') - Initializer: - IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= 0') - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') - IVariableDeclaratorOperation (Symbol: System.Int32 j) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'j = 0') - Initializer: - IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= 0') - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') - Initializer: - null - AtLoopBottom: - IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid, IsImplicit) (Syntax: '') - Expression: - IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid) (Syntax: '') - Children(0) - IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid, IsImplicit) (Syntax: 'j > 5') - Expression: - IBinaryOperation (BinaryOperatorKind.GreaterThan) (OperationKind.Binary, Type: System.Boolean, IsInvalid) (Syntax: 'j > 5') - Left: - ILocalReferenceOperation: j (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'j') - Right: - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 5, IsInvalid) (Syntax: '5') - Body: - IEmptyOperation (OperationKind.Empty, Type: null, IsInvalid) (Syntax: ';') -"; + string expectedOperationTree = """ + IForLoopOperation (LoopKind.For, Continue Label Id: 0, Exit Label Id: 1) (OperationKind.Loop, Type: null, IsInvalid) (Syntax: 'for (int k ... }') + Locals: Local_1: System.Int32 k + Local_2: System.Int32 j + Condition: + IBinaryOperation (BinaryOperatorKind.LessThan) (OperationKind.Binary, Type: System.Boolean, IsInvalid) (Syntax: 'k < 100') + Left: + ILocalReferenceOperation: k (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'k') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 100, IsInvalid) (Syntax: '100') + Before: + IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsImplicit) (Syntax: 'int k = 0, j = 0') + IVariableDeclarationOperation (2 declarators) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'int k = 0, j = 0') + Declarators: + IVariableDeclaratorOperation (Symbol: System.Int32 k) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'k = 0') + Initializer: + IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= 0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + IVariableDeclaratorOperation (Symbol: System.Int32 j) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'j = 0') + Initializer: + IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= 0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + Initializer: + null + AtLoopBottom: + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid, IsImplicit) (Syntax: 'j > 5') + Expression: + IBinaryOperation (BinaryOperatorKind.GreaterThan) (OperationKind.Binary, Type: System.Boolean, IsInvalid) (Syntax: 'j > 5') + Left: + ILocalReferenceOperation: j (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'j') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 5, IsInvalid) (Syntax: '5') + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsImplicit) (Syntax: 'k++') + Expression: + IIncrementOrDecrementOperation (Postfix) (OperationKind.Increment, Type: System.Int32) (Syntax: 'k++') + Target: + ILocalReferenceOperation: k (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'k') + Body: + IBlockOperation (0 statements) (OperationKind.Block, Type: null) (Syntax: '{ ... }') + """; VerifyOperationTreeForTest(source, expectedOperationTree); } From 4ffa80c59c7d6e16f55a5e8cc5507d5f91f9d1ed Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Mon, 4 Nov 2024 15:14:55 -0800 Subject: [PATCH 161/508] Add delay to RedirectFeaturesAnalyzers integration test --- .../CSharp/CSharpRedirectFeaturesAnalyzers.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRedirectFeaturesAnalyzers.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRedirectFeaturesAnalyzers.cs index 9be3c3b789c61..6197ffb45303f 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRedirectFeaturesAnalyzers.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRedirectFeaturesAnalyzers.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -86,7 +87,6 @@ await TestServices.SolutionExplorer.AddCustomProjectAsync( """, cancellationToken); - await TestServices.SolutionExplorer.RestoreNuGetPackagesAsync(ProjectName, cancellationToken); // Configure the global indentation size which would be part of the Host fallback options. var globalOptions = await TestServices.Shell.GetComponentModelServiceAsync(cancellationToken); @@ -151,6 +151,8 @@ private async Task WaitForCodeActionListToPopulateAsync(CancellationToken cancel await TestServices.Editor.ActivateAsync(cancellationToken); await TestServices.Editor.PlaceCaretAsync("void M()", charsOffset: -1, cancellationToken); + await Task.Delay(TimeSpan.FromSeconds(1)); + await TestServices.Editor.InvokeCodeActionListAsync(cancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync( From ea665d3b00030a76715cc43f36fa163a252849b1 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Mon, 4 Nov 2024 16:42:23 -0800 Subject: [PATCH 162/508] Also check if analyzer reference is isolated file reference to handle .net core processes --- .../Common/AbstractProjectExtensionProvider.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/Common/AbstractProjectExtensionProvider.cs b/src/Features/Core/Portable/Common/AbstractProjectExtensionProvider.cs index cf28b4a561435..b952df809e965 100644 --- a/src/Features/Core/Portable/Common/AbstractProjectExtensionProvider.cs +++ b/src/Features/Core/Portable/Common/AbstractProjectExtensionProvider.cs @@ -145,8 +145,10 @@ private ImmutableArray CreateExtensions(string language) if (TryGetExtensionsFromReference(this.Reference, out var extensions)) return extensions; + var analyzerFileReference = GetAnalyzerFileReference(this.Reference); + // otherwise, see whether we can pick it up from reference itself - if (this.Reference is not AnalyzerFileReference analyzerFileReference) + if (analyzerFileReference is null) return []; using var _ = ArrayBuilder.GetInstance(out var builder); @@ -183,5 +185,17 @@ private ImmutableArray CreateExtensions(string language) } return builder.ToImmutableAndClear(); + + static AnalyzerFileReference? GetAnalyzerFileReference(AnalyzerReference reference) + { + if (reference is AnalyzerFileReference analyzerFileReference) + return analyzerFileReference; +#if NET + if (reference is IsolatedAnalyzerFileReference isolatedReference) + return isolatedReference.UnderlyingAnalyzerFileReference; +#endif + + return null; + } } } From f40829f7648b67cd8460e2bfc47816066dff0fa0 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Mon, 4 Nov 2024 17:07:45 -0800 Subject: [PATCH 163/508] review feedback --- .../BrokeredServices/WrappedServiceBroker.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/WrappedServiceBroker.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/WrappedServiceBroker.cs index 6429c2e00270f..5acad362275c8 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/WrappedServiceBroker.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/WrappedServiceBroker.cs @@ -8,6 +8,7 @@ using Microsoft.ServiceHub.Framework; using Microsoft.VisualStudio.Composition; using Microsoft.VisualStudio.Shell.ServiceBroker; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer.BrokeredServices; #pragma warning restore RS0030 // Do not used banned APIs @@ -22,11 +23,12 @@ namespace Microsoft.CodeAnalysis.LanguageServer.BrokeredServices; [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] internal class WrappedServiceBroker() : IServiceBroker { - private readonly TaskCompletionSource _serviceBrokerTask = new(); + private readonly TaskCompletionSource _serviceBrokerTask = new(TaskCreationOptions.RunContinuationsAsynchronously); internal void SetServiceBroker(IServiceBroker serviceBroker) { - serviceBroker.AvailabilityChanged += (s, e) => AvailabilityChanged?.Invoke(s, e); + Contract.ThrowIfTrue(_serviceBrokerTask.Task.IsCompleted); + serviceBroker.AvailabilityChanged += (s, e) => AvailabilityChanged?.Invoke(this, e); _serviceBrokerTask.SetResult(serviceBroker); } From f83bc560a826edf56697a80f6822543558cd2745 Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Tue, 5 Nov 2024 00:46:25 -0800 Subject: [PATCH 164/508] Reset FormattingOptions.IndentionSize between integration tests --- .../New.IntegrationTests/InProcess/StateResetInProcess.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/StateResetInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/StateResetInProcess.cs index cce8b434279f0..9eac00fa11aa0 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/StateResetInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/StateResetInProcess.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Editor.InlineRename; using Microsoft.CodeAnalysis.Editor.Options; using Microsoft.CodeAnalysis.Editor.VisualBasic.LineCommit; +using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.InheritanceMargin; using Microsoft.CodeAnalysis.InlineRename; @@ -63,6 +64,7 @@ public async Task ResetGlobalOptionsAsync(CancellationToken cancellationToken) ResetOption(globalOptions, MetadataAsSourceOptionsStorage.NavigateToDecompiledSources); ResetOption(globalOptions, WorkspaceConfigurationOptionsStorage.SourceGeneratorExecution); ResetOption(globalOptions, WorkspaceConfigurationOptionsStorage.SourceGeneratorExecutionBalancedFeatureFlag); + ResetPerLanguageOption(globalOptions, FormattingOptions2.IndentationSize); ResetPerLanguageOption(globalOptions, BlockStructureOptionsStorage.CollapseSourceLinkEmbeddedDecompiledFilesWhenFirstOpened); ResetPerLanguageOption(globalOptions, CompletionOptionsStorage.ShowItemsFromUnimportedNamespaces); ResetPerLanguageOption(globalOptions, CompletionOptionsStorage.TriggerInArgumentLists); From 9dcb2a31699f7d09974c9b5af571b102b291ee44 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Tue, 5 Nov 2024 10:08:48 +0100 Subject: [PATCH 165/508] Use absolute path (#75713) --- .../CSharp/Portable/Microsoft.CodeAnalysis.CSharp.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.csproj b/src/Compilers/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.csproj index 5efe2721532a9..a3e31151c8b61 100644 --- a/src/Compilers/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.csproj +++ b/src/Compilers/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.csproj @@ -17,7 +17,7 @@ true - Generated + $(MSBuildThisFileDirectory)\Generated From 6eff4e47777d9c62b49c0f97763ac60b2347ff63 Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Tue, 5 Nov 2024 01:58:03 -0800 Subject: [PATCH 166/508] Ignore Build errors in RedirectFeaturesAnalyzers tests --- .../CSharp/CSharpRedirectFeaturesAnalyzers.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRedirectFeaturesAnalyzers.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRedirectFeaturesAnalyzers.cs index 6197ffb45303f..93346a5b66b89 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRedirectFeaturesAnalyzers.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRedirectFeaturesAnalyzers.cs @@ -10,6 +10,8 @@ using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Shell.TableManager; using Roslyn.Test.Utilities; using Roslyn.VisualStudio.IntegrationTests; using Roslyn.VisualStudio.NewIntegrationTests.InProcess; @@ -143,7 +145,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( FeatureAttribute.ErrorList ], cancellationToken); - return await TestServices.ErrorList.GetErrorsAsync(cancellationToken); + return await TestServices.ErrorList.GetErrorsAsync(ErrorSource.Other, __VSERRORCATEGORY.EC_MESSAGE, cancellationToken); } private async Task WaitForCodeActionListToPopulateAsync(CancellationToken cancellationToken) From 448c735c397f2f5ace85a784ea3ffaf7f2ad3dcb Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 5 Nov 2024 08:43:52 -0800 Subject: [PATCH 167/508] Update test --- .../Test/Emit3/Semantics/OutVarTests.cs | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/OutVarTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/OutVarTests.cs index 7dd853f221117..5c1212d8e9755 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/OutVarTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/OutVarTests.cs @@ -21573,6 +21573,7 @@ static bool TakeOutParam(object y, out bool x) (int)ErrorCode.ERR_UseDefViolation, (int)ErrorCode.ERR_SemicolonExpected, (int)ErrorCode.ERR_CloseParenExpected, + (int)ErrorCode.ERR_InvalidExprTerm, ]; compilation.GetDiagnostics().Where(d => !exclude.Contains(d.Code)).Verify( @@ -21769,15 +21770,10 @@ static bool TakeOutParam(object y, out bool x) (int)ErrorCode.WRN_UnreferencedVar, (int)ErrorCode.ERR_CloseParenExpected, (int)ErrorCode.ERR_SemicolonExpected, + (int)ErrorCode.ERR_InvalidExprTerm, ]; compilation.GetDiagnostics().Where(d => !exclude.Contains(d.Code)).Verify( - // (31,57): error CS1513: } expected - // Dummy(TakeOutParam(true, out var x8) && x8)) - Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(31, 57), - // (40,57): error CS1513: } expected - // Dummy(TakeOutParam(true, out var x9) && x9)) - Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(40, 57), // (12,22): error CS0841: Cannot use local variable 'x4' before it is declared // for (bool d, x4( Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x4").WithArguments("x4").WithLocation(12, 22), @@ -21793,18 +21789,18 @@ static bool TakeOutParam(object y, out bool x) // (28,21): error CS0103: The name 'b1' does not exist in the current context // for (bool d,b1(Dummy(TakeOutParam(true, out var x8) && x8)], Diagnostic(ErrorCode.ERR_NameNotInContext, "b1").WithArguments("b1").WithLocation(28, 21), - // (28,57): error CS0136: A local or parameter named 'x8' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter - // for (bool d,b1(Dummy(TakeOutParam(true, out var x8) && x8)], - Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x8").WithArguments("x8").WithLocation(28, 57), // (29,16): error CS0103: The name 'b2' does not exist in the current context // b2(Dummy(TakeOutParam(true, out var x8) && x8)); Diagnostic(ErrorCode.ERR_NameNotInContext, "b2").WithArguments("b2").WithLocation(29, 16), // (29,52): error CS0136: A local or parameter named 'x8' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // b2(Dummy(TakeOutParam(true, out var x8) && x8)); Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x8").WithArguments("x8").WithLocation(29, 52), - // (30,47): error CS0136: A local or parameter named 'x8' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter + // (30,47): error CS0128: A local variable or function named 'x8' is already defined in this scope // Dummy(TakeOutParam(true, out var x8) && x8); - Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x8").WithArguments("x8").WithLocation(30, 47), + Diagnostic(ErrorCode.ERR_LocalDuplicate, "x8").WithArguments("x8").WithLocation(30, 47), + // (31,47): error CS0128: A local variable or function named 'x8' is already defined in this scope + // Dummy(TakeOutParam(true, out var x8) && x8)) + Diagnostic(ErrorCode.ERR_LocalDuplicate, "x8").WithArguments("x8").WithLocation(31, 47), // (37,23): error CS0103: The name 'x9' does not exist in the current context // for (bool b = x9, Diagnostic(ErrorCode.ERR_NameNotInContext, "x9").WithArguments("x9").WithLocation(37, 23), @@ -21814,9 +21810,9 @@ static bool TakeOutParam(object y, out bool x) // (39,47): error CS0136: A local or parameter named 'x9' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // Dummy(TakeOutParam(true, out var x9) && x9); Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x9").WithArguments("x9").WithLocation(39, 47), - // (40,47): error CS0136: A local or parameter named 'x9' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter + // (40,47): error CS0128: A local variable or function named 'x9' is already defined in this scope // Dummy(TakeOutParam(true, out var x9) && x9)) - Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x9").WithArguments("x9").WithLocation(40, 47)); + Diagnostic(ErrorCode.ERR_LocalDuplicate, "x9").WithArguments("x9").WithLocation(40, 47)); var tree = compilation.SyntaxTrees.Single(); var model = compilation.GetSemanticModel(tree); @@ -21839,8 +21835,8 @@ static bool TakeOutParam(object y, out bool x) AssertNotContainedInDeclaratorArguments(x8Decl[1]); VerifyModelForOutVar(model, x8Decl[0], x8Ref[0]); VerifyModelForOutVar(model, x8Decl[1], x8Ref[1]); - VerifyModelForOutVar(model, x8Decl[2], x8Ref[2]); - VerifyModelForOutVar(model, x8Decl[3], x8Ref[3]); + VerifyModelForOutVarWithoutDataFlow(model, x8Decl[2], isShadowed: true); + VerifyModelForOutVarWithoutDataFlow(model, x8Decl[3], isShadowed: true); var x9Decl = GetOutVarDeclarations(tree, "x9").ToArray(); var x9Ref = GetReferences(tree, "x9").ToArray(); @@ -21849,7 +21845,7 @@ static bool TakeOutParam(object y, out bool x) AssertNotContainedInDeclaratorArguments(x9Decl[0]); VerifyModelForOutVar(model, x9Decl[0], x9Ref[1]); VerifyModelForOutVar(model, x9Decl[1], x9Ref[2]); - VerifyModelForOutVar(model, x9Decl[2], x9Ref[3]); + VerifyModelForOutVarWithoutDataFlow(model, x9Decl[2], isShadowed: true); } [Fact] From 7de7e64db894bcedec3fd0a1a5c73177e253ffe1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 5 Nov 2024 08:53:31 -0800 Subject: [PATCH 168/508] Update test --- .../Semantics/PatternMatchingTests_Scope.cs | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests_Scope.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests_Scope.cs index 7e5edc20234a4..cfb5fc42fed94 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests_Scope.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests_Scope.cs @@ -12403,6 +12403,7 @@ void Test14() (int)ErrorCode.ERR_AbstractAndExtern, (int)ErrorCode.ERR_SemicolonExpected, (int)ErrorCode.ERR_CloseParenExpected, + (int)ErrorCode.ERR_InvalidExprTerm, ]; compilation.GetDiagnostics().Where(d => !exclude.Contains(d.Code)).Verify( @@ -12593,15 +12594,10 @@ void Test9() (int)ErrorCode.WRN_UnreferencedVar, (int)ErrorCode.ERR_CloseParenExpected, (int)ErrorCode.ERR_SemicolonExpected, + (int)ErrorCode.ERR_InvalidExprTerm, ]; compilation.GetDiagnostics().Where(d => !exclude.Contains(d.Code)).Verify( - // (31,41): error CS1513: } expected - // Dummy(true is var x8 && x8)) - Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(31, 41), - // (40,41): error CS1513: } expected - // Dummy(true is var x9 && x9)) - Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(40, 41), // (12,22): error CS0841: Cannot use local variable 'x4' before it is declared // for (bool d, x4( Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x4").WithArguments("x4").WithLocation(12, 22), @@ -12617,18 +12613,18 @@ void Test9() // (28,21): error CS0103: The name 'b1' does not exist in the current context // for (bool d,b1(Dummy(true is var x8 && x8)], Diagnostic(ErrorCode.ERR_NameNotInContext, "b1").WithArguments("b1").WithLocation(28, 21), - // (28,42): error CS0136: A local or parameter named 'x8' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter - // for (bool d,b1(Dummy(true is var x8 && x8)], - Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x8").WithArguments("x8").WithLocation(28, 42), // (29,16): error CS0103: The name 'b2' does not exist in the current context // b2(Dummy(true is var x8 && x8)); Diagnostic(ErrorCode.ERR_NameNotInContext, "b2").WithArguments("b2").WithLocation(29, 16), // (29,37): error CS0136: A local or parameter named 'x8' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // b2(Dummy(true is var x8 && x8)); Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x8").WithArguments("x8").WithLocation(29, 37), - // (30,32): error CS0136: A local or parameter named 'x8' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter + // (30,32): error CS0128: A local variable or function named 'x8' is already defined in this scope // Dummy(true is var x8 && x8); - Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x8").WithArguments("x8").WithLocation(30, 32), + Diagnostic(ErrorCode.ERR_LocalDuplicate, "x8").WithArguments("x8").WithLocation(30, 32), + // (31,32): error CS0128: A local variable or function named 'x8' is already defined in this scope + // Dummy(true is var x8 && x8)) + Diagnostic(ErrorCode.ERR_LocalDuplicate, "x8").WithArguments("x8").WithLocation(31, 32), // (37,23): error CS0103: The name 'x9' does not exist in the current context // for (bool b = x9, Diagnostic(ErrorCode.ERR_NameNotInContext, "x9").WithArguments("x9").WithLocation(37, 23), @@ -12638,9 +12634,9 @@ void Test9() // (39,32): error CS0136: A local or parameter named 'x9' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // Dummy(true is var x9 && x9); Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x9").WithArguments("x9").WithLocation(39, 32), - // (40,32): error CS0136: A local or parameter named 'x9' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter + // (40,32): error CS0128: A local variable or function named 'x9' is already defined in this scope // Dummy(true is var x9 && x9)) - Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x9").WithArguments("x9").WithLocation(40, 32)); + Diagnostic(ErrorCode.ERR_LocalDuplicate, "x9").WithArguments("x9").WithLocation(40, 32)); var tree = compilation.SyntaxTrees.Single(); var model = compilation.GetSemanticModel(tree); @@ -12666,8 +12662,8 @@ void Test9() AssertNotContainedInDeclaratorArguments(x8Decl[1]); VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x8Decl[0], x8Ref[0]); VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x8Decl[1], x8Ref[1]); - VerifyModelForDeclarationOrVarSimplePattern(model, x8Decl[2], x8Ref[2]); - VerifyModelForDeclarationOrVarSimplePattern(model, x8Decl[3], x8Ref[3]); + VerifyModelForDeclarationOrVarSimplePattern(model, x8Decl[2], isShadowed: true); + VerifyModelForDeclarationOrVarSimplePattern(model, x8Decl[3], isShadowed: true); var x9Decl = GetPatternDeclarations(tree, "x9").ToArray(); var x9Ref = GetReferences(tree, "x9").ToArray(); @@ -12676,7 +12672,7 @@ void Test9() AssertNotContainedInDeclaratorArguments(x9Decl[0]); VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x9Decl[0], x9Ref[1]); VerifyModelForDeclarationOrVarSimplePattern(model, x9Decl[1], x9Ref[2]); - VerifyModelForDeclarationOrVarSimplePattern(model, x9Decl[2], x9Ref[3]); + VerifyModelForDeclarationOrVarSimplePattern(model, x9Decl[2], isShadowed: true); } [Fact] From a68c5bc1c12cea8db171e7c57f9e75766bacc6ca Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 5 Nov 2024 09:02:03 -0800 Subject: [PATCH 169/508] inline --- .../CSharp/Portable/Parser/LanguageParser.cs | 45 +++++++++---------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 38e6f19207a3a..e860418bf86b7 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -9165,7 +9165,7 @@ private ForStatementSyntax ParseForStatement(SyntaxList att // Do allow semicolons (with diagnostics) in the incrementors list. This allows us to consume // accidental extra incrementors that should have been separated by commas. incrementors: this.CurrentToken.Kind != SyntaxKind.CloseParenToken - ? this.ParseForStatementExpressionList(ref secondSemicolonToken, allowSemicolonAsSeparator: true) + ? parseForStatementExpressionList(ref secondSemicolonToken, allowSemicolonAsSeparator: true) : default, eatUnexpectedTokensAndCloseParenToken(), ParseEmbeddedStatement()); @@ -9242,7 +9242,7 @@ private ForStatementSyntax ParseForStatement(SyntaxList att // // Do not consume semicolons here as they are used to separate the initializers out from the // condition of the for loop. - return (variableDeclaration: null, this.ParseForStatementExpressionList(ref openParen, allowSemicolonAsSeparator: false)); + return (variableDeclaration: null, parseForStatementExpressionList(ref openParen, allowSemicolonAsSeparator: false)); } else { @@ -9277,30 +9277,20 @@ SyntaxToken eatUnexpectedTokensAndCloseParenToken() var result = this.EatToken(SyntaxKind.CloseParenToken); return AddLeadingSkippedSyntax(result, _pool.ToTokenListAndFree(skippedTokens).Node); } - } - private bool IsEndOfForStatementArgument() - { - return this.CurrentToken.Kind is SyntaxKind.SemicolonToken or SyntaxKind.CloseParenToken or SyntaxKind.OpenBraceToken; - } - - /// - /// Parses out a sequence of expressions. Both for the initializer section (the `for (initializer1, - /// initializer2, ...` section), as well as the incrementor section (the `for (;; incrementor1, incrementor2, - /// ...` section). - /// - private SeparatedSyntaxList ParseForStatementExpressionList( - ref SyntaxToken startToken, bool allowSemicolonAsSeparator) - { - return ParseCommaSeparatedSyntaxList( - ref startToken, - SyntaxKind.CloseParenToken, - static @this => @this.IsPossibleExpression(), - static @this => @this.ParseExpressionCore(), - skipBadForStatementExpressionListTokens, - allowTrailingSeparator: false, - requireOneElement: false, - allowSemicolonAsSeparator); + // Parses out a sequence of expressions. Both for the initializer section (the `for (initializer1, + // initializer2, ...` section), as well as the incrementor section (the `for (;; incrementor1, incrementor2, + // ...` section). + SeparatedSyntaxList parseForStatementExpressionList(ref SyntaxToken startToken, bool allowSemicolonAsSeparator) + => ParseCommaSeparatedSyntaxList( + ref startToken, + SyntaxKind.CloseParenToken, + static @this => @this.IsPossibleExpression(), + static @this => @this.ParseExpressionCore(), + skipBadForStatementExpressionListTokens, + allowTrailingSeparator: false, + requireOneElement: false, + allowSemicolonAsSeparator); static PostSkipAction skipBadForStatementExpressionListTokens( LanguageParser @this, ref SyntaxToken startToken, SeparatedSyntaxListBuilder list, SyntaxKind expectedKind, SyntaxKind closeKind) @@ -9315,6 +9305,11 @@ static PostSkipAction skipBadForStatementExpressionListTokens( } } + private bool IsEndOfForStatementArgument() + { + return this.CurrentToken.Kind is SyntaxKind.SemicolonToken or SyntaxKind.CloseParenToken or SyntaxKind.OpenBraceToken; + } + private CommonForEachStatementSyntax ParseForEachStatement( SyntaxList attributes, SyntaxToken awaitTokenOpt) { From ffd6abfe753de866d470a3b178e8f110c79ee742 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 5 Nov 2024 09:09:16 -0800 Subject: [PATCH 170/508] IDE tests --- .../DeclarationNameCompletionProviderTests.cs | 2 +- .../ConvertLinqQueryToForEachTests.cs | 19 ++++++++----------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs index 2c1975a5dbd0c..e848f3223fa8c 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs @@ -1892,7 +1892,7 @@ void M() } } """; - await VerifyItemExistsAsync(markup, "streamReader"); + await VerifyItemIsAbsentAsync(markup, "streamReader"); } [Fact] diff --git a/src/Features/CSharpTest/ConvertLinq/ConvertLinqQueryToForEachTests.cs b/src/Features/CSharpTest/ConvertLinq/ConvertLinqQueryToForEachTests.cs index 0be23f6a32bdf..d271b75cda114 100644 --- a/src/Features/CSharpTest/ConvertLinq/ConvertLinqQueryToForEachTests.cs +++ b/src/Features/CSharpTest/ConvertLinq/ConvertLinqQueryToForEachTests.cs @@ -3353,7 +3353,7 @@ void M(IEnumerable nums) { for(int i = ([|from int n1 in nums from int n2 in nums - select n1|]).Count(), i < 5; i++) + select n1|]).Count(); i < 5; i++) { Console.WriteLine(i); } @@ -3369,20 +3369,17 @@ class C { void M(IEnumerable nums) { - IEnumerable enumerable() + for(int i = 0; i < 5; i++) { - foreach (int n1 in nums) - { - foreach (int n2 in nums) - { - yield return n1; - } - } + Console.WriteLine(i); } - for (int i = enumerable().Count(), i < 5; i++) + foreach (int n1 in nums) { - Console.WriteLine(i); + foreach (int n2 in nums) + { + i++; + } } } } From 31aaa35bfbc333adfcbe9b78405f1d4afec40f7e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 5 Nov 2024 09:12:22 -0800 Subject: [PATCH 171/508] IDE tests --- ...etionProviderTests_NameDeclarationInfoTests.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests_NameDeclarationInfoTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests_NameDeclarationInfoTests.cs index 18fdf14d35f79..03185b0033a98 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests_NameDeclarationInfoTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests_NameDeclarationInfoTests.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -21,7 +19,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.DeclarationInfoTests; [Trait(Traits.Feature, Traits.Features.Completion)] public class DeclarationNameCompletion_ContextTests { - protected CSharpTestWorkspaceFixture fixture = new CSharpTestWorkspaceFixture(); + protected readonly CSharpTestWorkspaceFixture Fixture = new(); [Fact] public async Task AfterTypeInClass1() @@ -257,10 +255,9 @@ void M() } } """; - await VerifySymbolKinds(markup, - new SymbolKindOrTypeKind(SymbolKind.Local)); + await VerifySymbolKinds(markup); await VerifyModifiers(markup, new DeclarationModifiers()); - await VerifyTypeName(markup, "int"); + await VerifyTypeName(markup, null); await VerifyAccessibility(markup, null); } @@ -772,10 +769,10 @@ await VerifySymbolKinds(markup, new SymbolKindOrTypeKind(MethodKind.LocalFunction)); } - private async Task VerifyTypeName(string markup, string typeName) + private async Task VerifyTypeName(string markup, string? typeName) { var result = await GetResultsAsync(markup); - Assert.Equal(typeName, result.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); + Assert.Equal(typeName, result.Type?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); } private async Task VerifyNoModifiers(string markup) @@ -812,6 +809,6 @@ private async Task GetResultsAsync(string markup) private (Document, int) ApplyChangesToFixture(string markup) { MarkupTestFile.GetPosition(markup, out var text, out int position); - return (fixture.UpdateDocument(text, SourceCodeKind.Regular), position); + return (Fixture.UpdateDocument(text, SourceCodeKind.Regular), position); } } From 527468b58925f26e1841a0b41527f3fda416dd00 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 5 Nov 2024 09:12:44 -0800 Subject: [PATCH 172/508] Seal --- ...nNameCompletionProviderTests_NameDeclarationInfoTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests_NameDeclarationInfoTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests_NameDeclarationInfoTests.cs index 03185b0033a98..b0d85e386fdc2 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests_NameDeclarationInfoTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests_NameDeclarationInfoTests.cs @@ -17,9 +17,9 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.DeclarationInfoTests; [UseExportProvider] [Trait(Traits.Feature, Traits.Features.Completion)] -public class DeclarationNameCompletion_ContextTests +public sealed class DeclarationNameCompletion_ContextTests { - protected readonly CSharpTestWorkspaceFixture Fixture = new(); + private readonly CSharpTestWorkspaceFixture _fixture = new(); [Fact] public async Task AfterTypeInClass1() @@ -809,6 +809,6 @@ private async Task GetResultsAsync(string markup) private (Document, int) ApplyChangesToFixture(string markup) { MarkupTestFile.GetPosition(markup, out var text, out int position); - return (Fixture.UpdateDocument(text, SourceCodeKind.Regular), position); + return (_fixture.UpdateDocument(text, SourceCodeKind.Regular), position); } } From 48734503f8e20783c0bf00f5b7b3a681c527bea8 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 5 Nov 2024 09:22:43 -0800 Subject: [PATCH 173/508] Simplify --- .../CSharp/Portable/Parser/LanguageParser.cs | 50 +++++++------------ 1 file changed, 18 insertions(+), 32 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index e860418bf86b7..55ad424f7c647 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -8825,7 +8825,7 @@ private FixedStatementSyntax ParseFixedStatement(SyntaxList var saveTerm = _termState; _termState |= TerminatorState.IsEndOfFixedStatement; - var decl = ParseParenthesizedVariableDeclaration(VariableFlags.None); + var decl = ParseParenthesizedVariableDeclaration(VariableFlags.None, scopedKeyword: null); _termState = saveTerm; return _syntaxFactory.FixedStatement( @@ -9181,7 +9181,6 @@ private ForStatementSyntax ParseForStatement(SyntaxList att // Here can be either a declaration or an expression statement list. Scan // for a declaration first. bool isDeclaration = false; - bool haveScopedKeyword = false; if (this.CurrentToken.ContextualKind == SyntaxKind.ScopedKeyword) { @@ -9195,8 +9194,6 @@ private ForStatementSyntax ParseForStatement(SyntaxList att isDeclaration = ScanType() != ScanTypeFlags.NotType && this.CurrentToken.Kind == SyntaxKind.IdentifierToken; resetPoint.Reset(); } - - haveScopedKeyword = isDeclaration; } else if (this.CurrentToken.Kind == SyntaxKind.RefKeyword) { @@ -9213,28 +9210,7 @@ private ForStatementSyntax ParseForStatement(SyntaxList att if (isDeclaration) { - SyntaxToken scopedKeyword = null; - - if (haveScopedKeyword) - { - scopedKeyword = EatContextualToken(SyntaxKind.ScopedKeyword); - } - - var decl = ParseParenthesizedVariableDeclaration(VariableFlags.ForStatement); - - var declType = decl.Type; - - if (scopedKeyword != null) - { - declType = _syntaxFactory.ScopedType(scopedKeyword, declType); - } - - if (declType != decl.Type) - { - decl = decl.Update(declType, decl.Variables); - } - - return (decl, initializers: default); + return (ParseParenthesizedVariableDeclaration(VariableFlags.ForStatement, ParsePossibleScopedKeyword(isFunctionPointerParameter: false)), initializers: default); } else if (this.CurrentToken.Kind != SyntaxKind.SemicolonToken) { @@ -9921,8 +9897,7 @@ private void ParseUsingExpression(ref VariableDeclarationSyntax declaration, ref if (scopedKeyword != null) { - declaration = ParseParenthesizedVariableDeclaration(VariableFlags.None); - declaration = declaration.Update(_syntaxFactory.ScopedType(scopedKeyword, declaration.Type), declaration.Variables); + declaration = ParseParenthesizedVariableDeclaration(VariableFlags.None, scopedKeyword); return; } else @@ -9955,14 +9930,14 @@ private void ParseUsingExpression(ref VariableDeclarationSyntax declaration, ref case SyntaxKind.CommaToken: case SyntaxKind.CloseParenToken: this.Reset(ref resetPoint); - declaration = ParseParenthesizedVariableDeclaration(VariableFlags.None); + declaration = ParseParenthesizedVariableDeclaration(VariableFlags.None, scopedKeyword: null); break; case SyntaxKind.EqualsToken: // Parse it as a decl. If the next token is a : and only one variable was parsed, // convert the whole thing to ?: expression. this.Reset(ref resetPoint); - declaration = ParseParenthesizedVariableDeclaration(VariableFlags.None); + declaration = ParseParenthesizedVariableDeclaration(VariableFlags.None, scopedKeyword: null); // We may have non-nullable types in error scenarios. if (this.CurrentToken.Kind == SyntaxKind.ColonToken && @@ -9983,7 +9958,7 @@ private void ParseUsingExpression(ref VariableDeclarationSyntax declaration, ref else if (IsUsingStatementVariableDeclaration(st)) { this.Reset(ref resetPoint); - declaration = ParseParenthesizedVariableDeclaration(VariableFlags.None); + declaration = ParseParenthesizedVariableDeclaration(VariableFlags.None, scopedKeyword: null); } else { @@ -10074,6 +10049,7 @@ private StatementSyntax ParseLocalDeclarationStatement(SyntaxList /// Parse a local variable declaration for constructs where the variable declaration is enclosed in parentheses. /// Specifically, only for the `fixed (...)` `for(...)` or `using (...)` statements. /// - private VariableDeclarationSyntax ParseParenthesizedVariableDeclaration(VariableFlags initialFlags) + private VariableDeclarationSyntax ParseParenthesizedVariableDeclaration( + VariableFlags initialFlags, SyntaxToken? scopedKeyword) { var variables = _pool.AllocateSeparated(); ParseLocalDeclaration( @@ -10251,6 +10230,7 @@ private VariableDeclarationSyntax ParseParenthesizedVariableDeclaration(Variable stopOnCloseParen: true, attributes: default, mods: default, + scopedKeyword, initialFlags, out var type, out var localFunction); @@ -10266,12 +10246,16 @@ private void ParseLocalDeclaration( bool stopOnCloseParen, SyntaxList attributes, SyntaxList mods, + SyntaxToken? scopedKeyword, VariableFlags initialFlags, out TypeSyntax type, out LocalFunctionStatementSyntax localFunction) { type = allowLocalFunctions ? ParseReturnType() : this.ParseType(); + if (scopedKeyword != null) + type = _syntaxFactory.ScopedType(scopedKeyword, type); + VariableFlags flags = initialFlags | VariableFlags.LocalOrField; if (mods.Any((int)SyntaxKind.ConstKeyword)) { @@ -10298,6 +10282,8 @@ private void ParseLocalDeclaration( } } +#nullable disable + private bool IsEndOfDeclarationClause() { switch (this.CurrentToken.Kind) From b15605aa82d1933921259bf98db6d6461253b146 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 5 Nov 2024 09:27:12 -0800 Subject: [PATCH 174/508] Doc --- .../CSharp/Portable/Parser/LanguageParser.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 55ad424f7c647..9423cff2c3945 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -5041,11 +5041,15 @@ private void ParseVariableDeclarators( if (flags.HasFlag(VariableFlags.ForStatement) && this.PeekToken(1).Kind != SyntaxKind.SemicolonToken) { - if (!IsTrueIdentifier(this.PeekToken(1)) || - this.PeekToken(2).Kind is not (SyntaxKind.SemicolonToken or SyntaxKind.EqualsToken or SyntaxKind.CloseParenToken)) - { + // `int i = 0, ...` where what follows is not an identifier. Don't treat this as the start of a + // second variable. + if (!IsTrueIdentifier(this.PeekToken(1))) + break; + + // `int i = 0, j ...` where what follows is not something that continues a variable declaration. + // In this case, treat that `j` as the start of the condition expression instead. + if (this.PeekToken(2).Kind is not (SyntaxKind.SemicolonToken or SyntaxKind.EqualsToken or SyntaxKind.CloseParenToken)) break; - } } variables.AddSeparator(this.EatToken(SyntaxKind.CommaToken)); From c5018e827b85a6d1067cfb70dfbc0c931d165ab6 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 5 Nov 2024 10:31:50 -0800 Subject: [PATCH 175/508] Support navigating to metadata in progression results --- .../Progression/GraphNavigatorExtension.cs | 110 +++++++++--------- 1 file changed, 52 insertions(+), 58 deletions(-) diff --git a/src/VisualStudio/Core/Def/Progression/GraphNavigatorExtension.cs b/src/VisualStudio/Core/Def/Progression/GraphNavigatorExtension.cs index 9a51a3f72fa62..6a6a59cce9d25 100644 --- a/src/VisualStudio/Core/Def/Progression/GraphNavigatorExtension.cs +++ b/src/VisualStudio/Core/Def/Progression/GraphNavigatorExtension.cs @@ -16,6 +16,7 @@ using Microsoft.VisualStudio.GraphModel; using Microsoft.VisualStudio.GraphModel.CodeSchema; using Microsoft.VisualStudio.GraphModel.Schemas; +using Microsoft.VisualStudio.Shell; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Progression; @@ -32,74 +33,67 @@ internal sealed class GraphNavigatorExtension( public void NavigateTo(GraphObject graphObject) { + if (graphObject is not GraphNode graphNode) + return; - if (graphObject is GraphNode graphNode) - { - var sourceLocation = graphNode.GetValue(CodeNodeProperties.SourceLocation); - if (sourceLocation.FileName == null) - { - return; - } - - var projectId = graphNode.GetValue(RoslynGraphProperties.ContextProjectId); - var symbolId = graphNode.GetValue(RoslynGraphProperties.SymbolId); - - if (projectId != null) - { - var solution = _workspace.CurrentSolution; - var project = solution.GetProject(projectId); - if (project == null) - return; - - var document = project.Documents.FirstOrDefault( - d => string.Equals( - d.FilePath, - sourceLocation.FileName.LocalPath, - StringComparison.OrdinalIgnoreCase)); - - if (document == null) - return; - - _threadingContext.JoinableTaskFactory.Run(() => - NavigateToAsync(sourceLocation, symbolId, project, document, CancellationToken.None)); - } - } + _threadingContext.JoinableTaskFactory.Run(() => NavigateToAsync(graphNode, CancellationToken.None)); } - private async Task NavigateToAsync( - SourceLocation sourceLocation, SymbolKey? symbolId, Project project, Document document, CancellationToken cancellationToken) + private async Task NavigateToAsync(GraphNode graphNode, CancellationToken cancellationToken) { - // Notify of navigation so third parties can intercept the navigation - if (symbolId != null) + var projectId = graphNode.GetValue(RoslynGraphProperties.ContextProjectId); + var symbolId = graphNode.GetValue(RoslynGraphProperties.SymbolId); + + if (projectId is null) + return; + + var solution = _workspace.CurrentSolution; + var project = solution.GetProject(projectId); + if (project is null) + return; + + // Go through the mainline symbol id path if we have it. That way we notify third parties, and we can navigate + // to metadata. + if (symbolId is not null) { - var symbol = symbolId.Value.Resolve(await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false), cancellationToken: cancellationToken).Symbol; + var symbol = symbolId.Value.Resolve(await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false), cancellationToken: cancellationToken).GetAnySymbol(); + if (symbol is null) + return; + await GoToDefinitionHelpers.TryNavigateToLocationAsync( symbol, project.Solution, _threadingContext, _streamingPresenter.Value, cancellationToken).ConfigureAwait(false); - return; } - if (sourceLocation.IsValid) - { - // We must find the right document in this project. This may not be the - // ContextDocumentId if you have a partial member that is shown under one - // document, but only exists in the other + // If we didn't have a symbol id, attempt to navigate to the source location directly if the node includes one. + var sourceLocation = graphNode.GetValue(CodeNodeProperties.SourceLocation); + if (sourceLocation.FileName is null || !sourceLocation.IsValid) + return; - if (document != null) - { - var editorWorkspace = document.Project.Solution.Workspace; - var navigationService = editorWorkspace.Services.GetService(); - - // TODO: Get the platform to use and pass us an operation context, or create one ourselves. - await navigationService.TryNavigateToLineAndOffsetAsync( - _threadingContext, - editorWorkspace, - document.Id, - sourceLocation.StartPosition.Line, - sourceLocation.StartPosition.Character, - NavigationOptions.Default, - cancellationToken).ConfigureAwait(false); - } - } + var document = project.Documents.FirstOrDefault( + d => string.Equals( + d.FilePath, + sourceLocation.FileName.LocalPath, + StringComparison.OrdinalIgnoreCase)); + + if (document == null) + return; + + // We must find the right document in this project. This may not be the + // ContextDocumentId if you have a partial member that is shown under one + // document, but only exists in the other + + var editorWorkspace = document.Project.Solution.Workspace; + var navigationService = editorWorkspace.Services.GetService(); + + // TODO: Get the platform to use and pass us an operation context, or create one ourselves. + await navigationService.TryNavigateToLineAndOffsetAsync( + _threadingContext, + editorWorkspace, + document.Id, + sourceLocation.StartPosition.Line, + sourceLocation.StartPosition.Character, + NavigationOptions.Default, + cancellationToken).ConfigureAwait(false); } public int GetRank(GraphObject graphObject) From 5e5021f62e5ca62fdec910eed68e09d6b4b399f0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 5 Nov 2024 10:33:04 -0800 Subject: [PATCH 176/508] Return --- src/VisualStudio/Core/Def/Progression/GraphNavigatorExtension.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/VisualStudio/Core/Def/Progression/GraphNavigatorExtension.cs b/src/VisualStudio/Core/Def/Progression/GraphNavigatorExtension.cs index 6a6a59cce9d25..aa81af809c498 100644 --- a/src/VisualStudio/Core/Def/Progression/GraphNavigatorExtension.cs +++ b/src/VisualStudio/Core/Def/Progression/GraphNavigatorExtension.cs @@ -62,6 +62,7 @@ private async Task NavigateToAsync(GraphNode graphNode, CancellationToken cancel await GoToDefinitionHelpers.TryNavigateToLocationAsync( symbol, project.Solution, _threadingContext, _streamingPresenter.Value, cancellationToken).ConfigureAwait(false); + return; } // If we didn't have a symbol id, attempt to navigate to the source location directly if the node includes one. From 4ab10c03a89c46c741b5eada8e1c503d5a850857 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 5 Nov 2024 10:33:25 -0800 Subject: [PATCH 177/508] Reorder --- .../Core/Def/Progression/GraphNavigatorExtension.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VisualStudio/Core/Def/Progression/GraphNavigatorExtension.cs b/src/VisualStudio/Core/Def/Progression/GraphNavigatorExtension.cs index aa81af809c498..2894b83ec43e8 100644 --- a/src/VisualStudio/Core/Def/Progression/GraphNavigatorExtension.cs +++ b/src/VisualStudio/Core/Def/Progression/GraphNavigatorExtension.cs @@ -42,7 +42,6 @@ public void NavigateTo(GraphObject graphObject) private async Task NavigateToAsync(GraphNode graphNode, CancellationToken cancellationToken) { var projectId = graphNode.GetValue(RoslynGraphProperties.ContextProjectId); - var symbolId = graphNode.GetValue(RoslynGraphProperties.SymbolId); if (projectId is null) return; @@ -54,6 +53,7 @@ private async Task NavigateToAsync(GraphNode graphNode, CancellationToken cancel // Go through the mainline symbol id path if we have it. That way we notify third parties, and we can navigate // to metadata. + var symbolId = graphNode.GetValue(RoslynGraphProperties.SymbolId); if (symbolId is not null) { var symbol = symbolId.Value.Resolve(await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false), cancellationToken: cancellationToken).GetAnySymbol(); From c9590debe846b4365b43f33e63a1f5b0af843f31 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 5 Nov 2024 10:35:02 -0800 Subject: [PATCH 178/508] NRT --- .../Progression/GraphNavigatorExtension.cs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/VisualStudio/Core/Def/Progression/GraphNavigatorExtension.cs b/src/VisualStudio/Core/Def/Progression/GraphNavigatorExtension.cs index 2894b83ec43e8..70cb966833785 100644 --- a/src/VisualStudio/Core/Def/Progression/GraphNavigatorExtension.cs +++ b/src/VisualStudio/Core/Def/Progression/GraphNavigatorExtension.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Linq; using System.Threading; @@ -16,7 +14,6 @@ using Microsoft.VisualStudio.GraphModel; using Microsoft.VisualStudio.GraphModel.CodeSchema; using Microsoft.VisualStudio.GraphModel.Schemas; -using Microsoft.VisualStudio.Shell; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Progression; @@ -56,13 +53,17 @@ private async Task NavigateToAsync(GraphNode graphNode, CancellationToken cancel var symbolId = graphNode.GetValue(RoslynGraphProperties.SymbolId); if (symbolId is not null) { - var symbol = symbolId.Value.Resolve(await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false), cancellationToken: cancellationToken).GetAnySymbol(); - if (symbol is null) - return; - - await GoToDefinitionHelpers.TryNavigateToLocationAsync( - symbol, project.Solution, _threadingContext, _streamingPresenter.Value, cancellationToken).ConfigureAwait(false); - return; + var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); + if (compilation is not null) + { + var symbol = symbolId.Value.Resolve(compilation, cancellationToken: cancellationToken).GetAnySymbol(); + if (symbol is not null) + { + await GoToDefinitionHelpers.TryNavigateToLocationAsync( + symbol, project.Solution, _threadingContext, _streamingPresenter.Value, cancellationToken).ConfigureAwait(false); + return; + } + } } // If we didn't have a symbol id, attempt to navigate to the source location directly if the node includes one. @@ -84,7 +85,7 @@ await GoToDefinitionHelpers.TryNavigateToLocationAsync( // document, but only exists in the other var editorWorkspace = document.Project.Solution.Workspace; - var navigationService = editorWorkspace.Services.GetService(); + var navigationService = editorWorkspace.Services.GetRequiredService(); // TODO: Get the platform to use and pass us an operation context, or create one ourselves. await navigationService.TryNavigateToLineAndOffsetAsync( From ee15f98e9a615954b75b89085060fbd7f40ce1a5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 5 Nov 2024 12:47:54 -0800 Subject: [PATCH 179/508] Allow renaming with overload resolution when there is only one overload --- .../RenameTrackingTaggerProviderTests.cs | 29 +++++++++++++-- .../Test2/Rename/RenameCommandHandlerTests.vb | 1 - .../Rename/RenameNonRenameableSymbols.vb | 36 ++++++++----------- .../Core/Portable/Rename/RenameUtilities.cs | 6 ++++ 4 files changed, 47 insertions(+), 25 deletions(-) diff --git a/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs b/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs index 2e50137e128d2..6b09f4b1e8363 100644 --- a/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs +++ b/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs @@ -857,8 +857,10 @@ public void RenameTrackingDoesNotThrowAggregateException() Assert.Same(thrownException, caughtException); } - [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1063943")] - public async Task RenameTrackingNotFromReferenceWithWrongNumberOfArguments() + [WpfFact] + [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1063943")] + [WorkItem("https://github.com/dotnet/roslyn/issues/10914")] + public async Task RenameTrackingOnReferenceWithWrongNumberOfArguments() { var code = @" class C @@ -869,6 +871,29 @@ void M(int x) } }"; + using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); + state.EditorOperations.InsertText("eow"); + await state.AssertTag("M", "Meow"); + } + + [WpfFact] + [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1063943")] + [WorkItem("https://github.com/dotnet/roslyn/issues/10914")] + public async Task RenameTrackingOnReferenceWithWrongNumberOfArguments_Overloads() + { + var code = @" +class C +{ + void M(int x) + { + M$$(); + } + + void M(bool x) + { + } +}"; + using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("eow"); await state.AssertNoTag(); diff --git a/src/EditorFeatures/Test2/Rename/RenameCommandHandlerTests.vb b/src/EditorFeatures/Test2/Rename/RenameCommandHandlerTests.vb index f764b4ef590d1..19406ef40c3f5 100644 --- a/src/EditorFeatures/Test2/Rename/RenameCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/Rename/RenameCommandHandlerTests.vb @@ -6,7 +6,6 @@ Imports Microsoft.CodeAnalysis.Editor.Implementation.InlineRename Imports Microsoft.CodeAnalysis.Editor.InlineRename Imports Microsoft.CodeAnalysis.Editor.Shared.Extensions Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions -Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Text.Shared.Extensions Imports Microsoft.VisualStudio.Commanding diff --git a/src/EditorFeatures/Test2/Rename/RenameNonRenameableSymbols.vb b/src/EditorFeatures/Test2/Rename/RenameNonRenameableSymbols.vb index 270fc8854246c..d00714e271950 100644 --- a/src/EditorFeatures/Test2/Rename/RenameNonRenameableSymbols.vb +++ b/src/EditorFeatures/Test2/Rename/RenameNonRenameableSymbols.vb @@ -75,8 +75,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Rename End Using End Sub - - + Public Sub CannotRenameSpecialNames(host As RenameTestHost) Using workspace = CreateWorkspaceWithWaiter( @@ -97,8 +96,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Rename End Using End Sub - - + Public Sub CannotRenameTrivia(host As RenameTestHost) Using workspace = CreateWorkspaceWithWaiter( @@ -114,9 +112,10 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Rename End Using End Sub - - - Public Sub CannotRenameCandidateSymbol(host As RenameTestHost) + + + + Public Sub CanRenameCandidateSymbol(host As RenameTestHost) Using workspace = CreateWorkspaceWithWaiter( @@ -132,12 +131,11 @@ class Program , host) - AssertTokenNotRenamable(workspace) + AssertTokenRenamable(workspace) End Using End Sub - - + Public Sub CannotRenameSyntheticDefinition(host As RenameTestHost) Using workspace = CreateWorkspaceWithWaiter( @@ -158,8 +156,7 @@ class Program End Using End Sub - - + Public Sub CannotRenameXmlLiteralProperty(host As RenameTestHost) Using workspace = CreateWorkspaceWithWaiter( @@ -176,8 +173,7 @@ class Program End Using End Sub - - + Public Sub CannotRenameSymbolDefinedInMetaData(host As RenameTestHost) Using workspace = CreateWorkspaceWithWaiter( @@ -198,8 +194,7 @@ class Program End Using End Sub - - + Public Sub CannotRenameSymbolInReadOnlyBuffer(host As RenameTestHost) Using workspace = CreateWorkspaceWithWaiter( @@ -227,8 +222,7 @@ class Program End Using End Sub - - + Public Sub CannotRenameSymbolThatBindsToErrorType(host As RenameTestHost) Using workspace = CreateWorkspaceWithWaiter( @@ -249,8 +243,7 @@ class Program End Using End Sub - - + Public Sub CannotRenameSynthesizedParameters(host As RenameTestHost) Using workspace = CreateWorkspaceWithWaiter( @@ -635,8 +628,7 @@ namespace System End Sub - - + Public Sub RenameTupleFiledInLiteralRegress14600(host As RenameTestHost) Using workspace = CreateWorkspaceWithWaiter( diff --git a/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs b/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs index e7ab0bf835488..20b22fff40bf7 100644 --- a/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs +++ b/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs @@ -171,6 +171,12 @@ internal static TokenRenameInfo GetTokenRenameInfo( return TokenRenameInfo.CreateMemberGroupTokenInfo(symbolInfo.CandidateSymbols); } + // If we have overload resolution issues at the callsite, we generally don't want to rename (as it's unclear + // which overload the user is actually calling). However, if there is just a single overload, there's no real + // issue since it's clear which one the user wants to rename in that case. + if (symbolInfo.CandidateReason == CandidateReason.OverloadResolutionFailure && symbolInfo.CandidateSymbols.Length == 1) + return TokenRenameInfo.CreateMemberGroupTokenInfo(symbolInfo.CandidateSymbols); + if (RenameLocation.ShouldRename(symbolInfo.CandidateReason) && symbolInfo.CandidateSymbols.Length == 1) { From 17b40a47ead318f29053b842ff533c41c8b2cd10 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 5 Nov 2024 12:49:38 -0800 Subject: [PATCH 180/508] Raw strings --- .../RenameTrackingTaggerProviderTests.cs | 1231 +++++++++-------- 1 file changed, 653 insertions(+), 578 deletions(-) diff --git a/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs b/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs index 6b09f4b1e8363..82fb7dd786c14 100644 --- a/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs +++ b/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs @@ -18,15 +18,16 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.RenameTracking; [UseExportProvider] [Trait(Traits.Feature, Traits.Features.RenameTracking)] -public class RenameTrackingTaggerProviderTests +public sealed class RenameTrackingTaggerProviderTests { [WpfFact] public async Task RenameTrackingNotOnCreation() { - var code = @" -class C$$ -{ -}"; + var code = """ + class C$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); await state.AssertNoTag(); } @@ -43,10 +44,11 @@ public async Task RenameTrackingNotInBlankFile() [WpfFact] public async Task RenameTrackingTypingAtEnd() { - var code = @" -class C$$ -{ -}"; + var code = """ + class C$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("at"); await state.AssertTag("C", "Cat"); @@ -55,10 +57,11 @@ class C$$ [WpfFact] public async Task RenameTrackingTypingAtBeginning() { - var code = @" -class $$C -{ -}"; + var code = """ + class $$C + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("AB"); await state.AssertTag("C", "ABC"); @@ -67,10 +70,11 @@ class $$C [WpfFact] public async Task RenameTrackingTypingInMiddle() { - var code = @" -class AB$$CD -{ -}"; + var code = """ + class AB$$CD + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("ZZ"); await state.AssertTag("ABCD", "ABZZCD"); @@ -79,10 +83,11 @@ class AB$$CD [WpfFact] public async Task RenameTrackingDeleteFromEnd() { - var code = @" -class ABC$$ -{ -}"; + var code = """ + class ABC$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Backspace(); await state.AssertTag("ABC", "AB"); @@ -91,10 +96,11 @@ class ABC$$ [WpfFact] public async Task RenameTrackingDeleteFromBeginning() { - var code = @" -class $$ABC -{ -}"; + var code = """ + class $$ABC + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Delete(); await state.AssertTag("ABC", "BC"); @@ -103,10 +109,11 @@ class $$ABC [WpfFact] public async Task RenameTrackingDeleteFromMiddle() { - var code = @" -class AB$$C -{ -}"; + var code = """ + class AB$$C + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Backspace(); await state.AssertTag("ABC", "AC"); @@ -115,10 +122,11 @@ class AB$$C [WpfFact] public async Task RenameTrackingNotOnClassKeyword() { - var code = @" -class$$ ABCD -{ -}"; + var code = """ + class$$ ABCD + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("d"); await state.AssertNoTag(); @@ -127,15 +135,16 @@ public async Task RenameTrackingNotOnClassKeyword() [WpfFact] public async Task RenameTrackingNotAtMethodArgument() { - var code = @" -class ABCD -{ - void Goo(int x) - { - int abc = 3; - Goo($$ - } -}"; + var code = """ + class ABCD + { + void Goo(int x) + { + int abc = 3; + Goo($$ + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("a"); await state.AssertNoTag(); @@ -147,10 +156,11 @@ void Goo(int x) [WpfFact] public async Task RenameTrackingSessionContinuesAfterViewingTag() { - var code = @" -class C$$ -{ -}"; + var code = """ + class C$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("at"); await state.AssertTag("C", "Cat"); @@ -162,14 +172,15 @@ class C$$ [WpfFact] public async Task RenameTrackingNotInString() { - var code = @" -class C -{ - void Goo() - { - string s = ""abc$$"" - } -}"; + var code = """ + class C + { + void Goo() + { + string s = "abc$$" + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("d"); await state.AssertNoTag(); @@ -178,10 +189,11 @@ void Goo() [WpfFact] public async Task RenameTrackingHandlesAtSignAsCSharpEscape() { - var code = @" -class $$C -{ -}"; + var code = """ + class $$C + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("@"); await state.AssertTag("C", "@C"); @@ -190,9 +202,10 @@ class $$C [WpfFact] public async Task RenameTrackingHandlesSquareBracketsAsVisualBasicEscape() { - var code = @" -Class $$C -End Class"; + var code = """ + Class $$C + End Class + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.InsertText("["); await state.AssertNoTag(); @@ -205,10 +218,11 @@ public async Task RenameTrackingHandlesSquareBracketsAsVisualBasicEscape() [WpfFact] public async Task RenameTrackingNotOnSquareBracketsInCSharp() { - var code = @" -class $$C -{ -}"; + var code = """ + class $$C + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("["); await state.AssertNoTag(); @@ -221,10 +235,11 @@ class $$C [WpfFact] public async Task RenameTrackingHandlesUnicode() { - var code = @" -class C$$ -{ -}"; + var code = """ + class C$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("\u0414\u046E\u046A\u00DB\u00CA\u00DB\u00C4\u00C1\u00CD\u00E4\u00E1\u0152\u0178\u00F5\u00E0\u0178\u00FC\u00C4\u00B5\u00C1i\u00DBE\u00EA\u00E0\u00EA\u00E8\u00E4\u00E5\u00ED\u00F2\u00E8\u00F4\u00E8\u00EA\u00E0\u00F2\u00EE\u00F0\u00F1\u00EB\u00EE\u00E2\u00EE"); await state.AssertTag("C", "C\u0414\u046E\u046A\u00DB\u00CA\u00DB\u00C4\u00C1\u00CD\u00E4\u00E1\u0152\u0178\u00F5\u00E0\u0178\u00FC\u00C4\u00B5\u00C1i\u00DBE\u00EA\u00E0\u00EA\u00E8\u00E4\u00E5\u00ED\u00F2\u00E8\u00F4\u00E8\u00EA\u00E0\u00F2\u00EE\u00F0\u00F1\u00EB\u00EE\u00E2\u00EE"); @@ -233,10 +248,11 @@ class C$$ [WpfFact] public async Task RenameTrackingThroughKeyword() { - var code = @" -class i$$ -{ -}"; + var code = """ + class i$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("n"); await state.AssertNoTag(); @@ -251,10 +267,11 @@ class i$$ [WpfFact] public async Task RenameTrackingThroughIllegalStartCharacter() { - var code = @" -class $$abc -{ -}"; + var code = """ + class $$abc + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("9"); await state.AssertNoTag(); @@ -267,10 +284,11 @@ class $$abc [WpfFact] public async Task RenameTrackingOnBothSidesOfIdentifier() { - var code = @" -class $$Def -{ -}"; + var code = """ + class $$Def + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("Abc"); await state.AssertTag("Def", "AbcDef"); @@ -283,10 +301,11 @@ class $$Def [WpfFact] public async Task RenameTrackingThroughSameIdentifier() { - var code = @" -class C$$ -{ -}"; + var code = """ + class C$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("s"); await state.AssertTag("C", "Cs"); @@ -301,10 +320,11 @@ class C$$ [WpfFact] public async Task RenameTrackingThroughEmptyString() { - var code = @" -class C$$ -{ -}"; + var code = """ + class C$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Backspace(); await state.AssertNoTag(); @@ -316,10 +336,11 @@ class C$$ [WpfFact] public async Task RenameTrackingThroughEmptyStringWithCaretMove() { - var code = @" -class C$$ -{ -}"; + var code = """ + class C$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Backspace(); state.MoveCaret(-4); @@ -333,10 +354,11 @@ class C$$ [WpfFact] public async Task RenameTrackingNotThroughEmptyStringResumeOnDifferentSpace() { - var code = @" -class C$$ -{ -}"; + var code = """ + class C$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Backspace(); @@ -350,10 +372,11 @@ class C$$ [WpfFact] public async Task RenameTrackingReplaceIdentifierSuffix() { - var code = @" -class Identifi[|er|]$$ -{ -}"; + var code = """ + class Identifi[|er|]$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); var textSpan = state.HostDocument.SelectedSpans.Single(); state.EditorOperations.ReplaceText(new Span(textSpan.Start, textSpan.Length), "cation"); @@ -363,10 +386,11 @@ class Identifi[|er|]$$ [WpfFact] public async Task RenameTrackingReplaceIdentifierPrefix() { - var code = @" -class $$[|Ident|]ifier -{ -}"; + var code = """ + class $$[|Ident|]ifier + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); var textSpan = state.HostDocument.SelectedSpans.Single(); state.EditorOperations.ReplaceText(new Span(textSpan.Start, textSpan.Length), "Complex"); @@ -376,10 +400,11 @@ class $$[|Ident|]ifier [WpfFact] public async Task RenameTrackingReplaceIdentifierCompletely() { - var code = @" -class [|Cat|]$$ -{ -}"; + var code = """ + class [|Cat|]$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); var textSpan = state.HostDocument.SelectedSpans.Single(); state.EditorOperations.ReplaceText(new Span(textSpan.Start, textSpan.Length), "Dog"); @@ -389,16 +414,17 @@ class [|Cat|]$$ [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/34280")] public async Task RenameTrackingReplaceIdentifierWithDiscard() { - var code = @" -class Class -{ - int Method() - { - int i; - [|i|]$$ = Method(); - rteurn 0; - } -}"; + var code = """ + class Class + { + int Method() + { + int i; + [|i|]$$ = Method(); + rteurn 0; + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); var textSpan = state.HostDocument.SelectedSpans.Single(); state.EditorOperations.ReplaceText(new Span(textSpan.Start, textSpan.Length), "_"); @@ -408,10 +434,11 @@ int Method() [WpfFact] public async Task RenameTrackingNotAfterInvoke() { - var code = @" -class Cat$$ -{ -}"; + var code = """ + class Cat$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("s"); await state.AssertTag("Cat", "Cats", invokeAction: true); @@ -422,10 +449,11 @@ class Cat$$ [WpfFact] public async Task RenameTrackingInvokeAndChangeBackToOriginal() { - var code = @" -class Cat$$ -{ -}"; + var code = """ + class Cat$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("s"); await state.AssertTag("Cat", "Cats", invokeAction: true); @@ -439,10 +467,11 @@ class Cat$$ [WpfFact] public async Task RenameTrackingUndoOnceAndStartNewSession() { - var code = @" -class Cat$$ -{ -}"; + var code = """ + class Cat$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("abc"); await state.AssertTag("Cat", "Catabc", invokeAction: true); @@ -460,10 +489,11 @@ class Cat$$ [WpfFact] public async Task RenameTrackingUndoTwiceAndContinueSession() { - var code = @" -class Cat$$ -{ -}"; + var code = """ + class Cat$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("abc"); await state.AssertTag("Cat", "Catabc", invokeAction: true); @@ -481,10 +511,11 @@ class Cat$$ [WpfFact] public async Task RenameTrackingRedoAlwaysClearsState() { - var code = @" -class Cat$$ -{ -}"; + var code = """ + class Cat$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("s"); await state.AssertTag("Cat", "Cats", invokeAction: true); @@ -505,10 +536,11 @@ class Cat$$ [WpfFact] public async Task RenameTrackingUndoTwiceRedoTwiceUndoStillWorks() { - var code = @" -class Cat$$ -{ -}"; + var code = """ + class Cat$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("s"); await state.AssertTag("Cat", "Cats", invokeAction: true); @@ -534,14 +566,15 @@ class Cat$$ [WpfFact] public async Task RenameTrackingOnReference_ParameterAsArgument() { - var code = @" -class C -{ - void M(int x) - { - M(x$$); - } -}"; + var code = """ + class C + { + void M(int x) + { + M(x$$); + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("yz"); await state.AssertTag("x", "xyz"); @@ -550,14 +583,15 @@ void M(int x) [WpfFact] public async Task RenameTrackingOnReference_ParameterAsNamedArgument() { - var code = @" -class C -{ - void M(int x) - { - M(x$$: x); - } -}"; + var code = """ + class C + { + void M(int x) + { + M(x$$: x); + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("yz"); await state.AssertTag("x", "xyz"); @@ -566,17 +600,18 @@ void M(int x) [WpfFact] public async Task RenameTrackingOnReference_Namespace() { - var code = @" -namespace NS -{ - class C - { - static void M() - { - NS$$.C.M(); - } - } -}"; + var code = """ + namespace NS + { + class C + { + static void M() + { + NS$$.C.M(); + } + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("A"); await state.AssertTag("NS", "NSA"); @@ -585,118 +620,120 @@ static void M() [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/21657")] public async Task RenameTrackingOnReference_Attribute_CSharp() { - var code = @" -using System; + var code = """ + using System; -class [|$$ustom|]Attribute : Attribute -{ -} -"; + class [|$$ustom|]Attribute : Attribute + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("C"); await state.AssertTag("ustomAttribute", "CustomAttribute", invokeAction: true); - var expectedCode = @" -using System; + var expectedCode = """ + using System; -class CustomAttribute : Attribute -{ -} -"; + class CustomAttribute : Attribute + { + } + """; Assert.Equal(expectedCode, state.HostDocument.GetTextBuffer().CurrentSnapshot.GetText()); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/21657")] public async Task RenameTrackingOnReference_Attribute_VB() { - var code = @" -Import System; + var code = """ + Import System; -Public Class [|$$ustom|]Attribute - Inherits Attribute -End Class -"; + Public Class [|$$ustom|]Attribute + Inherits Attribute + End Class + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.InsertText("C"); await state.AssertTag("ustomAttribute", "CustomAttribute", invokeAction: true); - var expectedCode = @" -Import System; + var expectedCode = """ + Import System; -Public Class CustomAttribute - Inherits Attribute -End Class -"; + Public Class CustomAttribute + Inherits Attribute + End Class + """; Assert.Equal(expectedCode, state.HostDocument.GetTextBuffer().CurrentSnapshot.GetText()); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/21657")] public async Task RenameTrackingOnReference_Capitalized_Attribute_VB() { - var code = @" -Import System; + var code = """ + Import System; -Public Class [|$$ustom|]ATTRIBUTE - Inherits Attribute -End Class -"; + Public Class [|$$ustom|]ATTRIBUTE + Inherits Attribute + End Class + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.InsertText("C"); await state.AssertTag("ustomATTRIBUTE", "CustomATTRIBUTE", invokeAction: true); - var expectedCode = @" -Import System; + var expectedCode = """ + Import System; -Public Class CustomATTRIBUTE - Inherits Attribute -End Class -"; + Public Class CustomATTRIBUTE + Inherits Attribute + End Class + """; Assert.Equal(expectedCode, state.HostDocument.GetTextBuffer().CurrentSnapshot.GetText()); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/21657")] public async Task RenameTrackingOnReference_Not_Capitalized_Attribute_VB() { - var code = @" -Import System; + var code = """ + Import System; -Public Class [|$$ustom|]attribute - Inherits Attribute -End Class -"; + Public Class [|$$ustom|]attribute + Inherits Attribute + End Class + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.InsertText("C"); await state.AssertTag("ustomattribute", "Customattribute", invokeAction: true); - var expectedCode = @" -Import System; + var expectedCode = """ + Import System; -Public Class Customattribute - Inherits Attribute -End Class -"; + Public Class Customattribute + Inherits Attribute + End Class + """; Assert.Equal(expectedCode, state.HostDocument.GetTextBuffer().CurrentSnapshot.GetText()); } [WpfFact] public async Task RenameTrackingNotifiesThirdPartiesOfRenameOperation() { - var code = @" -class Cat$$ -{ - public Cat() - { - } -}"; + var code = """ + class Cat$$ + { + public Cat() + { + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("s"); await state.AssertTag("Cat", "Cats", invokeAction: true); Assert.Equal(1, state.RefactorNotifyService.OnBeforeSymbolRenamedCount); Assert.Equal(1, state.RefactorNotifyService.OnAfterSymbolRenamedCount); - var expectedCode = @" -class Cats -{ - public Cats() - { - } -}"; + var expectedCode = """ + class Cats + { + public Cats() + { + } + } + """; Assert.Equal(expectedCode, state.HostDocument.GetTextBuffer().CurrentSnapshot.GetText()); state.AssertNoNotificationMessage(); @@ -706,13 +743,14 @@ public Cats() [WpfFact] public async Task RenameTrackingHonorsThirdPartyRequestsForCancellationBeforeRename() { - var code = @" -class Cat$$ -{ - public Cat() - { - } -}"; + var code = """ + class Cat$$ + { + public Cat() + { + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp, onBeforeGlobalSymbolRenamedReturnValue: false); state.EditorOperations.InsertText("s"); await state.AssertTag("Cat", "Cats", invokeAction: true); @@ -722,13 +760,14 @@ public Cat() Assert.Equal(0, state.RefactorNotifyService.OnAfterSymbolRenamedCount); await state.AssertNoTag(); - var expectedCode = @" -class Cat -{ - public Cat() - { - } -}"; + var expectedCode = """ + class Cat + { + public Cat() + { + } + } + """; Assert.Equal(expectedCode, state.HostDocument.GetTextBuffer().CurrentSnapshot.GetText()); state.AssertNotificationMessage(); @@ -737,13 +776,14 @@ public Cat() [WpfFact] public async Task RenameTrackingAlertsAboutThirdPartyRequestsForCancellationAfterRename() { - var code = @" -class Cat$$ -{ - public Cat() - { - } -}"; + var code = """ + class Cat$$ + { + public Cat() + { + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp, onAfterGlobalSymbolRenamedReturnValue: false); state.EditorOperations.InsertText("s"); await state.AssertTag("Cat", "Cats", invokeAction: true); @@ -753,13 +793,14 @@ public Cat() state.AssertNotificationMessage(); // Make sure the rename completed - var expectedCode = @" -class Cats -{ - public Cats() - { - } -}"; + var expectedCode = """ + class Cats + { + public Cats() + { + } + } + """; Assert.Equal(expectedCode, state.HostDocument.GetTextBuffer().CurrentSnapshot.GetText()); await state.AssertNoTag(); } @@ -767,12 +808,13 @@ public Cats() [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530469")] public async Task RenameTrackingNotWhenStartedFromTextualWordInTrivia() { - var code = @" -Module Program - Sub Main() - Dim [x$$ = 1 - End Sub -End Module"; + var code = """ + Module Program + Sub Main() + Dim [x$$ = 1 + End Sub + End Module + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.InsertText("]"); await state.AssertNoTag(); @@ -781,12 +823,13 @@ End Sub [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530495")] public async Task RenameTrackingNotWhenCaseCorrectingReference() { - var code = @" -Module Program - Sub Main() - $$main() - End Sub -End Module"; + var code = """ + Module Program + Sub Main() + $$main() + End Sub + End Module + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.Delete(); await state.AssertTag("main", "ain"); @@ -797,14 +840,15 @@ End Sub [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/599508")] public async Task RenameTrackingNotWhenNewIdentifierReferenceBinds() { - var code = @" -Module Program - Sub Main() - $$[|main|]() - End Sub - Sub Goo() - End Sub -End Module"; + var code = """ + Module Program + Sub Main() + $$[|main|]() + End Sub + Sub Goo() + End Sub + End Module + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); var textSpan = state.HostDocument.SelectedSpans.Single(); state.EditorOperations.ReplaceText(new Span(textSpan.Start, textSpan.Length), "Go"); @@ -816,10 +860,11 @@ End Sub [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530400")] public async Task RenameTrackingNotWhenDeclaringEnumMembers() { - var code = @" -Enum E -$$ -End Enum"; + var code = """ + Enum E + $$ + End Enum + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.InsertText(" a"); state.EditorOperations.InsertText("b"); @@ -862,14 +907,15 @@ public void RenameTrackingDoesNotThrowAggregateException() [WorkItem("https://github.com/dotnet/roslyn/issues/10914")] public async Task RenameTrackingOnReferenceWithWrongNumberOfArguments() { - var code = @" -class C -{ - void M(int x) - { - M$$(); - } -}"; + var code = """ + class C + { + void M(int x) + { + M$$(); + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("eow"); @@ -881,18 +927,19 @@ void M(int x) [WorkItem("https://github.com/dotnet/roslyn/issues/10914")] public async Task RenameTrackingOnReferenceWithWrongNumberOfArguments_Overloads() { - var code = @" -class C -{ - void M(int x) - { - M$$(); - } + var code = """ + class C + { + void M(int x) + { + M$$(); + } - void M(bool x) - { - } -}"; + void M(bool x) + { + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("eow"); @@ -902,10 +949,11 @@ void M(bool x) [WpfFact] public async Task CancelRenameTracking() { - var code = @" -class C$$ -{ -}"; + var code = """ + class C$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("at"); await state.AssertTag("C", "Cat"); @@ -916,10 +964,11 @@ class C$$ [WpfFact] public async Task RenameTrackingNotWhenDeclaringEnumMembersEvenAfterCancellation() { - var code = @" -Enum E -$$ -End Enum"; + var code = """ + Enum E + $$ + End Enum + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.InsertText(" a"); state.EditorOperations.InsertText("b"); @@ -932,10 +981,11 @@ Enum E [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/540")] public async Task RenameTrackingDoesNotProvideDiagnosticAfterCancellation() { - var code = @" -class C$$ -{ -}"; + var code = """ + class C$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("at"); await state.AssertTag("C", "Cat"); @@ -951,36 +1001,38 @@ class C$$ [WpfFact] public async Task RenameTracking_Nameof_FromMethodGroupReference() { - var code = @" -class C -{ - void M() - { - nameof(M$$).ToString(); - } - - void M(int x) - { - } -}"; + var code = """ + class C + { + void M() + { + nameof(M$$).ToString(); + } + + void M(int x) + { + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("at"); await state.AssertTag("M", "Mat", invokeAction: true); // Make sure the rename completed - var expectedCode = @" -class C -{ - void Mat() - { - nameof(Mat).ToString(); - } - - void Mat(int x) - { - } -}"; + var expectedCode = """ + class C + { + void Mat() + { + nameof(Mat).ToString(); + } + + void Mat(int x) + { + } + } + """; Assert.Equal(expectedCode, state.HostDocument.GetTextBuffer().CurrentSnapshot.GetText()); await state.AssertNoTag(); } @@ -988,28 +1040,30 @@ void Mat(int x) [WpfFact] public async Task RenameTracking_Nameof_FromMethodDefinition_NoOverloads() { - var code = @" -class C -{ - void M$$() - { - nameof(M).ToString(); - } -}"; + var code = """ + class C + { + void M$$() + { + nameof(M).ToString(); + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("at"); await state.AssertTag("M", "Mat", invokeAction: true); // Make sure the rename completed - var expectedCode = @" -class C -{ - void Mat() - { - nameof(Mat).ToString(); - } -}"; + var expectedCode = """ + class C + { + void Mat() + { + nameof(Mat).ToString(); + } + } + """; Assert.Equal(expectedCode, state.HostDocument.GetTextBuffer().CurrentSnapshot.GetText()); await state.AssertNoTag(); } @@ -1017,36 +1071,38 @@ void Mat() [WpfFact] public async Task RenameTracking_Nameof_FromMethodDefinition_WithOverloads() { - var code = @" -class C -{ - void M$$() - { - nameof(M).ToString(); - } - - void M(int x) - { - } -}"; + var code = """ + class C + { + void M$$() + { + nameof(M).ToString(); + } + + void M(int x) + { + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("at"); await state.AssertTag("M", "Mat", invokeAction: true); // Make sure the rename completed - var expectedCode = @" -class C -{ - void Mat() - { - nameof(M).ToString(); - } - - void M(int x) - { - } -}"; + var expectedCode = """ + class C + { + void Mat() + { + nameof(M).ToString(); + } + + void M(int x) + { + } + } + """; Assert.Equal(expectedCode, state.HostDocument.GetTextBuffer().CurrentSnapshot.GetText()); await state.AssertNoTag(); } @@ -1054,14 +1110,15 @@ void M(int x) [WpfFact] public async Task RenameTracking_Nameof_FromReferenceToMetadata_NoTag() { - var code = @" -class C -{ - void M() - { - var x = nameof(ToString$$); - } -}"; + var code = """ + class C + { + void M() + { + var x = nameof(ToString$$); + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("z"); await state.AssertNoTag(); @@ -1070,16 +1127,17 @@ void M() [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/762964")] public async Task RenameTracking_NoTagWhenFirstEditChangesReferenceToAnotherSymbol() { - var code = @" -class C -{ - void M() - { - int abc = 7; - int ab = 8; - int z = abc$$; - } -}"; + var code = """ + class C + { + void M() + { + int abc = 7; + int ab = 8; + int z = abc$$; + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Backspace(); await state.AssertNoTag(); @@ -1088,14 +1146,15 @@ void M() [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/2605")] public async Task RenameTracking_CannotRenameToVarInCSharp() { - var code = @" -class C -{ - void M() - { - C$$ c; - } -}"; + var code = """ + class C + { + void M() + { + C$$ c; + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Backspace(); state.EditorOperations.InsertText("va"); @@ -1115,14 +1174,15 @@ void M() [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/2605")] public async Task RenameTracking_CannotRenameFromVarInCSharp() { - var code = @" -class C -{ - void M() - { - var$$ c = new C(); - } -}"; + var code = """ + class C + { + void M() + { + var$$ c = new C(); + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Backspace(); await state.AssertNoTag(); @@ -1132,12 +1192,13 @@ void M() [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/2605")] public async Task RenameTracking_CanRenameToVarInVisualBasic() { - var code = @" -Class C - Sub M() - Dim x as C$$ - End Sub -End Class"; + var code = """ + Class C + Sub M() + Dim x as C$$ + End Sub + End Class + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.Backspace(); state.EditorOperations.InsertText("var"); @@ -1149,14 +1210,15 @@ End Sub [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/2605")] public async Task RenameTracking_CannotRenameToDynamicInCSharp() { - var code = @" -class C -{ - void M() - { - C$$ c; - } -}"; + var code = """ + class C + { + void M() + { + C$$ c; + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Backspace(); state.EditorOperations.InsertText("dynami"); @@ -1176,15 +1238,16 @@ void M() [WpfFact] public async Task RenameImplicitTupleField() { - var code = @" -class C -{ - void M() - { - (int, int) x = (1, 2); - var y = x.Item1$$; - } -}"; + var code = """ + class C + { + void M() + { + (int, int) x = (1, 2); + var y = x.Item1$$; + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Backspace(); state.EditorOperations.Backspace(); @@ -1195,14 +1258,14 @@ void M() [WpfFact] public async Task RenameImplicitTupleFieldVB() { - var code = @" -class C - Sub M() - Dim x as (Integer, Integer) = (1, 2) - Dim y = x.Item1$$ - End Sub -End Class -"; + var code = """ + class C + Sub M() + Dim x as (Integer, Integer) = (1, 2) + Dim y = x.Item1$$ + End Sub + End Class + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.Backspace(); state.EditorOperations.Backspace(); @@ -1213,16 +1276,16 @@ End Class [WpfFact] public async Task RenameImplicitTupleFieldExtended() { - var code = @" -class C -{ - void M() - { - (int, int, int, int, int, int, int, int, int, int) x = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - var y = x.Item9$$; - } -} -"; + var code = """ + class C + { + void M() + { + (int, int, int, int, int, int, int, int, int, int) x = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + var y = x.Item9$$; + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Backspace(); state.EditorOperations.Backspace(); @@ -1233,14 +1296,14 @@ void M() [WpfFact] public async Task RenameImplicitTupleFieldExtendedVB() { - var code = @" -Class C - Sub M() - Dim x as (Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer) = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) - Dim y = x.Item9$$ - End Sub -End Class -"; + var code = """ + Class C + Sub M() + Dim x as (Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer) = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + Dim y = x.Item9$$ + End Sub + End Class + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.Backspace(); state.EditorOperations.Backspace(); @@ -1251,15 +1314,16 @@ End Class [WpfFact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems?_a=edit&id=371205")] public async Task RenameTrackingNotOnExplicitTupleReturnDeclaration_CSharp() { - var code = @" -class C -{ - void M() - { - (int abc$$, int) x = (1, 2); - var y = x.abc; - } -}"; + var code = """ + class C + { + void M() + { + (int abc$$, int) x = (1, 2); + var y = x.abc; + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Backspace(); state.EditorOperations.Backspace(); @@ -1270,13 +1334,14 @@ void M() [WpfFact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems?_a=edit&id=371205")] public async Task RenameTrackingNotOnExplicitTupleReturnDeclaration_VB() { - var code = @" -class C - Sub M() - Dim x as (abc$$ as integer, int Item2 as integer) = (1, 2) - Dim y = x.abc - End Sub -End Class"; + var code = """ + class C + Sub M() + Dim x as (abc$$ as integer, int Item2 as integer) = (1, 2) + Dim y = x.abc + End Sub + End Class + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.Backspace(); state.EditorOperations.Backspace(); @@ -1287,15 +1352,16 @@ End Sub [WpfFact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems?_a=edit&id=371205")] public async Task RenameTrackingNotOnExplicitTupleFieldReference_CSharp() { - var code = @" -class C -{ - void M() - { - (int abc, int) x = (1, 2); - var y = x.abc$$; - } -}"; + var code = """ + class C + { + void M() + { + (int abc, int) x = (1, 2); + var y = x.abc$$; + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Backspace(); state.EditorOperations.Backspace(); @@ -1306,13 +1372,14 @@ void M() [WpfFact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems?_a=edit&id=371205")] public async Task RenameTrackingNotOnExplicitTupleFieldReference_VB() { - var code = @" -class C - Sub M() - Dim x as (abc as integer, int Item2 as integer) = (1, 2) - Dim y = x.abc$$ - End Sub -End Class"; + var code = """ + class C + Sub M() + Dim x as (abc as integer, int Item2 as integer) = (1, 2) + Dim y = x.abc$$ + End Sub + End Class + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.Backspace(); state.EditorOperations.Backspace(); @@ -1323,14 +1390,15 @@ End Sub [WpfFact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems?_a=edit&id=371205")] public async Task RenameTrackingNotOnExplicitTupleElementsInDeclarations_CSharp() { - var code = @" -class C -{ - void M() - { - var t = (x$$: 1, y: 2); - } -}"; + var code = """ + class C + { + void M() + { + var t = (x$$: 1, y: 2); + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("2"); await state.AssertNoTag(); @@ -1339,12 +1407,13 @@ void M() [WpfFact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems?_a=edit&id=371205")] public async Task RenameTrackingNotOnExplicitTupleElementsInDeclarations_VB() { - var code = @" -Class C - Sub M() - Dim t = (x$$:=1, y:=2) - End Sub -End Class"; + var code = """ + Class C + Sub M() + Dim t = (x$$:=1, y:=2) + End Sub + End Class + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.InsertText("2"); await state.AssertNoTag(); @@ -1353,30 +1422,31 @@ End Sub [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/14159")] public async Task RenameTrackingNotOnWellKnownValueTupleType() { - var workspaceXml = @" - - - -using System; - -class C -{ - void M() - { - var x = new ValueTuple$$<int>(); - } -} - -namespace System -{ - public struct ValueTuple<T1> - { - public T1 Item1; - } -} - - -"; + var workspaceXml = """ + + + + using System; + + class C + { + void M() + { + var x = new ValueTuple$$<int>(); + } + } + + namespace System + { + public struct ValueTuple<T1> + { + public T1 Item1; + } + } + + + + """; using var state = RenameTrackingTestState.CreateFromWorkspaceXml(workspaceXml, LanguageNames.CSharp); state.EditorOperations.InsertText("2"); await state.AssertNoTag(); @@ -1385,25 +1455,26 @@ public struct ValueTuple<T1> [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/14159")] public async Task RenameTrackingOnThingsCalledValueTupleThatAreNotTheWellKnownType() { - var workspaceXml = @" - - - -class C -{ - void M() - { - var x = new ValueTuple$$<int>(); - } -} - -public struct ValueTuple<T1> -{ - public T1 Item1; -} - - -"; + var workspaceXml = """ + + + + class C + { + void M() + { + var x = new ValueTuple$$<int>(); + } + } + + public struct ValueTuple<T1> + { + public T1 Item1; + } + + + + """; using var state = RenameTrackingTestState.CreateFromWorkspaceXml(workspaceXml, LanguageNames.CSharp); state.EditorOperations.InsertText("2"); await state.AssertTag("ValueTuple", "ValueTuple2"); @@ -1412,15 +1483,16 @@ public struct ValueTuple<T1> [WpfFact] public async Task RenameTrackingOnDeconstruct() { - var code = @" -class C -{ - void Deconstruct$$(out int x1, out int x2) { x1 = 1; x2 = 2; } - void M() - { - var (y1, y2) = this; - } -}"; + var code = """ + class C + { + void Deconstruct$$(out int x1, out int x2) { x1 = 1; x2 = 2; } + void M() + { + var (y1, y2) = this; + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("2"); await state.AssertTag("Deconstruct", "Deconstruct2"); @@ -1429,10 +1501,11 @@ void M() [WpfFact] public async Task RenameTracking_UnmanagedConstraint_Keyword() { - var code = @" -class C<T> where T : $$unmanaged -{ -}"; + var code = """ + class C<T> where T : $$unmanaged + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); await state.AssertNoTag(); } @@ -1440,26 +1513,28 @@ class C<T> where T : $$unmanaged [WpfFact] public async Task RenameTracking_UnmanagedConstraint_Type() { - var code = @" -interface unmanaged -{ -} -class C<T> where T : $$unmanaged -{ -}"; + var code = """ + interface unmanaged + { + } + class C<T> where T : $$unmanaged + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("my"); await state.AssertTag("unmanaged", "myunmanaged", invokeAction: true); // Make sure the rename completed - var expectedCode = @" -interface myunmanaged -{ -} -class C where T : myunmanaged -{ -}"; + var expectedCode = """ + interface myunmanaged + { + } + class C where T : myunmanaged + { + } + """; Assert.Equal(expectedCode, state.HostDocument.GetTextBuffer().CurrentSnapshot.GetText()); await state.AssertNoTag(); } From e0cbf321b227d2e4ff06fe2c0bf367f3352031de Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 5 Nov 2024 13:33:29 -0800 Subject: [PATCH 181/508] Improve pattern matching when user is likely providing camel humps --- .../Test/Utilities/PatternMatcherTests.cs | 2 + .../CSharpCompletionCommandHandlerTests.vb | 71 ++++++++++++++++++- .../PatternMatcher.TextChunk.cs | 2 + .../PatternMatching/PatternMatcher.cs | 68 +++++++++++++----- 4 files changed, 125 insertions(+), 18 deletions(-) diff --git a/src/EditorFeatures/Test/Utilities/PatternMatcherTests.cs b/src/EditorFeatures/Test/Utilities/PatternMatcherTests.cs index 70f63b4c7c1b6..56bd915658eea 100644 --- a/src/EditorFeatures/Test/Utilities/PatternMatcherTests.cs +++ b/src/EditorFeatures/Test/Utilities/PatternMatcherTests.cs @@ -193,8 +193,10 @@ private static void VerifyBreakIntoCharacterParts(string original, params string [InlineData("Combine[|Bin|]ary", "bin", PatternMatchKind.StartOfWordSubstring, CaseInsensitive)] [InlineData("_ABC_[|Abc|]_", "Abc", PatternMatchKind.StartOfWordSubstring, CaseSensitive)] + [InlineData("[|C|]reate[|R|]ange", "CR", PatternMatchKind.CamelCaseExact, CaseSensitive)] [WorkItem("https://github.com/dotnet/roslyn/issues/51029")] + [WorkItem("https://github.com/dotnet/roslyn/issues/17275")] internal void TestNonFuzzyMatch( string candidate, string pattern, PatternMatchKind matchKind, bool isCaseSensitive) { diff --git a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb index 6cf7e7776f03c..c7058982685ed 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb @@ -16,7 +16,6 @@ Imports Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncCompletio Imports Microsoft.CodeAnalysis.Editor.Shared.Options Imports Microsoft.CodeAnalysis.Editor.[Shared].Utilities Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions -Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.Host Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.Options @@ -34,7 +33,7 @@ Imports Roslyn.Test.Utilities.TestGenerators Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense - Public Class CSharpCompletionCommandHandlerTests + Public NotInheritable Class CSharpCompletionCommandHandlerTests Public Async Function CompletionOnFileType_SameFile_NonQualified(showCompletionInArgumentLists As Boolean) As Task Using state = TestStateFactory.CreateCSharpTestState( @@ -12682,5 +12681,73 @@ internal class Program Await state.AssertCompletionItemsContain("Id", displayTextSuffix:="") End Using End Function + + + Public Async Function PreferCamelCasedExactMatchOverPrefixCaseInsensitiveMatch1(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + + , + showCompletionInArgumentLists:=showCompletionInArgumentLists, languageVersion:=LanguageVersion.CSharp12) + + state.SendTypeChars("CR") + Await state.AssertSelectedCompletionItem("CreateRange") + End Using + End Function + + + Public Async Function PreferCamelCasedExactMatchOverPrefixCaseInsensitiveMatch2(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + + , + showCompletionInArgumentLists:=showCompletionInArgumentLists, languageVersion:=LanguageVersion.CSharp12) + + state.SendTypeChars("cr") + Await state.AssertSelectedCompletionItem("Create") + End Using + End Function + + + Public Async Function PreferCamelCasedExactMatchOverPrefixCaseInsensitiveMatch3(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + + class C + { + void Create() { } + void CreateRange() { } + + void M() + { + this.$$ + } + } + , + showCompletionInArgumentLists:=showCompletionInArgumentLists, languageVersion:=LanguageVersion.CSharp12) + + state.SendTypeChars("Cr") + Await state.AssertSelectedCompletionItem("Create") + End Using + End Function End Class End Namespace diff --git a/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.TextChunk.cs b/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.TextChunk.cs index 23b15fec6d7e4..01fbfb1ab3bef 100644 --- a/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.TextChunk.cs +++ b/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.TextChunk.cs @@ -38,6 +38,7 @@ private struct TextChunk : IDisposable public WordSimilarityChecker SimilarityChecker; public readonly bool IsLowercase; + public readonly bool IsUppercase; public TextChunk(string text, bool allowFuzzingMatching) { @@ -50,6 +51,7 @@ public TextChunk(string text, bool allowFuzzingMatching) : default; IsLowercase = !ContainsUpperCaseLetter(text); + IsUppercase = !ContainsLowerCaseLetter(text); } public void Dispose() diff --git a/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.cs b/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.cs index 40d789da6019f..96c74e0f71db9 100644 --- a/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.cs +++ b/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.cs @@ -114,9 +114,19 @@ private static bool ContainsUpperCaseLetter(string pattern) for (var i = 0; i < pattern.Length; i++) { if (char.IsUpper(pattern[i])) - { return true; - } + } + + return false; + } + + private static bool ContainsLowerCaseLetter(string pattern) + { + // Expansion of "foreach(char ch in pattern)" to avoid a CharEnumerator allocation + for (var i = 0; i < pattern.Length; i++) + { + if (char.IsLower(pattern[i])) + return true; } return false; @@ -153,6 +163,8 @@ private static bool ContainsUpperCaseLetter(string pattern) in TextChunk patternChunk, bool punctuationStripped) { + using var candidateHumps = TemporaryArray.Empty; + var candidateLength = candidate.Length; var caseInsensitiveIndex = _compareInfo.IndexOf(candidate, patternChunk.Text, CompareOptions.IgnoreCase); @@ -170,15 +182,31 @@ private static bool ContainsUpperCaseLetter(string pattern) } else { + var isCaseSensitive = _compareInfo.IsPrefix(candidate, patternChunk.Text); + + if (!isCaseSensitive && patternChunk.IsUppercase) + { + // The user wrote something all upper case, but happened to match the prefix of the word. For + // example, matching `CR` against both `Create` (a case insensitive prefix match) and `CreateRange` + // (a camel hump match). We want to prefer the latter in this case as the all upper case string + // is a strong signal they wanted camel hump matching here. + PopulateCandidateHumps(); + + // Note: ensure that we match here case sensitively as well. We only want to take this if their + // camel humps actually matched real word starts. + var match = TryCamelCaseMatch(candidate, patternChunk, punctuationStripped, isLowercase: false, candidateHumps); + if (match is { IsCaseSensitive: true }) + return match; + + // Deliberately fall through. + } + // Lengths were the same, this is either a case insensitive or sensitive prefix match. return new PatternMatch( - PatternMatchKind.Prefix, punctuationStripped, isCaseSensitive: _compareInfo.IsPrefix(candidate, patternChunk.Text), - matchedSpan: GetMatchedSpan(0, patternChunk.Text.Length)); + PatternMatchKind.Prefix, punctuationStripped, isCaseSensitive, matchedSpan: GetMatchedSpan(0, patternChunk.Text.Length)); } } - using var candidateHumps = TemporaryArray.Empty; - var patternIsLowercase = patternChunk.IsLowercase; if (caseInsensitiveIndex > 0) { @@ -221,7 +249,8 @@ private static bool ContainsUpperCaseLetter(string pattern) // Now do the more expensive check to see if we're at the start of a word. This is to catch // word matches like CombineBinary. We want to find the hit against '[|Bin|]ary' not // 'Com[|bin|]e' - StringBreaker.AddWordParts(candidate, ref candidateHumps.AsRef()); + PopulateCandidateHumps(); + for (int i = 0, n = candidateHumps.Count; i < n; i++) { var hump = TextSpan.FromBounds(candidateHumps[i].Start, candidateLength); @@ -235,16 +264,17 @@ private static bool ContainsUpperCaseLetter(string pattern) } } - // Didn't have an exact/prefix match, or a high enough quality substring match. - // See if we can find a camel case match. - if (candidateHumps.Count == 0) - StringBreaker.AddWordParts(candidate, ref candidateHumps.AsRef()); + { + // Didn't have an exact/prefix match, or a high enough quality substring match. + // See if we can find a camel case match. + PopulateCandidateHumps(); - // Didn't have an exact/prefix match, or a high enough quality substring match. - // See if we can find a camel case match. - var match = TryCamelCaseMatch(candidate, patternChunk, punctuationStripped, patternIsLowercase, candidateHumps); - if (match != null) - return match; + // Didn't have an exact/prefix match, or a high enough quality substring match. + // See if we can find a camel case match. + var match = TryCamelCaseMatch(candidate, patternChunk, punctuationStripped, patternIsLowercase, candidateHumps); + if (match != null) + return match; + } // If pattern was all lowercase, we allow it to match an all lowercase section of the candidate. But // only after we've tried all other forms first. This is the weakest of all matches. For example, if @@ -265,6 +295,12 @@ private static bool ContainsUpperCaseLetter(string pattern) } return null; + + void PopulateCandidateHumps() + { + if (candidateHumps.Count == 0) + StringBreaker.AddWordParts(candidate, ref candidateHumps.AsRef()); + } } private TextSpan? GetMatchedSpan(int start, int length) From 414a61e1c2b7aa4c7f125c166f846fc0a051ef3e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 5 Nov 2024 13:58:01 -0800 Subject: [PATCH 182/508] Improve pattern matching when user is likely providing camel humps --- .../CSharpCompletionCommandHandlerTests.vb | 4 ++++ .../Portable/PatternMatching/PatternMatch.cs | 24 +++++++++++++++---- .../PatternMatching/PatternMatchKind.cs | 6 +++++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb index c7058982685ed..93e3d6a1af72e 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb @@ -12638,6 +12638,7 @@ $$ End Using End Function + Public Async Function CompletionInsideImplicitObjectCreationInsideCollectionExpression(showCompletionInArgumentLists As Boolean) As Task Using state = TestStateFactory.CreateCSharpTestState( @@ -12682,6 +12683,7 @@ internal class Program End Using End Function + Public Async Function PreferCamelCasedExactMatchOverPrefixCaseInsensitiveMatch1(showCompletionInArgumentLists As Boolean) As Task Using state = TestStateFactory.CreateCSharpTestState( @@ -12705,6 +12707,7 @@ internal class Program End Using End Function + Public Async Function PreferCamelCasedExactMatchOverPrefixCaseInsensitiveMatch2(showCompletionInArgumentLists As Boolean) As Task Using state = TestStateFactory.CreateCSharpTestState( @@ -12728,6 +12731,7 @@ internal class Program End Using End Function + Public Async Function PreferCamelCasedExactMatchOverPrefixCaseInsensitiveMatch3(showCompletionInArgumentLists As Boolean) As Task Using state = TestStateFactory.CreateCSharpTestState( diff --git a/src/Workspaces/Core/Portable/PatternMatching/PatternMatch.cs b/src/Workspaces/Core/Portable/PatternMatching/PatternMatch.cs index 76af1d62ffc13..ad069f7feb922 100644 --- a/src/Workspaces/Core/Portable/PatternMatching/PatternMatch.cs +++ b/src/Workspaces/Core/Portable/PatternMatching/PatternMatch.cs @@ -67,17 +67,33 @@ public int CompareTo(PatternMatch? other, bool ignoreCase) public int CompareTo(PatternMatch other, bool ignoreCase) { + // 1. In all scenarios, An case sensitive camel match (like CR against CreateRange) should beat a case + // insensitive match (like 'CR' against 'Create'). + // + // Other cases can be added here as necessary. + + switch (this.IsCaseSensitive, this.Kind.IsCamelCaseKind(), other.IsCaseSensitive, other.Kind.IsCamelCaseKind()) + { + case (true, true, false, false): + return -1; + case (false, false, true, true): + return 1; + } + // Compare types var comparison = this.Kind - other.Kind; if (comparison != 0) return comparison; - // Compare cases if (!ignoreCase) { - comparison = (!this.IsCaseSensitive).CompareTo(!other.IsCaseSensitive); - if (comparison != 0) - return comparison; + switch (this.IsCaseSensitive, other.IsCaseSensitive) + { + case (true, false): + return -1; + case (false, true): + return 1; + } } // Consider a match to be better if it was successful without stripping punctuation diff --git a/src/Workspaces/Core/Portable/PatternMatching/PatternMatchKind.cs b/src/Workspaces/Core/Portable/PatternMatching/PatternMatchKind.cs index e3c524344b7f5..d5fefcb9143b4 100644 --- a/src/Workspaces/Core/Portable/PatternMatching/PatternMatchKind.cs +++ b/src/Workspaces/Core/Portable/PatternMatching/PatternMatchKind.cs @@ -107,3 +107,9 @@ internal enum PatternMatchKind /// LowercaseSubstring, } + +internal static class PatternMatchKindExtensions +{ + public static bool IsCamelCaseKind(this PatternMatchKind kind) + => kind is PatternMatchKind.CamelCaseExact or PatternMatchKind.CamelCasePrefix or PatternMatchKind.CamelCaseNonContiguousPrefix or PatternMatchKind.CamelCaseSubstring; +} From c67ddccd7539bb1ccdfb82d3daa3ccdea564f8ba Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Tue, 5 Nov 2024 14:00:03 -0800 Subject: [PATCH 183/508] Use Warning as minimum severity in RedirectFeaturesAnalyzers --- .../CSharp/CSharpRedirectFeaturesAnalyzers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRedirectFeaturesAnalyzers.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRedirectFeaturesAnalyzers.cs index 93346a5b66b89..9508f418a39bc 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRedirectFeaturesAnalyzers.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRedirectFeaturesAnalyzers.cs @@ -145,7 +145,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( FeatureAttribute.ErrorList ], cancellationToken); - return await TestServices.ErrorList.GetErrorsAsync(ErrorSource.Other, __VSERRORCATEGORY.EC_MESSAGE, cancellationToken); + return await TestServices.ErrorList.GetErrorsAsync(ErrorSource.Other, __VSERRORCATEGORY.EC_WARNING, cancellationToken); } private async Task WaitForCodeActionListToPopulateAsync(CancellationToken cancellationToken) From 42ba372db201ab91ffcdac8e7c0736e6fc93a515 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 5 Nov 2024 14:20:20 -0800 Subject: [PATCH 184/508] Have the naming style fixer understand the 'I' pattern for interfaces better --- .../Tests/NamingStyles/NamingStylesTests.cs | 40 ++++++++++++++----- .../Compiler/Core/NamingStyles/NamingStyle.cs | 21 ++++++---- 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/Analyzers/CSharp/Tests/NamingStyles/NamingStylesTests.cs b/src/Analyzers/CSharp/Tests/NamingStyles/NamingStylesTests.cs index ee726f9e1485d..c184e762edd05 100644 --- a/src/Analyzers/CSharp/Tests/NamingStyles/NamingStylesTests.cs +++ b/src/Analyzers/CSharp/Tests/NamingStyles/NamingStylesTests.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -21,14 +19,10 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.NamingStyles; [Trait(Traits.Feature, Traits.Features.NamingStyle)] -public class NamingStylesTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_NoEditor +public sealed class NamingStylesTests(ITestOutputHelper logger) + : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_NoEditor(logger) { - public NamingStylesTests(ITestOutputHelper logger) - : base(logger) - { - } - - private static readonly NamingStylesTestOptionSets s_options = new NamingStylesTestOptionSets(LanguageNames.CSharp); + private static readonly NamingStylesTestOptionSets s_options = new(LanguageNames.CSharp); internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) => (new CSharpNamingStyleDiagnosticAnalyzer(), new NamingStyleCodeFixProvider()); @@ -1402,6 +1396,34 @@ internal interface """, new TestParameters(options: s_options.InterfaceNamesStartWithI)); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17656")] + public async Task TestInterfacesStartWithIOnTypeThatAlreadyStartsWithI1() + { + await TestInRegularAndScript1Async(""" + interface [|InputStream|] { } + """, """ + interface IInputStream { } + """, new TestParameters(options: s_options.InterfaceNamesStartWithI)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17656")] + public async Task TestInterfacesStartWithIOnTypeThatAlreadyStartsWithI2() + { + await TestInRegularAndScript1Async(""" + interface [|Stream|] { } + """, """ + interface IStream { } + """, new TestParameters(options: s_options.InterfaceNamesStartWithI)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17656")] + public async Task TestInterfacesStartWithIOnTypeThatAlreadyStartsWithI3() + { + await TestMissingInRegularAndScriptAsync(""" + interface [|IInputStream|] { } + """, new TestParameters(options: s_options.InterfaceNamesStartWithI)); + } + #if CODE_STYLE [Fact(Skip = "https://github.com/dotnet/roslyn/issues/42218")] #else diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/NamingStyle.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/NamingStyle.cs index 9e64f37819f53..57d4d16d1a82e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/NamingStyle.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/NamingStyle.cs @@ -427,9 +427,7 @@ private string EnsureSuffix(string name) for (var i = Suffix.Length; i > 0; i--) { if (name.EndsWith(Suffix[..i])) - { return name + Suffix[i..]; - } } return name + Suffix; @@ -437,15 +435,26 @@ private string EnsureSuffix(string name) private string EnsurePrefix(string name) { + // Exceptional cases. If the name is some interface name (like `InputStream`) and the rule is to have a single + // character prefix like "Add `I` for interfaces" don't consider the existing 'I' to be a match of the prefix. + + if (Prefix is [var prefixChar] && + char.IsUpper(prefixChar) && + name is [var nameChar1, var nameChar2, ..] && + prefixChar == nameChar1 && + char.IsLower(nameChar2)) + { + // return IInputStream here, even though InputStream already starts with 'I'. + return Prefix + name; + } + // If the name already starts with any suffix of the Prefix, only prepend the prefix of // the Prefix not contained in the longest such Prefix suffix. For example, if the // required prefix is "catdog_" and the name is "dog_test", then only prepend "cat". for (var i = 0; i < Prefix.Length; i++) { if (name.StartsWith(Prefix[i..])) - { return Prefix[..i] + name; - } } return Prefix + name; @@ -480,13 +489,11 @@ public void WriteTo(ObjectWriter writer) } public static NamingStyle ReadFrom(ObjectReader reader) - { - return new NamingStyle( + => new( reader.ReadGuid(), reader.ReadString(), reader.ReadString(), reader.ReadString(), reader.ReadString(), (Capitalization)reader.ReadInt32()); - } } From f97c50e8a5b03d27f5d0da16cc9649e07a9df95e Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Tue, 5 Nov 2024 14:45:22 -0800 Subject: [PATCH 185/508] VB: Recognize and check an `unmanaged` constraint (#75665) --- .../Portable/Symbols/BaseTypeAnalysis.cs | 62 +- .../Metadata/PE/PETypeParameterSymbol.cs | 2 +- .../Emit2/Emit/UnmanagedTypeModifierTests.cs | 64 + .../Core/Portable/Binding/UseSiteInfo.cs | 37 +- .../Symbols/INamedTypeSymbolInternal.cs | 65 + .../BasicDeterministicKeyBuilderTests.cs | 6 +- .../Utilities/VisualBasic/BasicTestSource.vb | 9 +- .../VisualBasic/CompilationTestUtils.vb | 2 +- .../Test/Utilities/VisualBasic/Extensions.vb | 4 +- .../VisualBasic/ParserTestUtilities.vb | 2 +- .../Test/Utilities/VisualBasic/TestOptions.vb | 1 + .../VisualBasic/Portable/Binding/Binder.vb | 3 + .../Portable/Binding/Binder_Invocation.vb | 2 +- .../Portable/Binding/Binder_Lookup.vb | 2 +- .../Portable/Binding/Binder_Symbols.vb | 2 +- .../Portable/Binding/Binder_Utils.vb | 2 +- .../Portable/Binding/BindingLocation.vb | 2 + .../VisualBasicSymbolMatcher.vb | 1 + .../VisualBasic/Portable/Errors/ErrorFacts.vb | 1 + .../VisualBasic/Portable/Errors/Errors.vb | 4 +- .../VisualBasic/Portable/LanguageVersion.vb | 14 +- .../VisualBasic/Portable/Parser/Parser.vb | 12 +- .../Portable/Parser/ParserFeature.vb | 7 + .../Portable/PublicAPI.Unshipped.txt | 2 +- .../Portable/Semantics/Conversions.vb | 1 + .../Portable/Semantics/OverloadResolution.vb | 2 +- ...nymousTypeOrDelegateTypeParameterSymbol.vb | 6 + .../Portable/Symbols/ArrayTypeSymbol.vb | 4 + .../Portable/Symbols/ConstraintsHelper.vb | 53 +- .../Symbols/IndexedTypeParameterSymbol.vb | 6 + .../Symbols/InstanceErrorTypeSymbol.vb | 6 + .../Metadata/PE/PETypeParameterSymbol.vb | 69 +- .../Symbols/MethodSignatureComparer.vb | 1 + .../Portable/Symbols/MethodSymbol.vb | 10 +- .../Portable/Symbols/NamedTypeSymbol.vb | 117 +- .../Symbols/ReducedExtensionMethodSymbol.vb | 18 +- .../RetargetingTypeParameterSymbol.vb | 6 + .../Symbols/Source/CrefTypeParameterSymbol.vb | 6 + .../Symbols/Source/SourceEventSymbol.vb | 15 +- .../Symbols/Source/SourceFieldSymbol.vb | 21 +- .../Portable/Symbols/Source/SourceFile.vb | 4 +- .../Symbols/Source/SourceMemberFieldSymbol.vb | 13 + .../Symbols/Source/SourceMethodSymbol.vb | 8 +- .../Symbols/Source/SourceModuleSymbol.vb | 4 +- .../Symbols/Source/SourceNamedTypeSymbol.vb | 8 +- .../Source/SourcePropertyAccessorSymbol.vb | 8 +- .../Symbols/Source/SourcePropertySymbol.vb | 16 +- .../Source/SourceTypeParameterSymbol.vb | 10 +- .../Symbols/SubstitutedTypeParameterSymbol.vb | 6 + .../SynthesizedClonedTypeParameterSymbol.vb | 6 + .../Portable/Symbols/TypeParameterSymbol.vb | 10 +- .../Portable/Symbols/TypeSymbol.vb | 8 +- .../Portable/Symbols/TypeSymbolExtensions.vb | 11 + .../VisualBasic/Portable/VBResources.resx | 6 + .../Portable/xlf/VBResources.cs.xlf | 10 + .../Portable/xlf/VBResources.de.xlf | 10 + .../Portable/xlf/VBResources.es.xlf | 10 + .../Portable/xlf/VBResources.fr.xlf | 10 + .../Portable/xlf/VBResources.it.xlf | 10 + .../Portable/xlf/VBResources.ja.xlf | 10 + .../Portable/xlf/VBResources.ko.xlf | 10 + .../Portable/xlf/VBResources.pl.xlf | 10 + .../Portable/xlf/VBResources.pt-BR.xlf | 10 + .../Portable/xlf/VBResources.ru.xlf | 10 + .../Portable/xlf/VBResources.tr.xlf | 10 + .../Portable/xlf/VBResources.zh-Hans.xlf | 10 + .../Portable/xlf/VBResources.zh-Hant.xlf | 10 + .../Test/CommandLine/CommandLineTests.vb | 11 +- .../Test/Emit/CodeGen/CodeGenTuples.vb | 4 +- .../Compilation/CompilationAPITests.vb | 6 +- .../Compilation/SemanticModelAPITests.vb | 10 +- .../Semantic/Semantics/OverloadResolution.vb | 2 +- .../SymbolsTests/GenericConstraintTests.vb | 12 +- .../SymbolsTests/InstantiatingGenerics.vb | 2 +- .../Metadata/PE/LoadCustomModifiers.vb | 82 +- .../Symbol/SymbolsTests/SymbolErrorTests.vb | 2 +- .../UnmanagedTypeConstraintTests.vb | 1887 +++++++++++++++++ .../Symbols/EETypeParameterSymbol.vb | 6 + .../Symbols/SimpleTypeParameterSymbol.vb | 6 + 79 files changed, 2747 insertions(+), 180 deletions(-) create mode 100644 src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/UnmanagedTypeConstraintTests.vb diff --git a/src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs b/src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs index 8583f3cb714b9..7d5e29491e665 100644 --- a/src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs +++ b/src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs @@ -8,6 +8,7 @@ using System.Diagnostics; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Symbols; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Symbols @@ -122,7 +123,9 @@ private static void StructDependsClosure(NamedTypeSymbol type, HashSet p /// internal static ManagedKind GetManagedKind(NamedTypeSymbol type, ref CompoundUseSiteInfo useSiteInfo) { - var (isManaged, hasGenerics) = IsManagedTypeHelper(type); + // The code below should be kept in sync with NamedTypeSymbol.GetManagedKind in VB + + var (isManaged, hasGenerics) = INamedTypeSymbolInternal.Helpers.IsManagedTypeHelper(type); var definitelyManaged = isManaged == ThreeState.True; if (isManaged == ThreeState.Unknown) { @@ -201,7 +204,7 @@ internal static ManagedKind GetManagedKind(NamedTypeSymbol type, ref CompoundUse } else { - var result = IsManagedTypeHelper(fieldNamedType); + var result = INamedTypeSymbolInternal.Helpers.IsManagedTypeHelper(fieldNamedType); hasGenerics = hasGenerics || result.hasGenerics; // NOTE: don't use ManagedKind.get on a NamedTypeSymbol - that could lead // to infinite recursion. @@ -235,60 +238,5 @@ internal static ManagedKind GetManagedKind(NamedTypeSymbol type, ref CompoundUse internal static TypeSymbol NonPointerType(this FieldSymbol field) => field.HasPointerType ? null : field.Type; - - /// - /// Returns True or False if we can determine whether the type is managed - /// without looking at its fields and Unknown otherwise. - /// Also returns whether or not the given type is generic. - /// - private static (ThreeState isManaged, bool hasGenerics) IsManagedTypeHelper(NamedTypeSymbol type) - { - // To match dev10, we treat enums as their underlying types. - if (type.IsEnumType()) - { - type = type.GetEnumUnderlyingType(); - } - - // Short-circuit common cases. - switch (type.SpecialType) - { - case SpecialType.System_Void: - case SpecialType.System_Boolean: - case SpecialType.System_Char: - case SpecialType.System_SByte: - case SpecialType.System_Byte: - case SpecialType.System_Int16: - case SpecialType.System_UInt16: - case SpecialType.System_Int32: - case SpecialType.System_UInt32: - case SpecialType.System_Int64: - case SpecialType.System_UInt64: - case SpecialType.System_Decimal: - case SpecialType.System_Single: - case SpecialType.System_Double: - case SpecialType.System_IntPtr: - case SpecialType.System_UIntPtr: - case SpecialType.System_ArgIterator: - case SpecialType.System_RuntimeArgumentHandle: - return (ThreeState.False, false); - case SpecialType.System_TypedReference: - return (ThreeState.True, false); - case SpecialType.None: - default: - // CONSIDER: could provide cases for other common special types. - break; // Proceed with additional checks. - } - - bool hasGenerics = type.IsGenericType; - switch (type.TypeKind) - { - case TypeKind.Enum: - return (ThreeState.False, hasGenerics); - case TypeKind.Struct: - return (ThreeState.Unknown, hasGenerics); - default: - return (ThreeState.True, hasGenerics); - } - } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs index a584624035ce3..ae6accbadf890 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs @@ -230,7 +230,7 @@ private ImmutableArray GetDeclaredConstraintTypes(ConsList< } // - presence of unmanaged pattern has to be matched with `valuetype` - // - IsUnmanagedAttribute is allowed iff there is an unmanaged pattern + // - IsUnmanagedAttribute is allowed if there is an unmanaged pattern if (hasUnmanagedModreqPattern && (_flags & GenericParameterAttributes.NotNullableValueTypeConstraint) == 0 || hasUnmanagedModreqPattern != peModule.HasIsUnmanagedAttribute(_handle)) { diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/UnmanagedTypeModifierTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/UnmanagedTypeModifierTests.cs index 3436b9a10cb13..dfc1fbea048ae 100644 --- a/src/Compilers/CSharp/Test/Emit2/Emit/UnmanagedTypeModifierTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Emit/UnmanagedTypeModifierTests.cs @@ -282,6 +282,70 @@ public static void Main() ); } + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/75681")] + public void LoadingUnmanagedTypeModifier_ModreqGeneric() + { + var ilSource = IsUnmanagedAttributeIL + @" +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M1() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M1 + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor +} + +.class public auto ansi beforefieldinit System.Runtime.InteropServices.UnmanagedType`1 + extends [mscorlib]System.Object +{ + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } +} +"; + + var reference = CompileIL(ilSource, prependDefaultHeader: false); + + var code = @" +public class Test +{ + public static void Main() + { + var obj = new TestRef(); + + obj.M1(); + } +}"; + + CreateCompilation(code, references: new[] { reference }).VerifyDiagnostics( + // An error is expected here, see https://github.com/dotnet/roslyn/issues/75681 + ); + } + [Fact] public void LoadingUnmanagedTypeModifier_AttributeWithoutModreq() { diff --git a/src/Compilers/Core/Portable/Binding/UseSiteInfo.cs b/src/Compilers/Core/Portable/Binding/UseSiteInfo.cs index 5b4d4e0667415..23ff99760ba72 100644 --- a/src/Compilers/Core/Portable/Binding/UseSiteInfo.cs +++ b/src/Compilers/Core/Portable/Binding/UseSiteInfo.cs @@ -270,14 +270,21 @@ public void AddDiagnostics(ICollection? diagnostics) foreach (var diagnosticInfo in diagnostics) { - if (_diagnostics.Add(diagnosticInfo) && diagnosticInfo?.Severity == DiagnosticSeverity.Error) - { - RecordPresenceOfAnError(); - } + AccumulateDiagnosticInfoAndRecordPresenceOfAnError(diagnosticInfo); } } } + private void AccumulateDiagnosticInfoAndRecordPresenceOfAnError(DiagnosticInfo diagnosticInfo) + { + Debug.Assert(_diagnostics is not null); + + if (_diagnostics.Add(diagnosticInfo) && diagnosticInfo?.Severity == DiagnosticSeverity.Error) + { + RecordPresenceOfAnError(); + } + } + public void AddDiagnostics(IReadOnlyCollection? diagnostics) { if (!AccumulatesDiagnostics) @@ -291,10 +298,7 @@ public void AddDiagnostics(IReadOnlyCollection? diagnostics) foreach (var diagnosticInfo in diagnostics) { - if (_diagnostics.Add(diagnosticInfo) && diagnosticInfo?.Severity == DiagnosticSeverity.Error) - { - RecordPresenceOfAnError(); - } + AccumulateDiagnosticInfoAndRecordPresenceOfAnError(diagnosticInfo); } } } @@ -312,14 +316,23 @@ public void AddDiagnostics(ImmutableArray diagnostics) foreach (var diagnosticInfo in diagnostics) { - if (_diagnostics.Add(diagnosticInfo) && diagnosticInfo?.Severity == DiagnosticSeverity.Error) - { - RecordPresenceOfAnError(); - } + AccumulateDiagnosticInfoAndRecordPresenceOfAnError(diagnosticInfo); } } } + public void AddDiagnosticInfo(DiagnosticInfo diagnosticInfo) + { + if (!AccumulatesDiagnostics) + { + return; + } + + _diagnostics ??= new HashSet(); + + AccumulateDiagnosticInfoAndRecordPresenceOfAnError(diagnosticInfo); + } + public void AddDependencies(UseSiteInfo info) { if (!_hasErrors && AccumulatesDependencies) diff --git a/src/Compilers/Core/Portable/Symbols/INamedTypeSymbolInternal.cs b/src/Compilers/Core/Portable/Symbols/INamedTypeSymbolInternal.cs index 9bbab4d281ebf..31e9f10ee96d6 100644 --- a/src/Compilers/Core/Portable/Symbols/INamedTypeSymbolInternal.cs +++ b/src/Compilers/Core/Portable/Symbols/INamedTypeSymbolInternal.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; +using System.Diagnostics; namespace Microsoft.CodeAnalysis.Symbols { @@ -16,5 +17,69 @@ internal interface INamedTypeSymbolInternal : ITypeSymbolInternal ImmutableArray GetMembers(); ImmutableArray GetMembers(string name); + + /// + /// True if this type or some containing type has type parameters. + /// + bool IsGenericType { get; } + + internal static class Helpers + { + /// + /// Returns True or False if we can determine whether the type is managed + /// without looking at its fields and Unknown otherwise. + /// Also returns whether or not the given type is generic. + /// + public static (ThreeState isManaged, bool hasGenerics) IsManagedTypeHelper(INamedTypeSymbolInternal type) + { + // To match dev10, we treat enums as their underlying types. + if (type.TypeKind == TypeKind.Enum) + { + Debug.Assert(type.EnumUnderlyingType is not null); + type = type.EnumUnderlyingType; + } + + // Short-circuit common cases. + switch (type.SpecialType) + { + case SpecialType.System_Void: + case SpecialType.System_Boolean: + case SpecialType.System_Char: + case SpecialType.System_SByte: + case SpecialType.System_Byte: + case SpecialType.System_Int16: + case SpecialType.System_UInt16: + case SpecialType.System_Int32: + case SpecialType.System_UInt32: + case SpecialType.System_Int64: + case SpecialType.System_UInt64: + case SpecialType.System_Decimal: + case SpecialType.System_Single: + case SpecialType.System_Double: + case SpecialType.System_IntPtr: + case SpecialType.System_UIntPtr: + case SpecialType.System_ArgIterator: + case SpecialType.System_RuntimeArgumentHandle: + return (ThreeState.False, false); + case SpecialType.System_TypedReference: + return (ThreeState.True, false); + case SpecialType.None: + default: + // CONSIDER: could provide cases for other common special types. + break; // Proceed with additional checks. + } + + bool hasGenerics = type.IsGenericType; + switch (type.TypeKind) + { + case TypeKind.Enum: + return (ThreeState.False, hasGenerics); + case TypeKind.Struct: + return (ThreeState.Unknown, hasGenerics); + default: + return (ThreeState.True, hasGenerics); + } + } + } } } diff --git a/src/Compilers/Core/RebuildTest/BasicDeterministicKeyBuilderTests.cs b/src/Compilers/Core/RebuildTest/BasicDeterministicKeyBuilderTests.cs index 24e0c4f6122c4..f6977a5e218fd 100644 --- a/src/Compilers/Core/RebuildTest/BasicDeterministicKeyBuilderTests.cs +++ b/src/Compilers/Core/RebuildTest/BasicDeterministicKeyBuilderTests.cs @@ -220,7 +220,7 @@ public void BasicPathMapWindows(string filePath, string workingDirectory, string ""specifiedLanguageVersion"": ""VisualBasic15"", ""preprocessorSymbols"": { ""TARGET"": ""exe"", - ""VBC_VER"": ""16.9"" + ""VBC_VER"": ""17.13"" } } } @@ -360,7 +360,7 @@ public void FeatureFlag() ""specifiedLanguageVersion"": ""Default"", ""preprocessorSymbols"": {{ ""TARGET"": ""library"", - ""VBC_VER"": ""16.9"", + ""VBC_VER"": ""17.13"", ""_MYTYPE"": ""Empty"" }} }} @@ -385,7 +385,7 @@ public void FeatureFlag() ""specifiedLanguageVersion"": ""Default"", ""preprocessorSymbols"": {{ ""TARGET"": ""library"", - ""VBC_VER"": ""16.9"", + ""VBC_VER"": ""17.13"", ""_MYTYPE"": ""Empty"" }} }} diff --git a/src/Compilers/Test/Utilities/VisualBasic/BasicTestSource.vb b/src/Compilers/Test/Utilities/VisualBasic/BasicTestSource.vb index dd88a392d4954..6fd897b5505f2 100644 --- a/src/Compilers/Test/Utilities/VisualBasic/BasicTestSource.vb +++ b/src/Compilers/Test/Utilities/VisualBasic/BasicTestSource.vb @@ -43,12 +43,17 @@ Public Structure BasicTestSource Dim source = TryCast(Value, String) If source IsNot Nothing Then - Return New SyntaxTree() {VisualBasicSyntaxTree.ParseText(SourceText.From(source, encoding:=Nothing, SourceHashAlgorithms.Default), parseOptions)} + Return New SyntaxTree() _ + { + VisualBasicSyntaxTree.ParseText( + SourceText.From(source, encoding:=Nothing, SourceHashAlgorithms.Default), + If(parseOptions, TestOptions.RegularLatest)) + } End If Dim sources = TryCast(Value, String()) If sources IsNot Nothing Then - Return sources.Select(Function(s) VisualBasicSyntaxTree.ParseText(SourceText.From(s, encoding:=Nothing, SourceHashAlgorithms.Default), parseOptions)).ToArray() + Return sources.Select(Function(s) VisualBasicSyntaxTree.ParseText(SourceText.From(s, encoding:=Nothing, SourceHashAlgorithms.Default), If(parseOptions, TestOptions.RegularLatest))).ToArray() End If Dim tree = TryCast(Value, SyntaxTree) diff --git a/src/Compilers/Test/Utilities/VisualBasic/CompilationTestUtils.vb b/src/Compilers/Test/Utilities/VisualBasic/CompilationTestUtils.vb index 92534ab7a301f..93f4e83be9b84 100644 --- a/src/Compilers/Test/Utilities/VisualBasic/CompilationTestUtils.vb +++ b/src/Compilers/Test/Utilities/VisualBasic/CompilationTestUtils.vb @@ -651,7 +651,7 @@ Friend Module CompilationUtils MarkupTestFile.GetSpans(codeWithMarker, codeWithoutMarker, spans) Dim text = SourceText.From(codeWithoutMarker, Encoding.UTF8) - Return (VisualBasicSyntaxTree.ParseText(text, parseOptions, If(programElement.@name, "")), spans) + Return (VisualBasicSyntaxTree.ParseText(text, If(parseOptions, TestOptions.RegularLatest), If(programElement.@name, "")), spans) End Function ' Find a node inside a tree. diff --git a/src/Compilers/Test/Utilities/VisualBasic/Extensions.vb b/src/Compilers/Test/Utilities/VisualBasic/Extensions.vb index 74017f93d7cc8..f8fb2e210426a 100644 --- a/src/Compilers/Test/Utilities/VisualBasic/Extensions.vb +++ b/src/Compilers/Test/Utilities/VisualBasic/Extensions.vb @@ -344,12 +344,12 @@ Friend Module Extensions Friend Function ReduceExtensionMethod(this As MethodSymbol, instanceType As TypeSymbol) As MethodSymbol - Return this.ReduceExtensionMethod(instanceType, CompoundUseSiteInfo(Of AssemblySymbol).Discarded) + Return this.ReduceExtensionMethod(instanceType, CompoundUseSiteInfo(Of AssemblySymbol).Discarded, LanguageVersion.Latest) End Function Friend Function ReduceExtensionMethod(this As MethodSymbol, instanceType As TypeSymbol, proximity As Integer) As MethodSymbol - Return this.ReduceExtensionMethod(instanceType, proximity, CompoundUseSiteInfo(Of AssemblySymbol).Discarded) + Return this.ReduceExtensionMethod(instanceType, proximity, CompoundUseSiteInfo(Of AssemblySymbol).Discarded, LanguageVersion.Latest) End Function diff --git a/src/Compilers/Test/Utilities/VisualBasic/ParserTestUtilities.vb b/src/Compilers/Test/Utilities/VisualBasic/ParserTestUtilities.vb index 005330a70f15c..1858d6987c0d7 100644 --- a/src/Compilers/Test/Utilities/VisualBasic/ParserTestUtilities.vb +++ b/src/Compilers/Test/Utilities/VisualBasic/ParserTestUtilities.vb @@ -110,7 +110,7 @@ Friend Module ParserTestUtilities encoding = Encoding.UTF8 End If - Dim tree = VisualBasicSyntaxTree.ParseText(SourceText.From(source, encoding), options:=If(options, VisualBasicParseOptions.Default), path:=fileName) + Dim tree = VisualBasicSyntaxTree.ParseText(SourceText.From(source, encoding), options:=If(options, TestOptions.RegularLatest), path:=fileName) Dim root = tree.GetRoot() ' Verify FullText Assert.Equal(source, root.ToFullString) diff --git a/src/Compilers/Test/Utilities/VisualBasic/TestOptions.vb b/src/Compilers/Test/Utilities/VisualBasic/TestOptions.vb index 84c370aa213f7..875f9fd5029a2 100644 --- a/src/Compilers/Test/Utilities/VisualBasic/TestOptions.vb +++ b/src/Compilers/Test/Utilities/VisualBasic/TestOptions.vb @@ -14,6 +14,7 @@ Public Class TestOptions Public Shared ReadOnly Regular15_5 As VisualBasicParseOptions = Regular.WithLanguageVersion(LanguageVersion.VisualBasic15_5) Public Shared ReadOnly Regular16 As VisualBasicParseOptions = Regular.WithLanguageVersion(LanguageVersion.VisualBasic16) Public Shared ReadOnly Regular16_9 As VisualBasicParseOptions = Regular.WithLanguageVersion(LanguageVersion.VisualBasic16_9) + Public Shared ReadOnly Regular17_13 As VisualBasicParseOptions = Regular.WithLanguageVersion(LanguageVersion.VisualBasic17_13) Public Shared ReadOnly RegularLatest As VisualBasicParseOptions = Regular.WithLanguageVersion(LanguageVersion.Latest) Public Shared ReadOnly RegularWithLegacyStrongName As VisualBasicParseOptions = Regular.WithFeature("UseLegacyStrongNameProvider") diff --git a/src/Compilers/VisualBasic/Portable/Binding/Binder.vb b/src/Compilers/VisualBasic/Portable/Binding/Binder.vb index f768e7f1ee1b8..a5d5bc1becfb0 100644 --- a/src/Compilers/VisualBasic/Portable/Binding/Binder.vb +++ b/src/Compilers/VisualBasic/Portable/Binding/Binder.vb @@ -1029,6 +1029,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Select Case Me.BindingLocation Case BindingLocation.BaseTypes, BindingLocation.MethodSignature, + BindingLocation.FieldType, + BindingLocation.PropertyType, + BindingLocation.EventType, BindingLocation.GenericConstraintsClause, BindingLocation.ProjectImportsDeclaration, BindingLocation.SourceFileImportsDeclaration diff --git a/src/Compilers/VisualBasic/Portable/Binding/Binder_Invocation.vb b/src/Compilers/VisualBasic/Portable/Binding/Binder_Invocation.vb index 4b6fc722871a4..a1aa76da2ac0e 100644 --- a/src/Compilers/VisualBasic/Portable/Binding/Binder_Invocation.vb +++ b/src/Compilers/VisualBasic/Portable/Binding/Binder_Invocation.vb @@ -2274,7 +2274,7 @@ ProduceBoundNode: Dim method = DirectCast(candidate.UnderlyingSymbol, MethodSymbol) ' TODO: Dev10 uses the location of the type parameter or argument that ' violated the constraint, rather than the entire invocation expression. - Dim succeeded = method.CheckConstraints(diagnosticLocation, diagnostics, template:=GetNewCompoundUseSiteInfo(diagnostics)) + Dim succeeded = method.CheckConstraints(Compilation.LanguageVersion, diagnosticLocation, diagnostics, template:=GetNewCompoundUseSiteInfo(diagnostics)) Debug.Assert(Not succeeded) Return End If diff --git a/src/Compilers/VisualBasic/Portable/Binding/Binder_Lookup.vb b/src/Compilers/VisualBasic/Portable/Binding/Binder_Lookup.vb index 366b4ae2b1da4..cbeb2eb0d71ea 100644 --- a/src/Compilers/VisualBasic/Portable/Binding/Binder_Lookup.vb +++ b/src/Compilers/VisualBasic/Portable/Binding/Binder_Lookup.vb @@ -1217,7 +1217,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ' Process all methods from the same type together. Do ' Try to reduce this method and merge with the current result - Dim reduced As MethodSymbol = methods(i).ReduceExtensionMethod(container, proximity, useSiteInfo) + Dim reduced As MethodSymbol = methods(i).ReduceExtensionMethod(container, proximity, useSiteInfo, binder.Compilation.LanguageVersion) If reduced IsNot Nothing Then lookupResult.MergeOverloadedOrPrioritizedExtensionMethods(binder.CheckViability(reduced, arity, options, reduced.ContainingType, useSiteInfo)) diff --git a/src/Compilers/VisualBasic/Portable/Binding/Binder_Symbols.vb b/src/Compilers/VisualBasic/Portable/Binding/Binder_Symbols.vb index f6da9ae8a394e..b3e96eea9d9d0 100644 --- a/src/Compilers/VisualBasic/Portable/Binding/Binder_Symbols.vb +++ b/src/Compilers/VisualBasic/Portable/Binding/Binder_Symbols.vb @@ -175,7 +175,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ' Interface IA(Of T As C) : End Interface ' Interface IB(Of T As C) : Inherits IA(Of T) : End Interface If checkConstraints AndAlso ShouldCheckConstraints Then - constructedType.CheckConstraintsForNonTuple(syntaxArguments, diagnostics, template:=GetNewCompoundUseSiteInfo(diagnostics)) + constructedType.CheckConstraintsForNonTuple(Compilation.LanguageVersion, syntaxArguments, diagnostics, template:=GetNewCompoundUseSiteInfo(diagnostics)) End If constructedType = DirectCast(TupleTypeSymbol.TransformToTupleIfCompatible(constructedType), NamedTypeSymbol) diff --git a/src/Compilers/VisualBasic/Portable/Binding/Binder_Utils.vb b/src/Compilers/VisualBasic/Portable/Binding/Binder_Utils.vb index f674f21110d33..12e56274d263c 100644 --- a/src/Compilers/VisualBasic/Portable/Binding/Binder_Utils.vb +++ b/src/Compilers/VisualBasic/Portable/Binding/Binder_Utils.vb @@ -301,7 +301,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic If ShouldCheckConstraints Then Dim diagnosticsBuilder = ArrayBuilder(Of TypeParameterDiagnosticInfo).GetInstance() Dim useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) = Nothing - constructedType.CheckConstraints(diagnosticsBuilder, useSiteDiagnosticsBuilder, template:=GetNewCompoundUseSiteInfo(diagBag)) + constructedType.CheckConstraints(Compilation.LanguageVersion, diagnosticsBuilder, useSiteDiagnosticsBuilder, template:=GetNewCompoundUseSiteInfo(diagBag)) If useSiteDiagnosticsBuilder IsNot Nothing Then diagnosticsBuilder.AddRange(useSiteDiagnosticsBuilder) diff --git a/src/Compilers/VisualBasic/Portable/Binding/BindingLocation.vb b/src/Compilers/VisualBasic/Portable/Binding/BindingLocation.vb index 64627343e1664..ee21c0c6e4394 100644 --- a/src/Compilers/VisualBasic/Portable/Binding/BindingLocation.vb +++ b/src/Compilers/VisualBasic/Portable/Binding/BindingLocation.vb @@ -21,8 +21,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic EventSignature FieldType HandlesClause + PropertyType PropertySignature PropertyAccessorSignature + EventType EventAccessorSignature End Enum diff --git a/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/VisualBasicSymbolMatcher.vb b/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/VisualBasicSymbolMatcher.vb index f14fa7854ca66..db9448ac0f6a1 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/VisualBasicSymbolMatcher.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/VisualBasicSymbolMatcher.vb @@ -478,6 +478,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit Debug.Assert(type.AllowsRefLikeType = other.AllowsRefLikeType) Debug.Assert(type.HasReferenceTypeConstraint = other.HasReferenceTypeConstraint) Debug.Assert(type.ConstraintTypesNoUseSiteDiagnostics.Length = other.ConstraintTypesNoUseSiteDiagnostics.Length) + Debug.Assert(type.HasUnmanagedTypeConstraint = other.HasUnmanagedTypeConstraint) Return True End Function diff --git a/src/Compilers/VisualBasic/Portable/Errors/ErrorFacts.vb b/src/Compilers/VisualBasic/Portable/Errors/ErrorFacts.vb index 90324908c2390..48fe778a53989 100644 --- a/src/Compilers/VisualBasic/Portable/Errors/ErrorFacts.vb +++ b/src/Compilers/VisualBasic/Portable/Errors/ErrorFacts.vb @@ -1366,6 +1366,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ERRID.ERR_DoNotUseRequiredMember, ERRID.ERR_UnsupportedRefReturningCallInWithStatement, ERRID.ERR_TypeReserved, + ERRID.ERR_UnmanagedConstraintNotSatisfied, ERRID.ERR_NextAvailable, ERRID.WRN_UseOfObsoleteSymbol2, ERRID.WRN_InvalidOverrideDueToTupleNames2, diff --git a/src/Compilers/VisualBasic/Portable/Errors/Errors.vb b/src/Compilers/VisualBasic/Portable/Errors/Errors.vb index 337adb8b5246a..7a1f4efecbec3 100644 --- a/src/Compilers/VisualBasic/Portable/Errors/Errors.vb +++ b/src/Compilers/VisualBasic/Portable/Errors/Errors.vb @@ -1781,8 +1781,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ERR_LockTypeUnsupported = 37329 ERR_InvalidVersionFormatDeterministic = 37330 ERR_TypeReserved = 37331 + ERR_UnmanagedConstraintNotSatisfied = 37332 - ERR_NextAvailable = 37332 + ERR_NextAvailable = 37333 '// WARNINGS BEGIN HERE WRN_UseOfObsoleteSymbol2 = 40000 @@ -2081,5 +2082,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic FEATURE_CommentsAfterLineContinuation FEATURE_InitOnlySettersUsage FEATURE_CallerArgumentExpression + FEATURE_UnmanagedConstraint End Enum End Namespace diff --git a/src/Compilers/VisualBasic/Portable/LanguageVersion.vb b/src/Compilers/VisualBasic/Portable/LanguageVersion.vb index 2c416182c3ac8..1a6aac70c9f81 100644 --- a/src/Compilers/VisualBasic/Portable/LanguageVersion.vb +++ b/src/Compilers/VisualBasic/Portable/LanguageVersion.vb @@ -21,6 +21,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic VisualBasic15_5 = 1505 VisualBasic16 = 1600 VisualBasic16_9 = 1609 + VisualBasic17_13 = 1713 Latest = Integer.MaxValue End Enum @@ -39,7 +40,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic LanguageVersion.VisualBasic15_3, LanguageVersion.VisualBasic15_5, LanguageVersion.VisualBasic16, - LanguageVersion.VisualBasic16_9 + LanguageVersion.VisualBasic16_9, + LanguageVersion.VisualBasic17_13 Return True End Select @@ -71,6 +73,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return "16" Case LanguageVersion.VisualBasic16_9 Return "16.9" + Case LanguageVersion.VisualBasic17_13 + Return "17.13" Case Else Throw ExceptionUtilities.UnexpectedValue(value) End Select @@ -87,7 +91,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Public Function MapSpecifiedToEffectiveVersion(version As LanguageVersion) As LanguageVersion Select Case version Case LanguageVersion.Latest - Return LanguageVersion.VisualBasic16_9 + Return LanguageVersion.VisualBasic17_13 Case LanguageVersion.Default Return LanguageVersion.VisualBasic16_9 Case Else @@ -97,7 +101,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Friend ReadOnly Property CurrentVersion As LanguageVersion Get - Return LanguageVersion.VisualBasic16_9 + Return LanguageVersion.VisualBasic17_13 End Get End Property @@ -128,6 +132,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return "16" Case LanguageVersion.VisualBasic16_9 Return "16.9" + Case LanguageVersion.VisualBasic17_13 + Return "17.13" Case LanguageVersion.Default Return "default" Case LanguageVersion.Latest @@ -167,6 +173,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic result = LanguageVersion.VisualBasic16 Case "16.9" result = LanguageVersion.VisualBasic16_9 + Case "17.13" + result = LanguageVersion.VisualBasic17_13 Case "default" result = LanguageVersion.Default Case "latest" diff --git a/src/Compilers/VisualBasic/Portable/Parser/Parser.vb b/src/Compilers/VisualBasic/Portable/Parser/Parser.vb index b9423962bd48f..2b7dc24d71d1f 100644 --- a/src/Compilers/VisualBasic/Portable/Parser/Parser.vb +++ b/src/Compilers/VisualBasic/Portable/Parser/Parser.vb @@ -6131,9 +6131,7 @@ checkNullable: Friend Shared Function CheckFeatureAvailability(diagnosticsOpt As DiagnosticBag, location As Location, languageVersion As LanguageVersion, feature As Feature) As Boolean If Not CheckFeatureAvailability(languageVersion, feature) Then If diagnosticsOpt IsNot Nothing Then - Dim featureName = ErrorFactory.ErrorInfo(feature.GetResourceId()) - Dim requiredVersion = New VisualBasicRequiredLanguageVersion(feature.GetLanguageVersion()) - diagnosticsOpt.Add(ERRID.ERR_LanguageVersion, location, languageVersion.GetErrorName(), featureName, requiredVersion) + diagnosticsOpt.Add(GetFeatureAvailabilityError(feature, languageVersion), location) End If Return False @@ -6141,6 +6139,14 @@ checkNullable: Return True End Function + Friend Shared Function GetFeatureAvailabilityError(feature As Feature, languageVersion As LanguageVersion) As DiagnosticInfo + Return ErrorFactory.ErrorInfo( + ERRID.ERR_LanguageVersion, + languageVersion.GetErrorName(), + ErrorFactory.ErrorInfo(feature.GetResourceId()), + New VisualBasicRequiredLanguageVersion(feature.GetLanguageVersion())) + End Function + Friend Shared Function CheckFeatureAvailability(diagnostics As BindingDiagnosticBag, location As Location, languageVersion As LanguageVersion, feature As Feature) As Boolean Return CheckFeatureAvailability(diagnostics.DiagnosticBag, location, languageVersion, feature) End Function diff --git a/src/Compilers/VisualBasic/Portable/Parser/ParserFeature.vb b/src/Compilers/VisualBasic/Portable/Parser/ParserFeature.vb index 71a47e3f057fd..9235e57dd1710 100644 --- a/src/Compilers/VisualBasic/Portable/Parser/ParserFeature.vb +++ b/src/Compilers/VisualBasic/Portable/Parser/ParserFeature.vb @@ -41,6 +41,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax UnconstrainedTypeParameterInConditional CommentsAfterLineContinuation InitOnlySettersUsage + UnmanagedConstraint End Enum Friend Module FeatureExtensions @@ -105,6 +106,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Case Feature.InitOnlySettersUsage Return LanguageVersion.VisualBasic16_9 + + Case Feature.UnmanagedConstraint + Return LanguageVersion.VisualBasic17_13 + Case Else Throw ExceptionUtilities.UnexpectedValue(feature) End Select @@ -178,6 +183,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Return ERRID.FEATURE_CommentsAfterLineContinuation Case Feature.InitOnlySettersUsage Return ERRID.FEATURE_InitOnlySettersUsage + Case Feature.UnmanagedConstraint + Return ERRID.FEATURE_UnmanagedConstraint Case Else Throw ExceptionUtilities.UnexpectedValue(feature) End Select diff --git a/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt b/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt index 8b137891791fe..360164d8fcc64 100644 --- a/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt @@ -1 +1 @@ - +Microsoft.CodeAnalysis.VisualBasic.LanguageVersion.VisualBasic17_13 = 1713 -> Microsoft.CodeAnalysis.VisualBasic.LanguageVersion diff --git a/src/Compilers/VisualBasic/Portable/Semantics/Conversions.vb b/src/Compilers/VisualBasic/Portable/Semantics/Conversions.vb index 7dd98a025230b..568685e8b4627 100644 --- a/src/Compilers/VisualBasic/Portable/Semantics/Conversions.vb +++ b/src/Compilers/VisualBasic/Portable/Semantics/Conversions.vb @@ -2032,6 +2032,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic While candidate IsNot Nothing If ConstraintsHelper.CheckConstraints(constructedSymbol:=Nothing, + LanguageVersion.Latest, ' Classifying conversions from/to type parameters. This is meaningful only when they and their constraints are declared in source substitution:=Nothing, typeParameter:=typeParam, typeArgument:=candidate, diff --git a/src/Compilers/VisualBasic/Portable/Semantics/OverloadResolution.vb b/src/Compilers/VisualBasic/Portable/Semantics/OverloadResolution.vb index ebc523567a7c2..6d69e5c4ce84c 100644 --- a/src/Compilers/VisualBasic/Portable/Semantics/OverloadResolution.vb +++ b/src/Compilers/VisualBasic/Portable/Semantics/OverloadResolution.vb @@ -2895,7 +2895,7 @@ Bailout: If method.IsGenericMethod Then Dim diagnosticsBuilder = ArrayBuilder(Of TypeParameterDiagnosticInfo).GetInstance() Dim useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) = Nothing - Dim satisfiedConstraints = method.CheckConstraints(diagnosticsBuilder, useSiteDiagnosticsBuilder, template:=useSiteInfo) + Dim satisfiedConstraints = method.CheckConstraints(binder.Compilation.LanguageVersion, diagnosticsBuilder, useSiteDiagnosticsBuilder, template:=useSiteInfo) diagnosticsBuilder.Free() If useSiteDiagnosticsBuilder IsNot Nothing AndAlso useSiteDiagnosticsBuilder.Count > 0 Then diff --git a/src/Compilers/VisualBasic/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousTypeOrDelegateTypeParameterSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousTypeOrDelegateTypeParameterSymbol.vb index f8571dbac4861..9d48c9b4c2517 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousTypeOrDelegateTypeParameterSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousTypeOrDelegateTypeParameterSymbol.vb @@ -42,6 +42,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Friend Overrides ReadOnly Property HasUnmanagedTypeConstraint As Boolean + Get + Return False + End Get + End Property + Public Overrides ReadOnly Property Locations As ImmutableArray(Of Location) Get Return ImmutableArray(Of Location).Empty diff --git a/src/Compilers/VisualBasic/Portable/Symbols/ArrayTypeSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/ArrayTypeSymbol.vb index baffd44f6e146..e209b6032c0a8 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/ArrayTypeSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/ArrayTypeSymbol.vb @@ -364,6 +364,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Friend MustOverride Function WithElementType(elementType As TypeSymbol) As ArrayTypeSymbol + Friend Overrides Function GetManagedKind(ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ManagedKind + Return ManagedKind.Managed + End Function + #Region "Use-Site Diagnostics" Friend Overrides Function GetUseSiteInfo() As UseSiteInfo(Of AssemblySymbol) diff --git a/src/Compilers/VisualBasic/Portable/Symbols/ConstraintsHelper.vb b/src/Compilers/VisualBasic/Portable/Symbols/ConstraintsHelper.vb index d7a8f078fc96a..129ee7d783771 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/ConstraintsHelper.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/ConstraintsHelper.vb @@ -383,12 +383,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public Sub CheckAllConstraints( type As TypeSymbol, + languageVersion As LanguageVersion, loc As Location, diagnostics As BindingDiagnosticBag, template As CompoundUseSiteInfo(Of AssemblySymbol)) Dim diagnosticsBuilder = ArrayBuilder(Of TypeParameterDiagnosticInfo).GetInstance() Dim useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) = Nothing - type.CheckAllConstraints(diagnosticsBuilder, useSiteDiagnosticsBuilder, template) + type.CheckAllConstraints(languageVersion, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) If useSiteDiagnosticsBuilder IsNot Nothing Then diagnosticsBuilder.AddRange(useSiteDiagnosticsBuilder) @@ -409,6 +410,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public Sub CheckAllConstraints( type As TypeSymbol, + languageVersion As LanguageVersion, diagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo), <[In], Out> ByRef useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo), template As CompoundUseSiteInfo(Of AssemblySymbol)) @@ -416,6 +418,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols diagnostics.diagnosticsBuilder = diagnosticsBuilder diagnostics.useSiteDiagnosticsBuilder = useSiteDiagnosticsBuilder diagnostics.template = template + diagnostics.languageVersion = languageVersion type.VisitType(s_checkConstraintsSingleTypeFunc, diagnostics) @@ -426,13 +429,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public diagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) Public useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) Public template As CompoundUseSiteInfo(Of AssemblySymbol) + Public languageVersion As LanguageVersion End Class Private ReadOnly s_checkConstraintsSingleTypeFunc As Func(Of TypeSymbol, CheckConstraintsDiagnosticsBuilders, Boolean) = AddressOf CheckConstraintsSingleType Private Function CheckConstraintsSingleType(type As TypeSymbol, diagnostics As CheckConstraintsDiagnosticsBuilders) As Boolean If type.Kind = SymbolKind.NamedType Then - DirectCast(type, NamedTypeSymbol).CheckConstraints(diagnostics.diagnosticsBuilder, diagnostics.useSiteDiagnosticsBuilder, diagnostics.template) + DirectCast(type, NamedTypeSymbol).CheckConstraints( + diagnostics.languageVersion, diagnostics.diagnosticsBuilder, diagnostics.useSiteDiagnosticsBuilder, diagnostics.template) End If Return False ' continue walking types End Function @@ -460,7 +465,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Dim offset As Integer = 0 For Each underlyingTuple In underlyingTupleTypeChain Dim useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) = Nothing - CheckTypeConstraints(underlyingTuple, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) + CheckTypeConstraints(underlyingTuple, + DirectCast(syntaxNode.SyntaxTree.Options, VisualBasicParseOptions).LanguageVersion, + diagnosticsBuilder, useSiteDiagnosticsBuilder, template) If useSiteDiagnosticsBuilder IsNot Nothing Then diagnosticsBuilder.AddRange(useSiteDiagnosticsBuilder) @@ -487,6 +494,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public Function CheckConstraintsForNonTuple( type As NamedTypeSymbol, + languageVersion As LanguageVersion, typeArgumentsSyntax As SeparatedSyntaxList(Of TypeSyntax), diagnostics As BindingDiagnosticBag, template As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean @@ -498,7 +506,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Dim diagnosticsBuilder = ArrayBuilder(Of TypeParameterDiagnosticInfo).GetInstance() Dim useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) = Nothing - Dim result = CheckTypeConstraints(type, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) + Dim result = CheckTypeConstraints(type, languageVersion, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) If useSiteDiagnosticsBuilder IsNot Nothing Then diagnosticsBuilder.AddRange(useSiteDiagnosticsBuilder) @@ -517,6 +525,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public Function CheckConstraints( type As NamedTypeSymbol, + languageVersion As LanguageVersion, diagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo), <[In], Out> ByRef useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo), template As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean @@ -529,12 +538,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols If Not RequiresChecking(type) Then Return True End If - Return CheckTypeConstraints(type, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) + Return CheckTypeConstraints(type, languageVersion, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) End Function Public Function CheckConstraints( method As MethodSymbol, + languageVersion As LanguageVersion, diagnosticLocation As Location, diagnostics As BindingDiagnosticBag, template As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean @@ -544,7 +554,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Dim diagnosticsBuilder = ArrayBuilder(Of TypeParameterDiagnosticInfo).GetInstance() Dim useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) = Nothing - Dim result = CheckMethodConstraints(method, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) + Dim result = CheckMethodConstraints(method, languageVersion, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) If useSiteDiagnosticsBuilder IsNot Nothing Then diagnosticsBuilder.AddRange(useSiteDiagnosticsBuilder) @@ -561,31 +571,34 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public Function CheckConstraints( method As MethodSymbol, + languageVersion As LanguageVersion, diagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo), <[In], Out> ByRef useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo), template As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean If Not RequiresChecking(method) Then Return True End If - Return CheckMethodConstraints(method, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) + Return CheckMethodConstraints(method, languageVersion, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) End Function Private Function CheckTypeConstraints( type As NamedTypeSymbol, + languageVersion As LanguageVersion, diagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo), <[In], Out> ByRef useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo), template As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean Dim substitution = type.TypeSubstitution - Return CheckConstraints(type, substitution, type.OriginalDefinition.TypeParameters, type.TypeArgumentsNoUseSiteDiagnostics, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) + Return CheckConstraints(type, languageVersion, substitution, type.OriginalDefinition.TypeParameters, type.TypeArgumentsNoUseSiteDiagnostics, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) End Function Private Function CheckMethodConstraints( method As MethodSymbol, + languageVersion As LanguageVersion, diagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo), <[In], Out> ByRef useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo), template As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean Dim substitution = DirectCast(method, SubstitutedMethodSymbol).TypeSubstitution - Return CheckConstraints(method, substitution, method.OriginalDefinition.TypeParameters, method.TypeArguments, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) + Return CheckConstraints(method, languageVersion, substitution, method.OriginalDefinition.TypeParameters, method.TypeArguments, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) End Function ''' @@ -599,6 +612,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public Function CheckConstraints( constructedSymbol As Symbol, + languageVersion As LanguageVersion, substitution As TypeSubstitution, typeParameters As ImmutableArray(Of TypeParameterSymbol), typeArguments As ImmutableArray(Of TypeSymbol), @@ -614,7 +628,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Dim typeArgument = typeArguments(i) Dim typeParameter = typeParameters(i) Dim useSiteInfo As New CompoundUseSiteInfo(Of AssemblySymbol)(template) - If Not CheckConstraints(constructedSymbol, substitution, typeParameter, typeArgument, diagnosticsBuilder, useSiteInfo) Then + If Not CheckConstraints(constructedSymbol, languageVersion, substitution, typeParameter, typeArgument, diagnosticsBuilder, useSiteInfo) Then succeeded = False End If @@ -628,6 +642,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public Function CheckConstraints( constructedSymbol As Symbol, + languageVersion As LanguageVersion, substitution As TypeSubstitution, typeParameter As TypeParameterSymbol, typeArgument As TypeSymbol, @@ -659,6 +674,24 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols succeeded = False End If + If typeParameter.HasUnmanagedTypeConstraint Then + + If Not InternalSyntax.Parser.CheckFeatureAvailability(languageVersion, InternalSyntax.Feature.UnmanagedConstraint) Then + useSiteInfo.AddDiagnosticInfo(InternalSyntax.Parser.GetFeatureAvailabilityError(InternalSyntax.Feature.UnmanagedConstraint, languageVersion)) + succeeded = False + End If + + Dim managedKind = typeArgument.GetManagedKind(useSiteInfo) + + If managedKind = ManagedKind.Managed Then + If diagnosticsBuilder IsNot Nothing Then + diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter, ErrorFactory.ErrorInfo(ERRID.ERR_UnmanagedConstraintNotSatisfied, typeArgument, typeParameter))) + End If + + succeeded = False + End If + End If + If typeParameter.HasValueTypeConstraint AndAlso Not SatisfiesValueTypeConstraint(constructedSymbol, typeParameter, typeArgument, diagnosticsBuilder, useSiteInfo) Then succeeded = False End If diff --git a/src/Compilers/VisualBasic/Portable/Symbols/IndexedTypeParameterSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/IndexedTypeParameterSymbol.vb index dca460d7f317d..d327dc3b1a378 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/IndexedTypeParameterSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/IndexedTypeParameterSymbol.vb @@ -140,6 +140,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Friend Overrides ReadOnly Property HasUnmanagedTypeConstraint As Boolean + Get + Return False + End Get + End Property + Public Overrides ReadOnly Property ContainingSymbol As Symbol Get Return Nothing diff --git a/src/Compilers/VisualBasic/Portable/Symbols/InstanceErrorTypeSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/InstanceErrorTypeSymbol.vb index dd6486d1d2839..37297227743c9 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/InstanceErrorTypeSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/InstanceErrorTypeSymbol.vb @@ -205,6 +205,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Friend Overrides ReadOnly Property HasUnmanagedTypeConstraint As Boolean + Get + Return False + End Get + End Property + Friend Overrides Function GetConstraints() As ImmutableArray(Of TypeParameterConstraint) Return ImmutableArray(Of TypeParameterConstraint).Empty End Function diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.vb index 6b033ecd2b07c..e1d387a2b4924 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.vb @@ -12,6 +12,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports System.Reflection Imports System.Reflection.Metadata.Ecma335 +Imports System.Runtime.InteropServices Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE @@ -34,6 +35,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Private _lazyConstraintTypes As ImmutableArray(Of TypeSymbol) + ''' + ''' Actually stores + ''' + Private _lazyHasIsUnmanagedConstraint As Byte + ''' ''' First error calculating bounds. ''' @@ -149,9 +155,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE End Get End Property - Private Function GetDeclaredConstraints() As ImmutableArray(Of TypeParameterConstraint) + Friend Overrides ReadOnly Property HasUnmanagedTypeConstraint As Boolean + Get + EnsureAllConstraintsAreResolved() + Return CType(Volatile.Read(_lazyHasIsUnmanagedConstraint), ThreeState).Value() + End Get + End Property + + Private Function GetDeclaredConstraints( ByRef hasUnmanagedModreqPattern As Boolean) As ImmutableArray(Of TypeParameterConstraint) Dim constraintsBuilder = ArrayBuilder(Of TypeParameterConstraint).GetInstance() + hasUnmanagedModreqPattern = False + If HasConstructorConstraint Then constraintsBuilder.Add(New TypeParameterConstraint(TypeParameterConstraintKind.Constructor, Nothing)) End If @@ -194,9 +209,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE End If For Each constraintHandle In constraints - Dim constraint = metadataReader.GetGenericParameterConstraint(constraintHandle) - Dim constraintTypeHandle = constraint.Type - Dim typeSymbol As TypeSymbol = tokenDecoder.GetTypeOfToken(constraintTypeHandle) + Dim typeSymbol As TypeSymbol = GetConstraintType(metadataReader, tokenDecoder, constraintHandle, hasUnmanagedModreqPattern) ' Drop 'System.ValueType' constraint type if the 'valuetype' constraint was also specified. If ((_flags And GenericParameterAttributes.NotNullableValueTypeConstraint) <> 0) AndAlso @@ -212,9 +225,50 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Next End If + ' - presence of unmanaged pattern has to be matched with `valuetype` + ' - IsUnmanagedAttribute is allowed if there is an unmanaged pattern + If (hasUnmanagedModreqPattern AndAlso (_flags And GenericParameterAttributes.NotNullableValueTypeConstraint) = 0) OrElse + hasUnmanagedModreqPattern <> moduleSymbol.Module.HasIsUnmanagedAttribute(_handle) Then + ' we do not recognize these combinations as "unmanaged" + hasUnmanagedModreqPattern = False + _lazyCachedBoundsUseSiteInfo.InterlockedInitializeFromSentinel(primaryDependency:=Nothing, New UseSiteInfo(Of AssemblySymbol)(ErrorFactory.ErrorInfo(ERRID.ERR_UnsupportedType1, Me))) + End If + Return constraintsBuilder.ToImmutableAndFree() End Function + Private Shared Function GetConstraintType( + metadataReader As MetadataReader, + tokenDecoder As MetadataDecoder, + constraintHandle As GenericParameterConstraintHandle, + ByRef hasUnmanagedModreqPattern As Boolean + ) As TypeSymbol + + Dim constraint = metadataReader.GetGenericParameterConstraint(constraintHandle) + Dim modifiers As ImmutableArray(Of ModifierInfo(Of TypeSymbol)) = Nothing + Dim typeSymbol = tokenDecoder.DecodeGenericParameterConstraint(constraint.Type, modifiers) + + If Not modifiers.IsDefaultOrEmpty AndAlso modifiers.Length > 1 Then + typeSymbol = New UnsupportedMetadataTypeSymbol() + ElseIf typeSymbol.SpecialType = SpecialType.System_ValueType Then + ' recognize "(class [mscorlib]System.ValueType modreq([mscorlib]System.Runtime.InteropServices.UnmanagedType" pattern as "unmanaged" + If Not modifiers.IsDefaultOrEmpty Then + Dim m As ModifierInfo(Of TypeSymbol) = modifiers.Single() + If Not m.IsOptional AndAlso m.Modifier.IsWellKnownTypeUnmanagedType() Then + hasUnmanagedModreqPattern = True + Else + ' Any other modifiers, optional or not, are not allowed + typeSymbol = New UnsupportedMetadataTypeSymbol() + End If + End If + ElseIf Not modifiers.IsDefaultOrEmpty Then + ' Any other modifiers, optional or not, are not allowed + typeSymbol = New UnsupportedMetadataTypeSymbol() + End If + + Return typeSymbol + End Function + Public Overrides ReadOnly Property Locations As ImmutableArray(Of Location) Get Return _containingSymbol.Locations @@ -273,6 +327,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE If _lazyConstraintTypes.IsDefault Then Dim diagnosticsBuilder = ArrayBuilder(Of TypeParameterDiagnosticInfo).GetInstance() Dim inherited = (_containingSymbol.Kind = SymbolKind.Method) AndAlso DirectCast(_containingSymbol, MethodSymbol).IsOverrides + Dim hasUnmanagedModreqPattern As Boolean = False ' Check direct constraints on the type parameter to generate any use-site errors ' (for example, the cycle in ".class public A<(!T)T>"). It's necessary to check for such @@ -284,7 +339,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE ' which cannot be satisfied if A and B are from different hierarchies.) It also isn't ' necessary to report redundant constraints since redundant constraints are still ' valid. Redundant constraints are dropped silently. - Dim constraints = Me.RemoveDirectConstraintConflicts(GetDeclaredConstraints(), inProgress.Prepend(Me), DirectConstraintConflictKind.None, diagnosticsBuilder) + Dim constraints = Me.RemoveDirectConstraintConflicts(GetDeclaredConstraints(hasUnmanagedModreqPattern), inProgress.Prepend(Me), DirectConstraintConflictKind.None, diagnosticsBuilder) Dim primaryDependency As AssemblySymbol = Me.PrimaryDependency Dim useSiteInfo As New UseSiteInfo(Of AssemblySymbol)(primaryDependency) @@ -299,6 +354,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE diagnosticsBuilder.Free() _lazyCachedBoundsUseSiteInfo.InterlockedInitializeFromSentinel(primaryDependency, useSiteInfo) + _lazyHasIsUnmanagedConstraint = hasUnmanagedModreqPattern.ToThreeState() + + ' Note, we are relying on the fact that _lazyConstraintTypes is initialized last, and + ' we depend on the memory barrier from this interlocked operation to prevent write reordering ImmutableInterlocked.InterlockedInitialize(_lazyConstraintTypes, GetConstraintTypesOnly(constraints)) End If diff --git a/src/Compilers/VisualBasic/Portable/Symbols/MethodSignatureComparer.vb b/src/Compilers/VisualBasic/Portable/Symbols/MethodSignatureComparer.vb index 39e5fbc32563e..c395012933648 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/MethodSignatureComparer.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/MethodSignatureComparer.vb @@ -864,6 +864,7 @@ Done: (typeParameter1.HasReferenceTypeConstraint <> typeParameter2.HasReferenceTypeConstraint) OrElse (typeParameter1.HasValueTypeConstraint <> typeParameter2.HasValueTypeConstraint) OrElse (typeParameter1.AllowsRefLikeType <> typeParameter2.AllowsRefLikeType) OrElse + (typeParameter1.HasUnmanagedTypeConstraint <> typeParameter2.HasUnmanagedTypeConstraint) OrElse (typeParameter1.Variance <> typeParameter2.Variance) Then Return False End If diff --git a/src/Compilers/VisualBasic/Portable/Symbols/MethodSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/MethodSymbol.vb index 5e3893146718c..3101385966ebc 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/MethodSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/MethodSymbol.vb @@ -784,16 +784,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols ''' Name lookup should use this method in order to capture proximity, which affects ''' overload resolution. ''' - Friend Function ReduceExtensionMethod(instanceType As TypeSymbol, proximity As Integer, ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As MethodSymbol - Return ReducedExtensionMethodSymbol.Create(instanceType, Me, proximity, useSiteInfo) + Friend Function ReduceExtensionMethod(instanceType As TypeSymbol, proximity As Integer, ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol), languageVersion As LanguageVersion) As MethodSymbol + Return ReducedExtensionMethodSymbol.Create(instanceType, Me, proximity, useSiteInfo, languageVersion) End Function ''' ''' If this is an extension method that can be applied to a instance of the given type, ''' returns the reduced method symbol thus formed. Otherwise, returns Nothing. ''' - Public Function ReduceExtensionMethod(instanceType As TypeSymbol, ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As MethodSymbol - Return ReduceExtensionMethod(instanceType, proximity:=0, useSiteInfo) + Public Function ReduceExtensionMethod(instanceType As TypeSymbol, ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol), languageVersion As LanguageVersion) As MethodSymbol + Return ReduceExtensionMethod(instanceType, proximity:=0, useSiteInfo, languageVersion) End Function ''' @@ -965,7 +965,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Throw New ArgumentNullException(NameOf(receiverType)) End If - Return Me.ReduceExtensionMethod(receiverType.EnsureVbSymbolOrNothing(Of TypeSymbol)(NameOf(receiverType)), CompoundUseSiteInfo(Of AssemblySymbol).Discarded) + Return Me.ReduceExtensionMethod(receiverType.EnsureVbSymbolOrNothing(Of TypeSymbol)(NameOf(receiverType)), CompoundUseSiteInfo(Of AssemblySymbol).Discarded, LanguageVersion.Latest) End Function Private ReadOnly Property IMethodSymbol_Parameters As ImmutableArray(Of IParameterSymbol) Implements IMethodSymbol.Parameters diff --git a/src/Compilers/VisualBasic/Portable/Symbols/NamedTypeSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/NamedTypeSymbol.vb index 03f4062b917df..b983ef0e1e1e1 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/NamedTypeSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/NamedTypeSymbol.vb @@ -936,7 +936,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols ''' ''' True if and only if this type or some containing type has type parameters. ''' - Public ReadOnly Property IsGenericType As Boolean Implements INamedTypeSymbol.IsGenericType + Public ReadOnly Property IsGenericType As Boolean Implements INamedTypeSymbol.IsGenericType, INamedTypeSymbolInternal.IsGenericType Get Dim p As NamedTypeSymbol = Me Do While p IsNot Nothing @@ -1195,6 +1195,121 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Return True End Function + Friend Overrides Function GetManagedKind(ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ManagedKind + Return GetManagedKind(Me, useSiteInfo) + End Function + + ''' + ''' is simple for most named types: + ''' enums are not managed; + ''' non-enum, non-struct named types are managed; + ''' type parameters are managed unless an 'unmanaged' constraint is present; + ''' all special types have spec'd values (basically, (non-string) primitives) are not managed; + ''' + ''' Only structs are complicated, because the definition is recursive. A struct type is managed + ''' if one of its instance fields is managed or a ref field. Unfortunately, this can result in infinite recursion. + ''' If the closure is finite, and we don't find anything definitely managed, then we return true. + ''' If the closure is infinite, we disregard all but a representative of any expanding cycle. + ''' + ''' Intuitively, this will only return true if there's a specific type we can point to that is would + ''' be managed even if it had no fields. e.g. struct S { S s; } is not managed, but struct S { S s; object o; } + ''' is because we can point to object. + ''' + Private Overloads Shared Function GetManagedKind(type As NamedTypeSymbol, ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ManagedKind + type = DirectCast(type.GetTupleUnderlyingTypeOrSelf(), NamedTypeSymbol) + + ' The code below is a clone of BaseTypeAnalysis.GetManagedKind from C#. It should be kept in sync going forward + Dim managedInfo = INamedTypeSymbolInternal.Helpers.IsManagedTypeHelper(type) + Dim definitelyManaged = (managedInfo.isManaged = ThreeState.True) + + If managedInfo.isManaged = ThreeState.Unknown Then + ' Otherwise, we have to build and inspect the closure of depended-upon types. + Dim hs = PooledHashSet(Of Symbol).GetInstance() + Dim result = DependsOnDefinitelyManagedType(type, hs, useSiteInfo) + definitelyManaged = result.definitelyManaged + managedInfo.hasGenerics = managedInfo.hasGenerics OrElse result.hasGenerics + hs.Free() + End If + + If definitelyManaged Then + Return ManagedKind.Managed + ElseIf managedInfo.hasGenerics Then + Return ManagedKind.UnmanagedWithGenerics + Else + Return ManagedKind.Unmanaged + End If + End Function + + Private Shared Function DependsOnDefinitelyManagedType( + type As NamedTypeSymbol, + partialClosure As HashSet(Of Symbol), + ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol) + ) As (definitelyManaged As Boolean, hasGenerics As Boolean) + + Debug.Assert(type IsNot Nothing) + + Dim hasGenerics = False + + If partialClosure.Add(type) Then + + For Each member In type.GetMembersUnordered() + + Dim field = TryCast(member, FieldSymbol) + + ' Only instance fields (including field-like events) affect the outcome. + If field Is Nothing OrElse field.IsShared Then + Continue For + End If + + useSiteInfo.Add(field.GetUseSiteInfo()) + Dim fieldType As TypeSymbol = field.Type.GetTupleUnderlyingTypeOrSelf() + + ' A ref struct which has a ref field is never considered unmanaged + ' but we cannot represent ref fields in VB + ' The previous line should collect an error about the fact + + Select Case fieldType.TypeKind + Case TypeKind.Pointer, TypeKind.FunctionPointer + ' pointers are unmanaged + ExceptionUtilities.UnexpectedValue(fieldType.TypeKind) + Continue For + End Select + + Dim fieldNamedType = TryCast(fieldType, NamedTypeSymbol) + If fieldNamedType Is Nothing Then + If fieldType.GetManagedKind(useSiteInfo) = ManagedKind.Managed Then + Return (True, hasGenerics) + End If + Else + Dim result = INamedTypeSymbolInternal.Helpers.IsManagedTypeHelper(fieldNamedType) + hasGenerics = hasGenerics OrElse result.hasGenerics + ' NOTE: don't use GetManagedKind on a NamedTypeSymbol - that could lead + ' to infinite recursion. + Select Case result.isManaged + Case ThreeState.True + Return (True, hasGenerics) + + Case ThreeState.False + Continue For + + Case ThreeState.Unknown + If Not fieldNamedType.OriginalDefinition.KnownCircularStruct Then + Dim dependsInfo = DependsOnDefinitelyManagedType(fieldNamedType, partialClosure, useSiteInfo) + hasGenerics = hasGenerics OrElse dependsInfo.hasGenerics + If dependsInfo.definitelyManaged Then + Return (True, hasGenerics) + End If + End If + + Continue For + End Select + End If + Next + End If + + Return (False, hasGenerics) + End Function + #Region "INamedTypeSymbol" Private ReadOnly Property INamedTypeSymbol_Arity As Integer Implements INamedTypeSymbol.Arity diff --git a/src/Compilers/VisualBasic/Portable/Symbols/ReducedExtensionMethodSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/ReducedExtensionMethodSymbol.vb index 7cad34b2202d4..bc1475beaed91 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/ReducedExtensionMethodSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/ReducedExtensionMethodSymbol.vb @@ -32,7 +32,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols ''' If this is an extension method that can be applied to an instance of the given type, ''' returns the curried method symbol thus formed. Otherwise, returns Nothing. ''' - Public Shared Function Create(instanceType As TypeSymbol, possiblyExtensionMethod As MethodSymbol, proximity As Integer, ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As MethodSymbol + Public Shared Function Create( + instanceType As TypeSymbol, + possiblyExtensionMethod As MethodSymbol, + proximity As Integer, + ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol), + languageVersion As LanguageVersion + ) As MethodSymbol + Debug.Assert(instanceType IsNot Nothing) Debug.Assert(possiblyExtensionMethod IsNot Nothing) Debug.Assert(proximity >= 0) @@ -140,7 +147,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols ' Check constraints. Dim diagnosticsBuilder = ArrayBuilder(Of TypeParameterDiagnosticInfo).GetInstance() Dim useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) = Nothing - success = possiblyExtensionMethod.CheckConstraints(partialSubstitution, + success = possiblyExtensionMethod.CheckConstraints(languageVersion, + partialSubstitution, typeParametersToFixArray, fixWithArray, diagnosticsBuilder, @@ -721,6 +729,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Friend Overrides ReadOnly Property HasUnmanagedTypeConstraint As Boolean + Get + Return _curriedFromTypeParameter.HasUnmanagedTypeConstraint + End Get + End Property + Public Overrides ReadOnly Property ContainingSymbol As Symbol Get Return _curriedMethod diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingTypeParameterSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingTypeParameterSymbol.vb index b814ac65952a1..4084c4e823693 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingTypeParameterSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingTypeParameterSymbol.vb @@ -80,6 +80,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Retargeting End Get End Property + Friend Overrides ReadOnly Property HasUnmanagedTypeConstraint As Boolean + Get + Return _underlyingTypeParameter.HasUnmanagedTypeConstraint + End Get + End Property + Public Overrides ReadOnly Property HasConstructorConstraint As Boolean Get Return _underlyingTypeParameter.HasConstructorConstraint diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/CrefTypeParameterSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/CrefTypeParameterSymbol.vb index dd14245f73a15..ba0c92ccaf095 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/CrefTypeParameterSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/CrefTypeParameterSymbol.vb @@ -79,6 +79,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Friend Overrides ReadOnly Property HasUnmanagedTypeConstraint As Boolean + Get + Return False + End Get + End Property + Friend Overrides Function GetConstraints() As ImmutableArray(Of TypeParameterConstraint) Return ImmutableArray(Of TypeParameterConstraint).Empty End Function diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceEventSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceEventSymbol.vb index 5513ecf608895..8cf47f22d2330 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceEventSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceEventSymbol.vb @@ -40,6 +40,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols IsDelegateFromImplements = &H2 ' Bit value valid once m_lazyType is assigned. ReportedExplicitImplementationDiagnostics = &H4 SymbolDeclaredEvent = &H8 ' Bit value for generating SymbolDeclaredEvent + TypeConstraintsChecked = &H10 End Enum Private _lazyType As TypeSymbol @@ -150,7 +151,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Sub Private Function ComputeType(diagnostics As BindingDiagnosticBag, ByRef isTypeInferred As Boolean, ByRef isDelegateFromImplements As Boolean) As TypeSymbol - Dim binder = CreateBinderForTypeDeclaration() + Dim binder = BinderBuilder.CreateBinderForType(ContainingSourceModule, _syntaxRef.SyntaxTree, _containingType) + binder = New LocationSpecificBinder(BindingLocation.EventType, Me, binder) Dim syntax = DirectCast(_syntaxRef.GetSyntax(), EventStatementSyntax) isTypeInferred = False @@ -744,10 +746,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Friend Overrides Sub GenerateDeclarationErrors(cancellationToken As CancellationToken) MyBase.GenerateDeclarationErrors(cancellationToken) - Dim unusedType = Me.Type + Dim type = Me.Type Dim unusedImplementations = Me.ExplicitInterfaceImplementations Me.CheckExplicitImplementationTypes() + If (_lazyState And StateFlags.TypeConstraintsChecked) = 0 Then + Dim sourceModule = DirectCast(Me.ContainingModule, SourceModuleSymbol) + Dim diagnostics = BindingDiagnosticBag.GetInstance() + type.CheckAllConstraints(DeclaringCompilation.LanguageVersion, + Locations(0), diagnostics, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, sourceModule.ContainingAssembly)) + sourceModule.AtomicSetFlagAndStoreDiagnostics(_lazyState, StateFlags.TypeConstraintsChecked, 0, diagnostics) + diagnostics.Free() + End If + If DeclaringCompilation.EventQueue IsNot Nothing Then Me.ContainingSourceModule.AtomicSetFlagAndRaiseSymbolDeclaredEvent(_lazyState, StateFlags.SymbolDeclaredEvent, 0, Me) End If diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceFieldSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceFieldSymbol.vb index 2556c6218da54..9e7528edde223 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceFieldSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceFieldSymbol.vb @@ -32,8 +32,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Private _lazyExpandedDocComment As String Private _lazyCustomAttributesBag As CustomAttributesBag(Of VisualBasicAttributeData) - ' Set to 1 when the compilation event has been produced - Private _eventProduced As Integer + ''' + ''' See + ''' + Protected _lazyState As Integer + + + Protected Enum StateFlags As Integer + TypeConstraintsChecked = &H1 + + EventProduced = &H2 + End Enum Protected Sub New(container As SourceMemberContainerTypeSymbol, syntaxRef As SyntaxReference, @@ -51,15 +60,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols m_memberFlags = memberFlags End Sub - Friend Overrides Sub GenerateDeclarationErrors(cancellationToken As CancellationToken) + Protected Overridable Sub GenerateDeclarationErrorsImpl(cancellationToken As CancellationToken) MyBase.GenerateDeclarationErrors(cancellationToken) Dim unusedType = Me.Type GetConstantValue(ConstantFieldsInProgress.Empty) + End Sub + + Friend NotOverridable Overrides Sub GenerateDeclarationErrors(cancellationToken As CancellationToken) + GenerateDeclarationErrorsImpl(cancellationToken) ' We want declaration events to be last, after all compilation analysis is done, so we produce them here Dim sourceModule = DirectCast(Me.ContainingModule, SourceModuleSymbol) - If Interlocked.CompareExchange(_eventProduced, 1, 0) = 0 AndAlso Not Me.IsImplicitlyDeclared Then + If ThreadSafeFlagOperations.Set(_lazyState, StateFlags.EventProduced) AndAlso Not Me.IsImplicitlyDeclared Then sourceModule.DeclaringCompilation.SymbolDeclaredEvent(Me) End If End Sub diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceFile.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceFile.vb index 98553d475e29f..d811f22c45274 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceFile.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceFile.vb @@ -360,7 +360,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Dim type = TryCast(namespaceOrType, TypeSymbol) If type IsNot Nothing Then clauseDiagnostics.Clear() - type.CheckAllConstraints(location, clauseDiagnostics, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, compilation.Assembly)) + type.CheckAllConstraints( + compilation.LanguageVersion, + location, clauseDiagnostics, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, compilation.Assembly)) diagnostics.AddRange(clauseDiagnostics.DiagnosticBag) If VisualBasicCompilation.ReportUnusedImportsInTree(location.PossiblyEmbeddedOrMySourceTree) Then diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMemberFieldSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMemberFieldSymbol.vb index 3a0251f8311d5..1e034909add1b 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMemberFieldSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMemberFieldSymbol.vb @@ -29,6 +29,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols MyBase.New(container, syntaxRef, name, memberFlags) End Sub + Protected Overrides Sub GenerateDeclarationErrorsImpl(cancellationToken As CancellationToken) + MyBase.GenerateDeclarationErrorsImpl(cancellationToken) + + If (_lazyState And StateFlags.TypeConstraintsChecked) = 0 Then + Dim sourceModule = DirectCast(Me.ContainingModule, SourceModuleSymbol) + Dim diagnostics = BindingDiagnosticBag.GetInstance() + Type.CheckAllConstraints(DeclaringCompilation.LanguageVersion, + Locations(0), diagnostics, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, sourceModule.ContainingAssembly)) + sourceModule.AtomicSetFlagAndStoreDiagnostics(_lazyState, StateFlags.TypeConstraintsChecked, 0, diagnostics) + diagnostics.Free() + End If + End Sub + Friend NotOverridable Overrides ReadOnly Property DeclarationSyntax As VisualBasicSyntaxNode Get Return Syntax.Parent.Parent diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMethodSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMethodSymbol.vb index 9d17716bffd83..bcf324bcc0325 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMethodSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMethodSymbol.vb @@ -2156,7 +2156,9 @@ lReportErrorOnTwoTokens: If param.Locations.Length > 0 Then ' Note: Errors are reported on the parameter name. Ideally, we should ' match Dev10 and report errors on the parameter type syntax instead. - param.Type.CheckAllConstraints(param.Locations(0), diagBag, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagBag, sourceModule.ContainingAssembly)) + param.Type.CheckAllConstraints( + DeclaringCompilation.LanguageVersion, + param.Locations(0), diagBag, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagBag, sourceModule.ContainingAssembly)) End If Next @@ -2164,7 +2166,9 @@ lReportErrorOnTwoTokens: Dim diagnosticsBuilder = ArrayBuilder(Of TypeParameterDiagnosticInfo).GetInstance() Dim useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) = Nothing - retType.CheckAllConstraints(diagnosticsBuilder, useSiteDiagnosticsBuilder, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagBag, sourceModule.ContainingAssembly)) + retType.CheckAllConstraints( + DeclaringCompilation.LanguageVersion, + diagnosticsBuilder, useSiteDiagnosticsBuilder, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagBag, sourceModule.ContainingAssembly)) If useSiteDiagnosticsBuilder IsNot Nothing Then diagnosticsBuilder.AddRange(useSiteDiagnosticsBuilder) diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceModuleSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceModuleSymbol.vb index 5721db14a43f3..a30a836f3121b 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceModuleSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceModuleSymbol.vb @@ -529,7 +529,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Private Sub ValidateImport(type As TypeSymbol, info As GlobalImportInfo, diagnostics As BindingDiagnosticBag) Dim diagnosticsBuilder = ArrayBuilder(Of TypeParameterDiagnosticInfo).GetInstance() Dim useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) = Nothing - type.CheckAllConstraints(diagnosticsBuilder, useSiteDiagnosticsBuilder, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, ContainingAssembly)) + type.CheckAllConstraints( + DeclaringCompilation.LanguageVersion, + diagnosticsBuilder, useSiteDiagnosticsBuilder, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, ContainingAssembly)) If useSiteDiagnosticsBuilder IsNot Nothing Then diagnosticsBuilder.AddRange(useSiteDiagnosticsBuilder) diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceNamedTypeSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceNamedTypeSymbol.vb index f540c8c35744f..abd72dfff73ac 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceNamedTypeSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceNamedTypeSymbol.vb @@ -1626,7 +1626,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Dim location = singleDeclaration.NameLocation diagnostics = BindingDiagnosticBag.GetInstance() - localBase.CheckAllConstraints(location, diagnostics, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, m_containingModule.ContainingAssembly)) + localBase.CheckAllConstraints( + DeclaringCompilation.LanguageVersion, + location, diagnostics, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, m_containingModule.ContainingAssembly)) If IsGenericType Then ' Check that generic type does not derive from System.Attribute. @@ -1678,7 +1680,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Dim location = singleDeclaration.NameLocation diagnostics = BindingDiagnosticBag.GetInstance() For Each [interface] In localInterfaces - [interface].CheckAllConstraints(location, diagnostics, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, m_containingModule.ContainingAssembly)) + [interface].CheckAllConstraints( + DeclaringCompilation.LanguageVersion, + location, diagnostics, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, m_containingModule.ContainingAssembly)) Next End If End If diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourcePropertyAccessorSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourcePropertyAccessorSymbol.vb index a34e887cd0f87..02addfd0ec070 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourcePropertyAccessorSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourcePropertyAccessorSymbol.vb @@ -191,7 +191,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Dim diagnosticsBuilder = ArrayBuilder(Of TypeParameterDiagnosticInfo).GetInstance() Dim useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) = Nothing - retType.CheckAllConstraints(diagnosticsBuilder, useSiteDiagnosticsBuilder, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagBag, sourceModule.ContainingAssembly)) + retType.CheckAllConstraints( + DeclaringCompilation.LanguageVersion, + diagnosticsBuilder, useSiteDiagnosticsBuilder, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagBag, sourceModule.ContainingAssembly)) If useSiteDiagnosticsBuilder IsNot Nothing Then diagnosticsBuilder.AddRange(useSiteDiagnosticsBuilder) @@ -260,7 +262,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols If param.Locations.Length > 0 Then ' Note: Errors are reported on the parameter name. Ideally, we should ' match Dev10 and report errors on the parameter type syntax instead. - param.Type.CheckAllConstraints(param.Locations(0), diagBag, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagBag, sourceModule.ContainingAssembly)) + param.Type.CheckAllConstraints( + DeclaringCompilation.LanguageVersion, + param.Locations(0), diagBag, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagBag, sourceModule.ContainingAssembly)) End If Next diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourcePropertySymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourcePropertySymbol.vb index 4f67c023631da..661fbf78934c2 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourcePropertySymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourcePropertySymbol.vb @@ -55,6 +55,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Private Enum StateFlags As Integer SymbolDeclaredEvent = &H1 ' Bit value for generating SymbolDeclaredEvent + + TypeConstraintsChecked = &H2 End Enum Private Sub New(container As SourceMemberContainerTypeSymbol, @@ -335,7 +337,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Property Private Function ComputeType(diagnostics As BindingDiagnosticBag) As TypeSymbol - Dim binder = CreateBinderForTypeDeclaration() + Dim binder = BinderBuilder.CreateBinderForType(DirectCast(ContainingModule, SourceModuleSymbol), _syntaxRef.SyntaxTree, _containingType) + binder = New LocationSpecificBinder(BindingLocation.PropertyType, Me, binder) If IsWithEvents Then Dim syntax = DirectCast(_syntaxRef.GetSyntax(), ModifiedIdentifierSyntax) @@ -1190,11 +1193,20 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols MyBase.GenerateDeclarationErrors(cancellationToken) ' Ensure return type attributes are bound - Dim unusedType = Me.Type + Dim type = Me.Type Dim unusedParameters = Me.Parameters Me.GetReturnTypeAttributesBag() Dim unusedImplementations = Me.ExplicitInterfaceImplementations + If (_lazyState And StateFlags.TypeConstraintsChecked) = 0 Then + Dim sourceModule = DirectCast(Me.ContainingModule, SourceModuleSymbol) + Dim diagnostics = BindingDiagnosticBag.GetInstance() + type.CheckAllConstraints(DeclaringCompilation.LanguageVersion, + Locations(0), diagnostics, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, sourceModule.ContainingAssembly)) + sourceModule.AtomicSetFlagAndStoreDiagnostics(_lazyState, StateFlags.TypeConstraintsChecked, 0, diagnostics) + diagnostics.Free() + End If + If DeclaringCompilation.EventQueue IsNot Nothing Then DirectCast(Me.ContainingModule, SourceModuleSymbol).AtomicSetFlagAndRaiseSymbolDeclaredEvent(_lazyState, StateFlags.SymbolDeclaredEvent, 0, Me) End If diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceTypeParameterSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceTypeParameterSymbol.vb index 126b2ef5c1ab5..eb20fe448f87d 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceTypeParameterSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceTypeParameterSymbol.vb @@ -90,6 +90,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Friend Overrides ReadOnly Property HasUnmanagedTypeConstraint As Boolean + Get + Return False + End Get + End Property + Friend Overrides Function GetConstraints() As ImmutableArray(Of TypeParameterConstraint) EnsureAllConstraintsAreResolved() Return _lazyConstraints @@ -183,7 +189,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols constraintType.AddUseSiteInfo(useSiteInfo) If Not diagnostics.Add(location, useSiteInfo) Then - constraintType.CheckAllConstraints(location, diagnostics, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, containingAssembly)) + constraintType.CheckAllConstraints( + DeclaringCompilation.LanguageVersion, + location, diagnostics, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, containingAssembly)) End If End If Next diff --git a/src/Compilers/VisualBasic/Portable/Symbols/SubstitutedTypeParameterSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/SubstitutedTypeParameterSymbol.vb index 475be49ff2a2c..461844dd4f76f 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/SubstitutedTypeParameterSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/SubstitutedTypeParameterSymbol.vb @@ -98,6 +98,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Friend Overrides ReadOnly Property HasUnmanagedTypeConstraint As Boolean + Get + Return _originalDefinition.HasUnmanagedTypeConstraint + End Get + End Property + Public Overrides ReadOnly Property ContainingSymbol As Symbol Get Return _containingSymbol diff --git a/src/Compilers/VisualBasic/Portable/Symbols/SynthesizedSymbols/SynthesizedClonedTypeParameterSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/SynthesizedSymbols/SynthesizedClonedTypeParameterSymbol.vb index e6a1a352fbd6d..398264f9da09f 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/SynthesizedSymbols/SynthesizedClonedTypeParameterSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/SynthesizedSymbols/SynthesizedClonedTypeParameterSymbol.vb @@ -76,6 +76,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Friend Overrides ReadOnly Property HasUnmanagedTypeConstraint As Boolean + Get + Return _correspondingMethodTypeParameter.HasUnmanagedTypeConstraint + End Get + End Property + Public Overrides ReadOnly Property ContainingSymbol As Symbol Get Return _container diff --git a/src/Compilers/VisualBasic/Portable/Symbols/TypeParameterSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/TypeParameterSymbol.vb index 184a1c7286655..603d4ca44ce2c 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/TypeParameterSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/TypeParameterSymbol.vb @@ -319,11 +319,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public MustOverride ReadOnly Property AllowsRefLikeType As Boolean Implements ITypeParameterSymbol.AllowsRefLikeType - Private ReadOnly Property HasUnmanagedTypeConstraint As Boolean Implements ITypeParameterSymbol.HasUnmanagedTypeConstraint - Get - Return False - End Get - End Property + Friend MustOverride ReadOnly Property HasUnmanagedTypeConstraint As Boolean Implements ITypeParameterSymbol.HasUnmanagedTypeConstraint Private ReadOnly Property HasNotNullConstraint As Boolean Implements ITypeParameterSymbol.HasNotNullConstraint Get @@ -333,6 +329,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public MustOverride ReadOnly Property Variance As VarianceKind Implements ITypeParameterSymbol.Variance + Friend Overrides Function GetManagedKind(ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ManagedKind + Return If(HasUnmanagedTypeConstraint, ManagedKind.Unmanaged, ManagedKind.Managed) + End Function + ''' ''' If this is a type parameter of a reduced extension method, gets the type parameter definition that ''' this type parameter was reduced from. Otherwise, returns Nothing. diff --git a/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbol.vb index 72f9437aa8837..c6f532857bfda 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbol.vb @@ -261,6 +261,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + ' + ' Indicates whether a type is managed or not in C# terms (i.e. you can take a pointer to it). + ' + Friend MustOverride Function GetManagedKind(ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ManagedKind + ' Only the compiler can create TypeSymbols. Friend Sub New() End Sub @@ -588,8 +593,7 @@ Done: Private ReadOnly Property ITypeSymbol_IsUnmanagedType As Boolean Implements ITypeSymbol.IsUnmanagedType Get - ' VB has no concept of unmanaged types - Return False + Return GetManagedKind(CompoundUseSiteInfo(Of AssemblySymbol).Discarded) <> ManagedKind.Managed End Get End Property diff --git a/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbolExtensions.vb b/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbolExtensions.vb index 8dbdcb3e68379..80ccdad058a12 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbolExtensions.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbolExtensions.vb @@ -1308,6 +1308,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols namedTypeSymbol.IsContainedInNamespace(NameOf(System), NameOf(System.Threading)) End Function + ' Keep in sync with C# equivalent. + + Friend Function IsWellKnownTypeUnmanagedType(typeSymbol As TypeSymbol) As Boolean + Dim namedTypeSymbol = TryCast(typeSymbol, NamedTypeSymbol) + Return namedTypeSymbol IsNot Nothing AndAlso + namedTypeSymbol.Name = "UnmanagedType" AndAlso + namedTypeSymbol.Arity = 0 AndAlso + namedTypeSymbol.ContainingType Is Nothing AndAlso + IsContainedInNamespace(typeSymbol, "System", "Runtime", "InteropServices") + End Function + Private Function IsWellKnownCompilerServicesTopLevelType(typeSymbol As TypeSymbol, name As String) As Boolean If Not String.Equals(typeSymbol.Name, name) Then diff --git a/src/Compilers/VisualBasic/Portable/VBResources.resx b/src/Compilers/VisualBasic/Portable/VBResources.resx index 6e3a339790dbb..aa32f54bfc7c7 100644 --- a/src/Compilers/VisualBasic/Portable/VBResources.resx +++ b/src/Compilers/VisualBasic/Portable/VBResources.resx @@ -2784,6 +2784,9 @@ Type argument '{0}' does not satisfy the 'Structure' constraint for type parameter '{1}'. + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' does not satisfy the 'Class' constraint for type parameter '{1}'. @@ -5626,6 +5629,9 @@ assigning to or passing 'ByRef' properties with init-only setters + + recognizing 'unmanaged' constraint + Init-only property '{0}' can only be assigned by an object member initializer, or on 'Me', 'MyClass` or 'MyBase' in an instance constructor. diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.cs.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.cs.xlf index 58a67c5e5dee6..678da8c69cc85 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.cs.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.cs.xlf @@ -132,6 +132,11 @@ Atribut UnmanagedCallersOnly se nepodporuje. + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the Visual Basic compiler. {0} vyžaduje funkci kompilátoru {1}, což tato verze kompilátoru Visual Basic nepodporuje. @@ -162,6 +167,11 @@ parametry neomezeného typu v binárních podmíněných výrazech + + recognizing 'unmanaged' constraint + recognizing 'unmanaged' constraint + + Visual Basic Compiler Options diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.de.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.de.xlf index 9e9ed4cc30aa5..8ec7aec8c00c2 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.de.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.de.xlf @@ -132,6 +132,11 @@ Das Attribut "UnmanagedCallersOnly" wird nicht unterstützt. + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the Visual Basic compiler. "{0}" erfordert die Compilerfunktion "{1}", die von dieser Version des Visual Basic Compilers nicht unterstützt wird. @@ -162,6 +167,11 @@ Nicht eingeschränkte Typparameter in binären bedingten Ausdrücken + + recognizing 'unmanaged' constraint + recognizing 'unmanaged' constraint + + Visual Basic Compiler Options diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.es.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.es.xlf index edd7d212905a9..bf862f7cc8d8a 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.es.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.es.xlf @@ -132,6 +132,11 @@ No se admite el atributo "UnmanagedCallersOnly". + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the Visual Basic compiler. '{0}' requiere la característica del compilador '{1}', que no es compatible con esta versión del compilador de Visual Basic. @@ -162,6 +167,11 @@ parámetros de tipo sin restricciones en expresiones condicionales binarias + + recognizing 'unmanaged' constraint + recognizing 'unmanaged' constraint + + Visual Basic Compiler Options diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.fr.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.fr.xlf index e9940a1be752b..34598d12f1e3c 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.fr.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.fr.xlf @@ -132,6 +132,11 @@ L'attribut 'UnmanagedCallersOnly' n'est pas pris en charge. + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the Visual Basic compiler. '{0}' nécessite la fonctionnalité de compilateur '{1}', qui n’est pas prise en charge par cette version du compilateur Visual Basic. @@ -162,6 +167,11 @@ paramètres de type sans contrainte dans les expressions conditionnelles binaires + + recognizing 'unmanaged' constraint + recognizing 'unmanaged' constraint + + Visual Basic Compiler Options diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.it.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.it.xlf index 1115589772d4f..f29bdafb4d467 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.it.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.it.xlf @@ -132,6 +132,11 @@ L'attributo 'UnmanagedCallersOnly' non è supportato. + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the Visual Basic compiler. '{0}' richiede la funzionalità del compilatore '{1}', che non è supportata da questa versione del compilatore Visual Basic. @@ -162,6 +167,11 @@ parametri di tipo non senza vincoli in espressioni condizionali binarie + + recognizing 'unmanaged' constraint + recognizing 'unmanaged' constraint + + Visual Basic Compiler Options diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ja.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ja.xlf index dd1aeb31ff170..cee9b4d04c988 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ja.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ja.xlf @@ -132,6 +132,11 @@ 'UnmanagedCallersOnly' 属性はサポートされていません。 + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the Visual Basic compiler. '{0}' にはコンパイラ機能 '{1}' が必要ですが、このバージョンのVisual Basic コンパイラではサポートされていません。 @@ -162,6 +167,11 @@ バイナリ条件式での非制約型パラメーター + + recognizing 'unmanaged' constraint + recognizing 'unmanaged' constraint + + Visual Basic Compiler Options diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ko.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ko.xlf index 7810d8be04987..a7324e86a2bb4 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ko.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ko.xlf @@ -132,6 +132,11 @@ 'UnmanagedCallersOnly' 특성은 지원되지 않습니다. + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the Visual Basic compiler. '{0}'에는 이 버전의 Visual Basic 컴파일러에서 지원하지 않는 컴파일러 기능 '{1}'이(가) 필요합니다. @@ -162,6 +167,11 @@ 이진 조건식의 비제한 형식 매개 변수 + + recognizing 'unmanaged' constraint + recognizing 'unmanaged' constraint + + Visual Basic Compiler Options diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.pl.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.pl.xlf index 07f875f3c3812..350bd6716bc38 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.pl.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.pl.xlf @@ -132,6 +132,11 @@ Atrybut „UnmanagedCallersOnly” jest nieobsługiwany. + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the Visual Basic compiler. „{0}” wymaga funkcji kompilatora „{1}”, która nie jest obsługiwana przez tę wersję kompilatora języka Visual Basic. @@ -162,6 +167,11 @@ parametry typu bez ograniczeń w binarnych wyrażeniach warunkowych + + recognizing 'unmanaged' constraint + recognizing 'unmanaged' constraint + + Visual Basic Compiler Options diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.pt-BR.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.pt-BR.xlf index 80f5bd687e3e0..2da6bfec81618 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.pt-BR.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.pt-BR.xlf @@ -132,6 +132,11 @@ Não há suporte para o atributo 'UnmanagedCallersOnly'. + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the Visual Basic compiler. '{0}' requer o recurso de compilador '{1}', o que não é suportado por esta versão do compilador do Visual Basic. @@ -162,6 +167,11 @@ parâmetros de tipo irrestritos em expressões condicionais binárias + + recognizing 'unmanaged' constraint + recognizing 'unmanaged' constraint + + Visual Basic Compiler Options diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ru.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ru.xlf index 09bf20a178096..2c2b9facbe584 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ru.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ru.xlf @@ -132,6 +132,11 @@ Атрибут "UnmanagedCallersOnly" не поддерживается. + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the Visual Basic compiler. Для "{0}" требуется функция компилятора "{1}", которая не поддерживается в этой версии компилятора Visual Basic. @@ -162,6 +167,11 @@ параметры неограниченного типа в двоичных условных выражениях + + recognizing 'unmanaged' constraint + recognizing 'unmanaged' constraint + + Visual Basic Compiler Options diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.tr.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.tr.xlf index 68462011b0d12..f8ee6dd28cb27 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.tr.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.tr.xlf @@ -132,6 +132,11 @@ 'UnmanagedCallersOnly' özniteliği desteklenmez. + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the Visual Basic compiler. '{0}', Visual Basic derleyicisinin bu sürümü tarafından desteklenmeyen '{1}' derleyici özelliğini gerektirir. @@ -162,6 +167,11 @@ ikili koşullu ifadelerde kısıtlanmamış tür parametreleri + + recognizing 'unmanaged' constraint + recognizing 'unmanaged' constraint + + Visual Basic Compiler Options diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hans.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hans.xlf index 76bc9c7093c0c..78a655222e9b3 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hans.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hans.xlf @@ -132,6 +132,11 @@ 不支持 "UnmanagedCallersOnly" 属性。 + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the Visual Basic compiler. '{0}' 需要编译器功能 '{1}',此版本的 Visual Basic 编译器不支持此功能。 @@ -162,6 +167,11 @@ 二进制条件表达式中的无约束类型参数 + + recognizing 'unmanaged' constraint + recognizing 'unmanaged' constraint + + Visual Basic Compiler Options diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hant.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hant.xlf index de5cea83ecad1..f081ca6a94841 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hant.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hant.xlf @@ -132,6 +132,11 @@ 不支援 'UnmanagedCallersOnly' 屬性。 + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the Visual Basic compiler. '{0}' 需要編譯器功能 '{1}',此版本的 Visual Basic 編譯器不支援此功能。 @@ -162,6 +167,11 @@ 二進位條件運算式中的非限制式型別參數 + + recognizing 'unmanaged' constraint + recognizing 'unmanaged' constraint + + Visual Basic Compiler Options diff --git a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb index 46325712059f2..56c4f2eea52d6 100644 --- a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb +++ b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb @@ -1530,6 +1530,10 @@ End Module").Path parsedArgs.Errors.Verify() Assert.Equal(LanguageVersion.VisualBasic16_9, parsedArgs.ParseOptions.LanguageVersion) + parsedArgs = DefaultParse({"/langVERSION:17.13", "a.vb"}, _baseDirectory) + parsedArgs.Errors.Verify() + Assert.Equal(LanguageVersion.VisualBasic17_13, parsedArgs.ParseOptions.LanguageVersion) + ' The canary check is a reminder that this test needs to be updated when a language version is added LanguageVersionAdded_Canary() @@ -2055,7 +2059,7 @@ End Module").Path ' - update the "UpgradeProject" codefixer (not yet supported in VB) ' - update all the tests that call this canary ' - update the command-line documentation (CommandLine.md) - AssertEx.SetEqual({"default", "9", "10", "11", "12", "14", "15", "15.3", "15.5", "16", "16.9", "latest"}, + AssertEx.SetEqual({"default", "9", "10", "11", "12", "14", "15", "15.3", "15.5", "16", "16.9", "17.13", "latest"}, System.Enum.GetValues(GetType(LanguageVersion)).Cast(Of LanguageVersion)().Select(Function(v) v.ToDisplayString())) ' For minor versions, the format should be "x.y", such as "15.3" End Sub @@ -2077,7 +2081,8 @@ End Module").Path "15.3", "15.5", "16", - "16.9" + "16.9", + "17.13" } AssertEx.SetEqual(versions, errorCodes) @@ -2098,6 +2103,7 @@ End Module").Path Assert.Equal(LanguageVersion.VisualBasic15_5, LanguageVersion.VisualBasic15_5.MapSpecifiedToEffectiveVersion()) Assert.Equal(LanguageVersion.VisualBasic16, LanguageVersion.VisualBasic16.MapSpecifiedToEffectiveVersion()) Assert.Equal(LanguageVersion.VisualBasic16_9, LanguageVersion.VisualBasic16_9.MapSpecifiedToEffectiveVersion()) + Assert.Equal(LanguageVersion.VisualBasic17_13, LanguageVersion.VisualBasic17_13.MapSpecifiedToEffectiveVersion()) ' The canary check is a reminder that this test needs to be updated when a language version is added LanguageVersionAdded_Canary() @@ -2121,6 +2127,7 @@ End Module").Path InlineData("16", True, LanguageVersion.VisualBasic16), InlineData("16.0", True, LanguageVersion.VisualBasic16), InlineData("16.9", True, LanguageVersion.VisualBasic16_9), + InlineData("17.13", True, LanguageVersion.VisualBasic17_13), InlineData("DEFAULT", True, LanguageVersion.Default), InlineData("default", True, LanguageVersion.Default), InlineData("LATEST", True, LanguageVersion.Latest), diff --git a/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenTuples.vb b/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenTuples.vb index d67a719f84de7..3a01a511788d1 100644 --- a/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenTuples.vb +++ b/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenTuples.vb @@ -14448,7 +14448,7 @@ End Class BC32106: Type argument 'T' does not satisfy the 'Class' constraint for type parameter 'T2'. Dim field As List(Of (T, T)) - ~ + ~~~~~ BC32106: Type argument 'U' does not satisfy the 'Class' constraint for type parameter 'T2'. Function M(Of U)(x As U) As (U, U) ~~~~~~ @@ -14516,7 +14516,7 @@ End Class BC32105: Type argument 'T' does not satisfy the 'Structure' constraint for type parameter 'T2'. Dim field As List(Of (T, T)) - ~ + ~~~~~ BC32105: Type argument 'U' does not satisfy the 'Structure' constraint for type parameter 'T2'. Function M(Of U As Class)(x As (U, U)) As (U, U) ~ diff --git a/src/Compilers/VisualBasic/Test/Semantic/Compilation/CompilationAPITests.vb b/src/Compilers/VisualBasic/Test/Semantic/Compilation/CompilationAPITests.vb index fa7e860d7c8c4..7a317369b781d 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Compilation/CompilationAPITests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Compilation/CompilationAPITests.vb @@ -2324,10 +2324,10 @@ End Class Public Sub ReferenceManagerReuse_WithSyntaxTrees() Dim ta = Parse("Imports System") - Dim tb = Parse("Imports System", options:=TestOptions.Script) + Dim tb = Parse("Imports System", options:=TestOptions.Script.WithLanguageVersion(LanguageVersion.Latest)) Dim tc = Parse("#r ""bar"" ' error: #r in regular code") - Dim tr = Parse("#r ""goo""", options:=TestOptions.Script) - Dim ts = Parse("#r ""bar""", options:=TestOptions.Script) + Dim tr = Parse("#r ""goo""", options:=TestOptions.Script.WithLanguageVersion(LanguageVersion.Latest)) + Dim ts = Parse("#r ""bar""", options:=TestOptions.Script.WithLanguageVersion(LanguageVersion.Latest)) Dim a = VisualBasicCompilation.Create("c", syntaxTrees:={ta}) diff --git a/src/Compilers/VisualBasic/Test/Semantic/Compilation/SemanticModelAPITests.vb b/src/Compilers/VisualBasic/Test/Semantic/Compilation/SemanticModelAPITests.vb index b91a3444a2145..84df0803e7b17 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Compilation/SemanticModelAPITests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Compilation/SemanticModelAPITests.vb @@ -2947,12 +2947,12 @@ End Class") Single(Function(n) n.Identifier.ValueText = name) Return CType(model.GetDeclaredSymbol(decl), ILocalSymbol).Type End Function - ' VB does not have a concept of a managed type - Assert.False(getLocalType("s1").IsUnmanagedType) - Assert.False(getLocalType("s2").IsUnmanagedType) + + Assert.True(getLocalType("s1").IsUnmanagedType) + Assert.True(getLocalType("s2").IsUnmanagedType) Assert.False(getLocalType("s3").IsUnmanagedType) - Assert.False(getLocalType("s4").IsUnmanagedType) - Assert.False(getLocalType("e1").IsUnmanagedType) + Assert.True(getLocalType("s4").IsUnmanagedType) + Assert.True(getLocalType("e1").IsUnmanagedType) End Sub diff --git a/src/Compilers/VisualBasic/Test/Semantic/Semantics/OverloadResolution.vb b/src/Compilers/VisualBasic/Test/Semantic/Semantics/OverloadResolution.vb index b3944b3117778..28420b328047d 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Semantics/OverloadResolution.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Semantics/OverloadResolution.vb @@ -3050,7 +3050,7 @@ Class OptionStrictOff End Class - Dim optionStrictOffTree = VisualBasicSyntaxTree.ParseText(optionStrictOff.Value) + Dim optionStrictOffTree = VisualBasicSyntaxTree.ParseText(optionStrictOff.Value, options:=TestOptions.RegularLatest) Dim c1 = VisualBasicCompilation.Create("Test1", syntaxTrees:={Parse(SemanticResourceUtil.OverloadResolutionTestSource), optionStrictOffTree}, diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/GenericConstraintTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/GenericConstraintTests.vb index 0f6ea6c15d133..55c1d3224dc62 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/GenericConstraintTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/GenericConstraintTests.vb @@ -4686,7 +4686,7 @@ End Interface compilation.AssertTheseDiagnostics( BC32044: Type argument 'String' does not inherit from or implement the constraint type 'IStoreable'. Public ReadOnly Property Deleted As IEnumerable(Of UpdateResult(Of String)) - ~~~~~~ + ~~~~~~~ ) End Sub @@ -5014,10 +5014,10 @@ Delegate Sub D(Of T As New)() compilation.AssertTheseDiagnostics( BC32044: Type argument 'C3T2' does not inherit from or implement the constraint type 'Integer'. Dim x As C1(Of Integer, Integer).C2(Of C2T1, C2T2).C3(Of C3T1, C3T2) - ~~~~ + ~ ) Assert.Throws(Of InvalidOperationException)(Sub() c5.Construct(c1)) diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Metadata/PE/LoadCustomModifiers.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Metadata/PE/LoadCustomModifiers.vb index b08336c56d925..58edd937fbfc4 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Metadata/PE/LoadCustomModifiers.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Metadata/PE/LoadCustomModifiers.vb @@ -110,7 +110,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Symbols.Metadata.PE End Sub - Public Sub UnmanagedConstraint_RejectedSymbol_OnClass() + Public Sub UnmanagedConstraint_OnClass() Dim reference = CreateCSharpCompilation(" public class TestRef where T : unmanaged { @@ -122,6 +122,7 @@ public class TestRef where T : unmanaged Class Test Shared Sub Main() Dim x = New TestRef(Of String)() + Dim y = New TestRef(Of Integer)() End Sub End Class @@ -129,24 +130,63 @@ End Class Dim compilation = CreateCompilationWithMscorlib45AndVBRuntime(source, references:={reference}) - AssertTheseDiagnostics(compilation, -BC30649: '' is an unsupported type. + Dim errs As XElement = + +BC32105: Type argument 'String' does not satisfy the 'Structure' constraint for type parameter 'T'. Dim x = New TestRef(Of String)() ~~~~~~ -BC32044: Type argument 'String' does not inherit from or implement the constraint type '?'. +BC37332: Type argument 'String' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter 'T'. Dim x = New TestRef(Of String)() ~~~~~~ + + + AssertTheseDiagnostics(compilation, errs) + + Dim typeParameter = compilation.GetTypeByMetadataName("TestRef`1").TypeParameters.Single() + Assert.True(typeParameter.HasValueTypeConstraint) + Assert.True(typeParameter.HasUnmanagedTypeConstraint) + + typeParameter = compilation.GetTypeByMetadataName("System.Nullable`1").TypeParameters.Single() + Assert.True(typeParameter.HasValueTypeConstraint) + Assert.False(typeParameter.HasUnmanagedTypeConstraint) + + compilation = CreateCompilationWithMscorlib45AndVBRuntime(source, references:={reference}, parseOptions:=TestOptions.Regular17_13) + AssertTheseDiagnostics(compilation, errs) + + typeParameter = compilation.GetTypeByMetadataName("TestRef`1").TypeParameters.Single() + Assert.True(typeParameter.HasValueTypeConstraint) + Assert.True(typeParameter.HasUnmanagedTypeConstraint) + compilation = CreateCompilationWithMscorlib45AndVBRuntime(source, references:={reference}, parseOptions:=TestOptions.RegularLatest) + + AssertTheseDiagnostics(compilation, errs) + + typeParameter = compilation.GetTypeByMetadataName("TestRef`1").TypeParameters.Single() + Assert.True(typeParameter.HasValueTypeConstraint) + Assert.True(typeParameter.HasUnmanagedTypeConstraint) + + compilation = CreateCompilationWithMscorlib45AndVBRuntime(source, references:={reference}, parseOptions:=TestOptions.Regular16_9) + AssertTheseDiagnostics(compilation, BC32105: Type argument 'String' does not satisfy the 'Structure' constraint for type parameter 'T'. Dim x = New TestRef(Of String)() ~~~~~~ +BC36716: Visual Basic 16.9 does not support recognizing 'unmanaged' constraint. + Dim x = New TestRef(Of String)() + ~~~~~~ +BC37332: Type argument 'String' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter 'T'. + Dim x = New TestRef(Of String)() + ~~~~~~ +BC36716: Visual Basic 16.9 does not support recognizing 'unmanaged' constraint. + Dim y = New TestRef(Of Integer)() + ~~~~~~~ ) - Dim badTypeParameter = compilation.GetTypeByMetadataName("TestRef`1").TypeParameters.Single() - Assert.True(badTypeParameter.HasValueTypeConstraint) + typeParameter = compilation.GetTypeByMetadataName("TestRef`1").TypeParameters.Single() + Assert.True(typeParameter.HasValueTypeConstraint) + Assert.True(typeParameter.HasUnmanagedTypeConstraint) End Sub - Public Sub UnmanagedConstraint_RejectedSymbol_OnMethod() + Public Sub UnmanagedConstraint_OnMethod() Dim reference = CreateCSharpCompilation(" public class TestRef { @@ -170,17 +210,21 @@ End Class Dim compilation = CreateCompilationWithMscorlib45AndVBRuntime(source, references:={reference}) AssertTheseDiagnostics(compilation, -BC30649: '' is an unsupported type. +BC32105: Type argument 'String' does not satisfy the 'Structure' constraint for type parameter 'T'. + x.M(Of String)() + ~~~~~~~~~~~~ +BC37332: Type argument 'String' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter 'T'. x.M(Of String)() - ~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~ ) - Dim badTypeParameter = compilation.GetTypeByMetadataName("TestRef").GetMethod("M").TypeParameters.Single() - Assert.True(badTypeParameter.HasValueTypeConstraint) + Dim typeParameter = compilation.GetTypeByMetadataName("TestRef").GetMethod("M").TypeParameters.Single() + Assert.True(typeParameter.HasValueTypeConstraint) + Assert.True(typeParameter.HasUnmanagedTypeConstraint) End Sub - Public Sub UnmanagedConstraint_RejectedSymbol_OnDelegate() + Public Sub UnmanagedConstraint_OnDelegate() Dim reference = CreateCSharpCompilation(" public delegate T D() where T : unmanaged; ", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() @@ -192,25 +236,23 @@ Class Test Shared Sub Main(del As D(Of String)) End Sub End Class - + Dim compilation = CreateCompilationWithMscorlib45AndVBRuntime(source, references:={reference}) AssertTheseDiagnostics(compilation, -BC30649: '' is an unsupported type. - Shared Sub Main(del As D(Of String)) - ~~~ -BC32044: Type argument 'String' does not inherit from or implement the constraint type '?'. +BC32105: Type argument 'String' does not satisfy the 'Structure' constraint for type parameter 'T'. Shared Sub Main(del As D(Of String)) ~~~ -BC32105: Type argument 'String' does not satisfy the 'Structure' constraint for type parameter 'T'. +BC37332: Type argument 'String' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter 'T'. Shared Sub Main(del As D(Of String)) ~~~ ) - Dim badTypeParameter = compilation.GetTypeByMetadataName("D`1").TypeParameters.Single() - Assert.True(badTypeParameter.HasValueTypeConstraint) + Dim typeParameter = compilation.GetTypeByMetadataName("D`1").TypeParameters.Single() + Assert.True(typeParameter.HasValueTypeConstraint) + Assert.True(typeParameter.HasUnmanagedTypeConstraint) End Sub End Class diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/SymbolErrorTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/SymbolErrorTests.vb index 131c09d199002..e32077a0bb2bc 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/SymbolErrorTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/SymbolErrorTests.vb @@ -14655,7 +14655,7 @@ BC32081: 'New' constraint cannot be specified multiple times for the same type p Dim expectedErrors1 = CompilationUtils.AssertTheseDiagnostics(compilation1, expectedErrors1) End Sub diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/UnmanagedTypeConstraintTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/UnmanagedTypeConstraintTests.vb new file mode 100644 index 0000000000000..8730118939182 --- /dev/null +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/UnmanagedTypeConstraintTests.vb @@ -0,0 +1,1887 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports System.Runtime.InteropServices +Imports Microsoft.CodeAnalysis.CSharp +Imports Microsoft.CodeAnalysis.Test.Utilities +Imports Microsoft.CodeAnalysis.VisualBasic.Symbols +Imports Roslyn.Test.Utilities + +Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests + + Public Class UnmanagedTypeConstraintTests + Inherits BasicTestBase + + + Public Sub LoadingADifferentModifierTypeForUnmanagedConstraint() + + Dim ilSource = IsUnmanagedAttributeIL + " +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M1() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M1 + + .method public hidebysig instance void + M2() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M2 + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor + +}" + + Dim reference = CompileIL(ilSource, prependDefaultHeader:=False) + + Dim code = " +public class Test + public shared sub Main() + dim obj = new TestRef() + + obj.M1(Of Integer)() ' valid + obj.M2(Of Integer)() ' invalid + End Sub +End Class +" + + CreateCompilation(code, references:={reference}). + AssertTheseDiagnostics( +BC30649: '' is an unsupported type. + obj.M2(Of Integer)() ' invalid + ~~~~~~~~~~~~~~~~~~~~ +BC30649: 'T' is an unsupported type. + obj.M2(Of Integer)() ' invalid + ~~~~~~~~~~~~~~~~~~~~ + ) + End Sub + + + Public Sub LoadingUnmanagedTypeModifier_OptionalIsError() + + Dim ilSource = IsUnmanagedAttributeIL + " +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M1() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M1 + + .method public hidebysig instance void + M2() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M2 + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor + +}" + + Dim reference = CompileIL(ilSource, prependDefaultHeader:=False) + + Dim code = " +public class Test + public shared sub Main() + Dim obj = new TestRef() + + obj.M1(Of Integer)() ' valid + obj.M2(Of Integer)() ' invalid + end sub +end class +" + + CreateCompilation(code, references:={reference}). + AssertTheseDiagnostics( +BC30649: '' is an unsupported type. + obj.M2(Of Integer)() ' invalid + ~~~~~~~~~~~~~~~~~~~~ +BC30649: 'T' is an unsupported type. + obj.M2(Of Integer)() ' invalid + ~~~~~~~~~~~~~~~~~~~~ + ) + End Sub + + + Public Sub LoadingUnmanagedTypeModifier_MoreThanOneModifier() + + Dim ilSource = IsUnmanagedAttributeIL + " +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M1() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M1 + + .method public hidebysig instance void + M2() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M2 + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor + +}" + + Dim reference = CompileIL(ilSource, prependDefaultHeader:=False) + + Dim code = " +public class Test + public shared sub Main() + Dim obj = new TestRef() + + obj.M1(Of Integer)() ' valid + obj.M2(Of Integer)() ' invalid + end sub +end class +" + + CreateCompilation(code, references:={reference}). + AssertTheseDiagnostics( +BC30649: '' is an unsupported type. + obj.M2(Of Integer)() ' invalid + ~~~~~~~~~~~~~~~~~~~~ +BC30649: 'T' is an unsupported type. + obj.M2(Of Integer)() ' invalid + ~~~~~~~~~~~~~~~~~~~~ + ) + End Sub + + + Public Sub LoadingUnmanagedTypeModifier_ModreqWithoutAttribute() + + Dim ilSource = IsUnmanagedAttributeIL + " +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M1() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M1 + + .method public hidebysig instance void + M2() cil managed + { + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M2 + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor + +}" + + Dim reference = CompileIL(ilSource, prependDefaultHeader:=False) + + Dim code = " +public class Test + public shared sub Main() + Dim obj = new TestRef() + + obj.M1(Of Integer)() ' valid + obj.M2(Of Integer)() ' invalid + end sub +end class +" + + CreateCompilation(code, references:={reference}). + AssertTheseDiagnostics( +BC30649: 'T' is an unsupported type. + obj.M2(Of Integer)() ' invalid + ~~~~~~~~~~~~~~~~~~~~ + ) + End Sub + + + Public Sub LoadingUnmanagedTypeModifier_ModreqGeneric() + + Dim ilSource = IsUnmanagedAttributeIL + " +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M1() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M1 + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor +} + +.class public auto ansi beforefieldinit System.Runtime.InteropServices.UnmanagedType`1 + extends [mscorlib]System.Object +{ + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } +} +" + + Dim reference = CompileIL(ilSource, prependDefaultHeader:=False) + + Dim code = " +public class Test + public shared sub Main() + Dim obj = new TestRef() + + obj.M1(Of Integer)() + end sub +end class +" + + CreateCompilation(code, references:={reference}). + AssertTheseDiagnostics( +BC30649: '' is an unsupported type. + obj.M1(Of Integer)() + ~~~~~~~~~~~~~~~~~~~~ +BC30649: 'T' is an unsupported type. + obj.M1(Of Integer)() + ~~~~~~~~~~~~~~~~~~~~ + ) + End Sub + + + Public Sub LoadingUnmanagedTypeModifier_AttributeWithoutModreq() + + Dim ilSource = IsUnmanagedAttributeIL + " +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M1() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M1 + + .method public hidebysig instance void + M2() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M2 + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor + +}" + + Dim reference = CompileIL(ilSource, prependDefaultHeader:=False) + + Dim code = " +public class Test + public shared sub Main() + Dim obj = new TestRef() + + obj.M1(Of Integer)() ' valid + obj.M2(Of Integer)() ' invalid + end sub +end class +" + + CreateCompilation(code, references:={reference}). + AssertTheseDiagnostics( +BC30649: 'T' is an unsupported type. + obj.M2(Of Integer)() ' invalid + ~~~~~~~~~~~~~~~~~~~~ + ) + End Sub + + + Public Sub UnmanagedTypeModreqOnOverriddenMethod() + Dim reference = CreateCSharpCompilation(" +public class Parent +{ + public virtual string M() where T : unmanaged => ""Parent""; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Child + Inherits Parent + + public overrides Function M(Of T as Structure)() As string + Return ""Child"" + End Function +End Class +" + + Dim compilation = CreateCompilation(source, references:={reference}) + + Dim typeParameter = compilation.GetTypeByMetadataName("Parent").GetMethod("M").TypeParameters.Single() + Assert.True(typeParameter.HasValueTypeConstraint) + Assert.True(typeParameter.HasUnmanagedTypeConstraint) + + typeParameter = compilation.GetTypeByMetadataName("Child").GetMethod("M").TypeParameters.Single() + Assert.True(typeParameter.HasValueTypeConstraint) + Assert.False(typeParameter.HasUnmanagedTypeConstraint) + + AssertTheseDiagnostics(compilation, +BC32077: 'Public Overrides Function M(Of T As Structure)() As String' cannot override 'Public Overridable Overloads Function M(Of T As Structure)() As String' because they differ by type parameter constraints. + public overrides Function M(Of T as Structure)() As string + ~ + ) + End Sub + + + Public Sub UnmanagedTypeModreqOnImplementedMethod() + Dim reference = CreateCSharpCompilation(" +public interface Parent +{ + string M() where T : unmanaged; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Child + Implements Parent + + Function M(Of T as Structure)() As string Implements Parent.M + Return ""Child"" + End Function +End Class +" + + Dim compilation = CreateCompilation(source, references:={reference}) + + Dim typeParameter = compilation.GetTypeByMetadataName("Parent").GetMethod("M").TypeParameters.Single() + Assert.True(typeParameter.HasValueTypeConstraint) + Assert.True(typeParameter.HasUnmanagedTypeConstraint) + + typeParameter = compilation.GetTypeByMetadataName("Child").GetMethod("M").TypeParameters.Single() + Assert.True(typeParameter.HasValueTypeConstraint) + Assert.False(typeParameter.HasUnmanagedTypeConstraint) + + AssertTheseDiagnostics(compilation, +BC32078: 'Public Function M(Of T As Structure)() As String' cannot implement 'Parent.Function M(Of T As Structure)() As String' because they differ by type parameter constraints. + Function M(Of T as Structure)() As string Implements Parent.M + ~~~~~~~~ + ) + End Sub + + + Public Sub UnmanagedConstraintWithClassConstraint_IL() + + Dim ilSource = IsUnmanagedAttributeIL + " +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor + +}" + + Dim reference = CompileIL(ilSource, prependDefaultHeader:=False) + + Dim code = " +public class Test + public shared sub Main() + Dim obj = new TestRef() + + obj.M(Of integer)() + obj.M(Of string)() + end sub +end class +" + + CreateCompilation(code, references:={reference}). + AssertTheseDiagnostics( +BC30649: 'T' is an unsupported type. + obj.M(Of integer)() + ~~~~~~~~~~~~~~~~~~~ +BC30649: 'T' is an unsupported type. + obj.M(Of string)() + ~~~~~~~~~~~~~~~~~~ + ) + End Sub + + + Public Sub UnmanagedConstraintWithConstructorConstraint_IL() + + Dim ilSource = IsUnmanagedAttributeIL + " +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M<.ctor (class [mscorlib]System.ValueType modreq([mscorlib]System.Runtime.InteropServices.UnmanagedType)) T>() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor + +}" + + Dim reference = CompileIL(ilSource, prependDefaultHeader:=False) + + Dim code = " +public class Test + public shared sub Main() + Dim obj = new TestRef() + + obj.M(Of integer)() + obj.M(Of string)() + end sub +end class +" + + CreateCompilation(code, references:={reference}). + AssertTheseDiagnostics( +BC30649: 'T' is an unsupported type. + obj.M(Of integer)() + ~~~~~~~~~~~~~~~~~~~ +BC30649: 'T' is an unsupported type. + obj.M(Of string)() + ~~~~~~~~~~~~~~~~~~ + ) + End Sub + + + Public Sub UnmanagedConstraintWithoutValueTypeConstraint_IL() + + Dim ilSource = IsUnmanagedAttributeIL + " +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M<(class [mscorlib]System.ValueType modreq([mscorlib]System.Runtime.InteropServices.UnmanagedType)) T>() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor + +}" + + Dim reference = CompileIL(ilSource, prependDefaultHeader:=False) + + Dim code = " +public class Test + public shared sub Main() + Dim obj = new TestRef() + + obj.M(Of integer)() + obj.M(Of string)() + end sub +end class +" + + CreateCompilation(code, references:={reference}). + AssertTheseDiagnostics( +BC30649: 'T' is an unsupported type. + obj.M(Of integer)() + ~~~~~~~~~~~~~~~~~~~ +BC30649: 'T' is an unsupported type. + obj.M(Of string)() + ~~~~~~~~~~~~~~~~~~ + ) + End Sub + + + Public Sub UnmanagedConstraintWithTypeConstraint_IL() + + Dim ilSource = IsUnmanagedAttributeIL + " +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor + +}" + + Dim reference = CompileIL(ilSource, prependDefaultHeader:=False) + + Dim code = " +public class Test + public shared sub Main() + Dim obj = new TestRef() + + obj.M(Of integer)() + obj.M(Of string)() + obj.M(Of S1)() + end sub +end class + +Structure S1 +End Structure +" + + CreateCompilation(code, references:={reference}). + AssertTheseDiagnostics( +BC32105: Type argument 'String' does not satisfy the 'Structure' constraint for type parameter 'T'. + obj.M(Of string)() + ~~~~~~~~~~~~ +BC37332: Type argument 'String' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter 'T'. + obj.M(Of string)() + ~~~~~~~~~~~~ +BC32044: Type argument 'S1' does not inherit from or implement the constraint type 'IComparable'. + obj.M(Of S1)() + ~~~~~~~~ + ) + End Sub + + + Public Sub UnmanagedTypeModreqNotSet() + Dim reference = CreateCSharpCompilation(" +public interface Parent +{ + string M() where T : struct; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Child + Implements Parent + + Function M(Of T as Structure)() As string Implements Parent.M + Return ""Child"" + End Function +End Class +" + + Dim compilation = CreateCompilation(source, references:={reference}) + + Dim typeParameter = compilation.GetTypeByMetadataName("Parent").GetMethod("M").TypeParameters.Single() + Assert.True(typeParameter.HasValueTypeConstraint) + Assert.False(typeParameter.HasUnmanagedTypeConstraint) + + typeParameter = compilation.GetTypeByMetadataName("Child").GetMethod("M").TypeParameters.Single() + Assert.True(typeParameter.HasValueTypeConstraint) + Assert.False(typeParameter.HasUnmanagedTypeConstraint) + + AssertNoDiagnostics(compilation) + End Sub + + + Public Sub UnmanagedCheck_Array() + Dim reference = CreateCSharpCompilation(" +public class Test where T : unmanaged +{ +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main + Dim o As Object + o = GetType(Test(Of Integer())) + o = GetType(Test(Of Integer()())) + o = GetType(Test(Of Integer(,))) + o = GetType(Test(Of Integer)) + End Sub +End Class +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, +BC32105: Type argument 'Integer()' does not satisfy the 'Structure' constraint for type parameter 'T'. + o = GetType(Test(Of Integer())) + ~~~~~~~~~ +BC37332: Type argument 'Integer()' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter 'T'. + o = GetType(Test(Of Integer())) + ~~~~~~~~~ +BC32105: Type argument 'Integer()()' does not satisfy the 'Structure' constraint for type parameter 'T'. + o = GetType(Test(Of Integer()())) + ~~~~~~~~~~~ +BC37332: Type argument 'Integer()()' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter 'T'. + o = GetType(Test(Of Integer()())) + ~~~~~~~~~~~ +BC32105: Type argument 'Integer(*,*)' does not satisfy the 'Structure' constraint for type parameter 'T'. + o = GetType(Test(Of Integer(,))) + ~~~~~~~~~~ +BC37332: Type argument 'Integer(*,*)' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter 'T'. + o = GetType(Test(Of Integer(,))) + ~~~~~~~~~~ + ) + + compilation = CreateCompilation(source, references:={reference}, parseOptions:=TestOptions.Regular17_13) + AssertTheseDiagnostics(compilation, ) + + compilation = CreateCompilation(source, references:={reference}, parseOptions:=TestOptions.Regular16_9) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_AnonymousType() + Dim reference = CreateCSharpCompilation(" +public class Test +{ + public void M(T x) where T : unmanaged {} +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main + Dim o As New Test + o.M(new with {.A = 1}) + End Sub +End Class +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ' does not satisfy the 'Structure' constraint for type parameter 'T'. + o.M(new with {.A = 1}) + ~ +BC37332: Type argument '' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter 'T'. + o.M(new with {.A = 1}) + ~ + ]]>) + End Sub + + + Public Sub UnmanagedCheck_TypedReference() + Dim reference = CreateCSharpCompilation(" +public class Test +{ + public void M(T x) where T : unmanaged {} +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main + Dim o As New Test + o.M(CType(Nothing, System.TypedReference)) + End Sub +End Class +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_RefStruct() + Dim reference = CreateCSharpCompilation(" +public class Test +{ + public void M(T x) where T : unmanaged, allows ref struct {} + + void Tst() + { + this.M((RefS)default); + } +} + +public ref struct RefS { } +public ref struct RefG { public T field; } +public ref struct Ref { ref int field; } +public ref struct StructWithIndirectRefField +{ + public Ref Field; +} +public ref struct StructWithIndirectRefField2 +{ + public StructWithRefField Field; +} +public ref struct StructWithRefField +{ + public ref T RefField; +} + +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest), referencedAssemblies:=Basic.Reference.Assemblies.Net90.References.All).EmitToImageReference() + + Dim source = " +public class Program + + Shared Sub Main + Dim o As New Test + o.M(CType(Nothing, RefS)) + o.M(CType(Nothing, RefG(Of String))) + o.M(CType(Nothing, Ref)) + o.M(CType(Nothing, StructWithIndirectRefField)) + o.M(CType(Nothing, StructWithIndirectRefField2)) + End Sub +End Class +" + + Dim compilation = CreateCompilation(source, references:={reference}, targetFramework:=TargetFramework.Net90) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_Class() + Dim reference = CreateCSharpCompilation(" +public class Test +{ + public void M(T x) where T : unmanaged {} +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main + Dim o As New Test + o.M(New Program()) + End Sub +End Class +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_StructWithManagedField() + Dim reference = CreateCSharpCompilation(" +public class Test +{ + public void M(T x) where T : unmanaged {} +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main + Dim o As New Test + o.M(New S1()) + o.M(New S2()) + o.M(New S3()) + o.M(New S4(Of Integer)()) + o.M(New S4(Of Program)()) + o.M(New S5(Of Program)()) + End Sub +End Class + +Structure S1 + Dim F as Program +End Structure + +Structure S2 + Dim F as Integer +End Structure + +Structure S3 + Shared F as Program +End Structure + +Structure S4(Of T) + Dim F as T +End Structure + +Structure S5(Of T) + Dim F as Integer +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + + Dim s1 = compilation.GetTypeByMetadataName("S1") + Dim s2 = compilation.GetTypeByMetadataName("S2") + Dim s3 = compilation.GetTypeByMetadataName("S3") + Dim s4 = compilation.GetTypeByMetadataName("S4`1") + Dim s5 = compilation.GetTypeByMetadataName("S5`1") + Assert.Equal(ManagedKind.Managed, s1.GetManagedKind(Nothing)) + Assert.Equal(ManagedKind.Unmanaged, s2.GetManagedKind(Nothing)) + Assert.Equal(ManagedKind.Unmanaged, s3.GetManagedKind(Nothing)) + Assert.Equal(ManagedKind.Managed, s4.GetManagedKind(Nothing)) + Assert.Equal(ManagedKind.UnmanagedWithGenerics, s5.GetManagedKind(Nothing)) + + Assert.False(DirectCast(s1, INamedTypeSymbol).IsUnmanagedType) + Assert.True(DirectCast(s2, INamedTypeSymbol).IsUnmanagedType) + Assert.True(DirectCast(s3, INamedTypeSymbol).IsUnmanagedType) + Assert.False(DirectCast(s4, INamedTypeSymbol).IsUnmanagedType) + Assert.True(DirectCast(s5, INamedTypeSymbol).IsUnmanagedType) + + Dim s5T = s5.TypeParameters(0) + Assert.Equal(ManagedKind.Managed, s5T.GetManagedKind(Nothing)) + Assert.False(DirectCast(s5T, ITypeSymbol).IsUnmanagedType) + Assert.False(s5T.HasUnmanagedTypeConstraint) + Assert.False(DirectCast(s5T, ITypeParameterSymbol).HasUnmanagedTypeConstraint) + + Dim mT = compilation.GetTypeByMetadataName("Test").GetMember(Of MethodSymbol)("M").TypeParameters(0) + Assert.Equal(ManagedKind.Unmanaged, mT.GetManagedKind(Nothing)) + Assert.True(DirectCast(mT, ITypeSymbol).IsUnmanagedType) + Assert.True(mT.HasUnmanagedTypeConstraint) + Assert.True(DirectCast(mT, ITypeParameterSymbol).HasUnmanagedTypeConstraint) + End Sub + + + Public Sub UnmanagedCheck_StructWithManagedProperty() + Dim reference = CreateCSharpCompilation(" +public class Test +{ + public void M(T x) where T : unmanaged {} +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main + Dim o As New Test + o.M(New S1()) + o.M(New S2()) + End Sub +End Class + +Structure S1 + Property F as Program +End Structure + +Structure S2 + Property F as Integer +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_StructWithEvent() + Dim reference = CreateCSharpCompilation(" +public class Test +{ + public void M(T x) where T : unmanaged {} +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main + Dim o As New Test + o.M(New S1()) + End Sub +End Class + +Structure S1 + Event E as System.Action +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_StructWithCycle() + Dim reference = CreateCSharpCompilation(" +public class Test +{ + public void M(T x) where T : unmanaged {} +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main + Dim o As New Test + o.M(New S1()) + o.M(New S2()) + End Sub +End Class + +Structure S1 + Dim F as S2 +End Structure + +Structure S2 + Dim F as S1 +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_Tuple() + Dim reference = CreateCSharpCompilation(" +public class Test +{ + public void M(T x) where T : unmanaged {} +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main(Of T)(x As T) + Dim o As New Test + o.M((1, 1)) + o.M((1, x)) + o.M((1, ""s"")) + o.M((1, 2, 3, 4, 5, 6, 7, 8, ""s"")) + o.M(new S1()) + o.M(new S2()) + o.M(new S3()) + o.M(new S4(Of T)()) + End Sub +End Class + +Structure S1 + Dim F as (Integer, Integer) +End Structure + +Structure S2 + Dim F as (String, Integer) +End Structure + +Structure S3 + Dim F as (Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, String) +End Structure + +Structure S4(Of T) + Dim F as (T, Integer) +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_Enum() + Dim reference = CreateCSharpCompilation(" +public class Test +{ + public void M(T x) where T : unmanaged {} +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main() + Dim o As New Test + o.M(E1.Val) + o.M(new S1()) + End Sub +End Class + +Enum E1 + Val +End Enum + +Structure S1 + Dim F as E1 +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + compilation.AssertNoDiagnostics() + End Sub + + + Public Sub UnmanagedCheck_Interface() + Dim reference = CreateCSharpCompilation(" +public class Test +{ + public void M(T x) where T : unmanaged {} +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main(x As I1) + Dim o As New Test + o.M(x) + o.M(New S2()) + End Sub +End Class + +Public Interface I1 +End Interface + +Structure S2 + Dim F as I1 +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_Pointer() + Dim reference = CreateCSharpCompilation(" +public class Test +{ + public void M(T x) where T : unmanaged {} + + public static S1 GetS1() => default; + public static S2 GetS2() => default; +} + +unsafe public struct S1 +{ + int* P1; +} + +unsafe public struct S2 +{ + delegate* P1; +} +", + parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest), + compilationOptions:=New CSharp.CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary).WithAllowUnsafe(True)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main() + Dim o As New Test + o.M(New S1()) + o.M(New S2()) + o.M(Test.GetS1()) + o.M(Test.GetS2()) + End Sub +End Class +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_ExpandingTypeArgument_01() + Dim reference = CreateCSharpCompilation(" +public struct YourStruct where T : unmanaged +{ + public T field; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +Structure MyStruct(Of T) + Dim field as YourStruct(Of MyStruct(Of MyStruct(Of T))) + Dim s as string +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_ExpandingTypeArgument_02() + Dim reference = CreateCSharpCompilation(" +public struct YourStruct where T : unmanaged +{ + public T field; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +Structure MyStruct(Of T) + Dim s as string + Dim field as YourStruct(Of MyStruct(Of MyStruct(Of T))) +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_ExpandingTypeArgument_03() + Dim reference = CreateCSharpCompilation(" +public struct YourStruct where T : unmanaged +{ + public T field; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +Structure MyStruct(Of T) + Property field as YourStruct(Of MyStruct(Of MyStruct(Of T))) + Dim s as string +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_ExpandingTypeArgument_04() + Dim reference = CreateCSharpCompilation(" +public struct YourStruct where T : unmanaged +{ + public T field; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +Structure MyStruct(Of T) + Dim s as string + Property field as YourStruct(Of MyStruct(Of MyStruct(Of T))) +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_ExpandingTypeArgument_05() + Dim reference = CreateCSharpCompilation(" +public struct YourStruct where T : unmanaged +{ + public T field; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +Structure MyStruct(Of T) + Event field as YourStruct(Of MyStruct(Of MyStruct(Of T))) + Dim s as string +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_ExpandingTypeArgument_06() + Dim reference = CreateCSharpCompilation(" +public struct YourStruct where T : unmanaged +{ + public T field; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +Structure MyStruct(Of T) + Dim s as string + Event field as YourStruct(Of MyStruct(Of MyStruct(Of T))) +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_ExpandingTypeArgument_07() + Dim reference = CreateCSharpCompilation(" +public struct YourStruct where T : unmanaged +{ + public T field; + string s; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +Structure MyStruct(Of T) + Dim field as YourStruct(Of MyStruct(Of MyStruct(Of T))) +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_ExpandingTypeArgument_08() + Dim reference = CreateCSharpCompilation(" +public struct YourStruct where T : unmanaged +{ + string s; + public T field; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +Structure MyStruct(Of T) + Dim field as YourStruct(Of MyStruct(Of MyStruct(Of T))) +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_ExpandingTypeArgument_09() + Dim reference = CreateCSharpCompilation(" +public struct YourStruct where T : unmanaged +{ + public T field; + string s; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +Structure MyStruct(Of T) + Property field as YourStruct(Of MyStruct(Of MyStruct(Of T))) +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_ExpandingTypeArgument_10() + Dim reference = CreateCSharpCompilation(" +public struct YourStruct where T : unmanaged +{ + string s; + public T field; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +Structure MyStruct(Of T) + Property field as YourStruct(Of MyStruct(Of MyStruct(Of T))) +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_ExpandingTypeArgument_11() + Dim reference = CreateCSharpCompilation(" +public struct YourStruct where T : unmanaged +{ + public T field; + string s; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +Structure MyStruct(Of T) + Event field as YourStruct(Of MyStruct(Of MyStruct(Of T))) +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_ExpandingTypeArgument_12() + Dim reference = CreateCSharpCompilation(" +public struct YourStruct where T : unmanaged +{ + string s; + public T field; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +Structure MyStruct(Of T) + Event field as YourStruct(Of MyStruct(Of MyStruct(Of T))) +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_TypeParameter() + Dim reference = CreateCSharpCompilation(" +public class Test +{ + public void M(T x) where T : unmanaged {} +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main(Of T1, T2 As Class, T3 As Structure)(x1 as T1, x2 As T2, x3 As T3) + Dim o As New Test + o.M(x1) + o.M(x2) + o.M(x3) + End Sub +End Class +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub ConsumeAssertEqual() + Dim reference = CreateCSharpCompilation(" +using System; + +public class Assert +{ + public static void Equal( + T[] expected, + T[] actual) + where T : unmanaged, IEquatable + { + System.Console.Write(""T[]""); + } + + public static void Equal( + T expected, + T actual) + { + System.Console.Write(""T""); + } +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main + Assert.Equal(New Integer() {1, 2}, New Integer() {1, 2}) + Assert.Equal(New String() {1, 2}, New String() {1, 2}) + End Sub +End Class +" + + Dim compilation = CreateCompilation(source, references:={reference}, options:=TestOptions.DebugExe) + CompileAndVerify(compilation, expectedOutput:="T[]T").VerifyDiagnostics() + + compilation = CreateCompilation(source, references:={reference}, options:=TestOptions.DebugExe, parseOptions:=TestOptions.Regular17_13) + CompileAndVerify(compilation, expectedOutput:="T[]T").VerifyDiagnostics() + + compilation = CreateCompilation(source, references:={reference}, options:=TestOptions.DebugExe, parseOptions:=TestOptions.Regular16_9) + compilation.AssertTheseDiagnostics( +BC36716: Visual Basic 16.9 does not support recognizing 'unmanaged' constraint. + Assert.Equal(New Integer() {1, 2}, New Integer() {1, 2}) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +BC36716: Visual Basic 16.9 does not support recognizing 'unmanaged' constraint. + Assert.Equal(New String() {1, 2}, New String() {1, 2}) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ) + End Sub + + + Public Sub ConsumeExtensionMethod() + Dim reference = CreateCSharpCompilation(" +using System; + +public static class Assert +{ + public static void Equal( + this T[] expected, + T[] actual) + where T : unmanaged, IEquatable + { + System.Console.Write(""T[]""); + } + + public static void Equal( + this T expected, + T actual) + { + System.Console.Write(""T""); + } +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main + Call New Integer() {1, 2}.Equal(New Integer() {1, 2}) + Call New String() {1, 2}.Equal(New String() {1, 2}) + End Sub +End Class +" + + Dim compilation = CreateCompilation(source, references:={reference}, options:=TestOptions.DebugExe) + CompileAndVerify(compilation, expectedOutput:="T[]T").VerifyDiagnostics() + + compilation = CreateCompilation(source, references:={reference}, options:=TestOptions.DebugExe, parseOptions:=TestOptions.Regular17_13) + CompileAndVerify(compilation, expectedOutput:="T[]T").VerifyDiagnostics() + + compilation = CreateCompilation(source, references:={reference}, options:=TestOptions.DebugExe, parseOptions:=TestOptions.Regular16_9) + CompileAndVerify(compilation, expectedOutput:="TT").VerifyDiagnostics() + End Sub + + Private Const IsUnmanagedAttributeIL As String = " +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) + .ver 4:0:0:0 +} +.assembly Test +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module Test.dll +.imagebase 0x10000000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 +.corflags 0x00000001 + +.class private auto ansi sealed beforefieldinit System.Runtime.CompilerServices.IsUnmanagedAttribute + extends [mscorlib]System.Attribute +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Attribute::.ctor() + IL_0006: nop + IL_0007: ret + } +} +" + + End Class +End Namespace diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/Symbols/EETypeParameterSymbol.vb b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/Symbols/EETypeParameterSymbol.vb index 55b4f53ca80d4..a2de7b87fbc9a 100644 --- a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/Symbols/EETypeParameterSymbol.vb +++ b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/Symbols/EETypeParameterSymbol.vb @@ -118,5 +118,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator Return InternalSubstituteTypeParametersDistinct(substitution, constraintTypes) End Get End Property + + Friend Overrides ReadOnly Property HasUnmanagedTypeConstraint As Boolean + Get + Return _sourceTypeParameterSymbol.HasUnmanagedTypeConstraint + End Get + End Property End Class End Namespace diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/Symbols/SimpleTypeParameterSymbol.vb b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/Symbols/SimpleTypeParameterSymbol.vb index 81adcbcdf74f0..c72350f30684f 100644 --- a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/Symbols/SimpleTypeParameterSymbol.vb +++ b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/Symbols/SimpleTypeParameterSymbol.vb @@ -88,6 +88,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator End Get End Property + Friend Overrides ReadOnly Property HasUnmanagedTypeConstraint As Boolean + Get + Return False + End Get + End Property + Public Overrides ReadOnly Property Locations As ImmutableArray(Of Location) Get Throw ExceptionUtilities.Unreachable From 4efe5f09a7a3c6c3d22cffa8c50d600214e29b37 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 5 Nov 2024 18:50:11 -0800 Subject: [PATCH 186/508] Don't offer 'convert anon type to tuple' when in an expression tree --- ...ymousTypeToTupleCodeRefactoringProvider.cs | 10 +++------- .../ConvertAnonymousTypeToTupleTests.cs | 20 ++++++++++++++++++- ...ymousTypeToTupleCodeRefactoringProvider.cs | 5 +++++ 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/Features/CSharp/Portable/ConvertAnonymousType/CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertAnonymousType/CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider.cs index cf3d02bb5eee2..884b529a0f0a0 100644 --- a/src/Features/CSharp/Portable/ConvertAnonymousType/CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertAnonymousType/CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider.cs @@ -16,18 +16,14 @@ namespace Microsoft.CodeAnalysis.CSharp.ConvertAnonymousType; using static SyntaxFactory; [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.ConvertAnonymousTypeToTuple), Shared] -internal class CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider +[method: ImportingConstructor] +[method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] +internal sealed class CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider() : AbstractConvertAnonymousTypeToTupleCodeRefactoringProvider< ExpressionSyntax, TupleExpressionSyntax, AnonymousObjectCreationExpressionSyntax> { - [ImportingConstructor] - [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] - public CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider() - { - } - protected override int GetInitializerCount(AnonymousObjectCreationExpressionSyntax anonymousType) => anonymousType.Initializers.Count; diff --git a/src/Features/CSharpTest/ConvertAnonymousType/ConvertAnonymousTypeToTupleTests.cs b/src/Features/CSharpTest/ConvertAnonymousType/ConvertAnonymousTypeToTupleTests.cs index a0caafd2247db..dd7360e049644 100644 --- a/src/Features/CSharpTest/ConvertAnonymousType/ConvertAnonymousTypeToTupleTests.cs +++ b/src/Features/CSharpTest/ConvertAnonymousType/ConvertAnonymousTypeToTupleTests.cs @@ -9,12 +9,13 @@ using Microsoft.CodeAnalysis.CSharp.ConvertAnonymousType; using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings; using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertAnonymousType; [Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)] -public partial class ConvertAnonymousTypeToTupleTests : AbstractCSharpCodeActionTest_NoEditor +public sealed class ConvertAnonymousTypeToTupleTests : AbstractCSharpCodeActionTest_NoEditor { protected override CodeRefactoringProvider CreateCodeRefactoringProvider(TestWorkspace workspace, TestParameters parameters) => new CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider(); @@ -483,4 +484,21 @@ void Method() """; await TestInRegularAndScriptAsync(text, expected); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/34749")] + public async Task NotInExpressionTree() + { + await TestMissingInRegularAndScriptAsync(""" + using System.Linq.Expressions; + + class C + { + static void Main(string[] args) + { + Expression> test = + (par1, par2) => [||]new { Parameter1 = par1, Parameter2 = par2 }; + } + } + """); + } } diff --git a/src/Features/Core/Portable/ConvertAnonymousType/AbstractConvertAnonymousTypeToTupleCodeRefactoringProvider.cs b/src/Features/Core/Portable/ConvertAnonymousType/AbstractConvertAnonymousTypeToTupleCodeRefactoringProvider.cs index 5864f837f4bdb..42784975f2e1e 100644 --- a/src/Features/Core/Portable/ConvertAnonymousType/AbstractConvertAnonymousTypeToTupleCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/ConvertAnonymousType/AbstractConvertAnonymousTypeToTupleCodeRefactoringProvider.cs @@ -42,6 +42,11 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte return; var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var semanticFacts = document.GetRequiredLanguageService(); + + if (semanticFacts.IsInExpressionTree(semanticModel, anonymousNode, semanticModel.Compilation.ExpressionOfTType(), cancellationToken)) + return; + var allAnonymousNodes = GetAllAnonymousTypesInContainer(document, semanticModel, anonymousNode, cancellationToken); // If we have multiple different anonymous types in this member, then offer two fixes, one to just fixup this From 77df314dbde365976afa610f9375be8ad5994d45 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 5 Nov 2024 19:43:33 -0800 Subject: [PATCH 187/508] Fix issue with 'convert to conditional expression' and the suppression operator --- .../UseConditionalExpressionForReturnTests.cs | 39 ++++++++++++++++--- ...UseConditionalExpressionCodeFixProvider.cs | 8 +++- .../Services/SyntaxFacts/CSharpSyntaxKinds.cs | 1 + .../Core/Services/SyntaxFacts/ISyntaxKinds.cs | 1 + .../SyntaxFacts/VisualBasicSyntaxKinds.vb | 1 + 5 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForReturnTests.cs b/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForReturnTests.cs index fa03b73ac9ba4..e942959c3fbab 100644 --- a/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForReturnTests.cs +++ b/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForReturnTests.cs @@ -16,16 +16,12 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseConditionalExpression; [Trait(Traits.Feature, Traits.Features.CodeActionsUseConditionalExpression)] -public partial class UseConditionalExpressionForReturnTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_NoEditor +public sealed class UseConditionalExpressionForReturnTests(ITestOutputHelper logger) + : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_NoEditor(logger) { private static readonly ParseOptions CSharp8 = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp8); private static readonly ParseOptions CSharp9 = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9); - public UseConditionalExpressionForReturnTests(ITestOutputHelper logger) - : base(logger) - { - } - internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) => (new CSharpUseConditionalExpressionForReturnDiagnosticAnalyzer(), new CSharpUseConditionalExpressionForReturnCodeFixProvider()); @@ -2281,4 +2277,35 @@ private bool AreSimilarCore(string node1, string node2) } """, title: AnalyzersResources.Simplify_check); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38879")] + public async Task TesSuppressionOperator() + { + await TestInRegularAndScript1Async(""" + #nullable enable + + class Program + { + public static string Method(bool empty) + { + [||]if (empty) + { + return string.Empty; + } + + return null!; + } + } + """, """ + #nullable enable + + class Program + { + public static string Method(bool empty) + { + return empty ? string.Empty : null!; + } + } + """); + } } diff --git a/src/Analyzers/Core/CodeFixes/UseConditionalExpression/AbstractUseConditionalExpressionCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseConditionalExpression/AbstractUseConditionalExpressionCodeFixProvider.cs index fcba559fe08f2..30b983f726ef0 100644 --- a/src/Analyzers/Core/CodeFixes/UseConditionalExpression/AbstractUseConditionalExpressionCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseConditionalExpression/AbstractUseConditionalExpressionCodeFixProvider.cs @@ -5,7 +5,6 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; @@ -161,7 +160,12 @@ private TExpressionSyntax CastValueIfNecessary( if (statement is IThrowOperation throwOperation) return ConvertToExpression(throwOperation); - var sourceSyntax = value.Syntax.WithoutTrivia(); + var suppressKind = this.SyntaxFacts.SyntaxKinds.SuppressNullableWarningExpression; + var sourceSyntax = value.Syntax; + while (sourceSyntax is { Parent.RawKind: var kind } && kind == suppressKind) + sourceSyntax = sourceSyntax.Parent; + + sourceSyntax = sourceSyntax.WithoutTrivia(); // If there was an implicit conversion generated by the compiler, then convert that to an // explicit conversion inside the condition. This is needed as there is no type diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs index 3aefcfa76563a..0847a5e73c5f6 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs @@ -112,6 +112,7 @@ public int Convert(TSyntaxKind kind) where TSyntaxKind : struct public int ReferenceEqualsExpression => (int)SyntaxKind.EqualsExpression; public int ReferenceNotEqualsExpression => (int)SyntaxKind.NotEqualsExpression; public int SimpleMemberAccessExpression => (int)SyntaxKind.SimpleMemberAccessExpression; + public int? SuppressNullableWarningExpression => (int)SyntaxKind.SuppressNullableWarningExpression; public int TernaryConditionalExpression => (int)SyntaxKind.ConditionalExpression; public int ThisExpression => (int)SyntaxKind.ThisExpression; public int? ThrowExpression => (int)SyntaxKind.ThrowExpression; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs index ddaf52a46398e..33878d5faf1fb 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs @@ -155,6 +155,7 @@ internal interface ISyntaxKinds int ReferenceEqualsExpression { get; } int ReferenceNotEqualsExpression { get; } int SimpleMemberAccessExpression { get; } + int? SuppressNullableWarningExpression { get; } int TernaryConditionalExpression { get; } int ThisExpression { get; } int? ThrowExpression { get; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb index b6de084b165a3..a1275626184c2 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb @@ -114,6 +114,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Public ReadOnly Property ReferenceEqualsExpression As Integer = SyntaxKind.IsExpression Implements ISyntaxKinds.ReferenceEqualsExpression Public ReadOnly Property ReferenceNotEqualsExpression As Integer = SyntaxKind.IsNotExpression Implements ISyntaxKinds.ReferenceNotEqualsExpression Public ReadOnly Property SimpleMemberAccessExpression As Integer = SyntaxKind.SimpleMemberAccessExpression Implements ISyntaxKinds.SimpleMemberAccessExpression + Public ReadOnly Property SuppressNullableWarningExpression As Integer? Implements ISyntaxKinds.SuppressNullableWarningExpression Public ReadOnly Property TernaryConditionalExpression As Integer = SyntaxKind.TernaryConditionalExpression Implements ISyntaxKinds.TernaryConditionalExpression Public ReadOnly Property ThisExpression As Integer = SyntaxKind.MeExpression Implements ISyntaxKinds.ThisExpression Public ReadOnly Property ThrowExpression As Integer? Implements ISyntaxKinds.ThrowExpression From 9fb065874ef45ef93403eb44fa88a9c7cdf4cd52 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 5 Nov 2024 20:25:31 -0800 Subject: [PATCH 188/508] Classify 'await' as a control keyword --- .../SyntacticClassifierTests.cs | 40 +++++++++++ .../Classification/AbstractClassifierTests.cs | 4 +- .../Classification/ClassificationHelpers.cs | 71 +++++++++---------- 3 files changed, 76 insertions(+), 39 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs b/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs index 2fbe62118fa0f..ab315ace15c42 100644 --- a/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs +++ b/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs @@ -257,6 +257,46 @@ await TestInMethodAsync( Punctuation.CloseCurly); } + [Theory, CombinatorialData] + [WorkItem("https://github.com/dotnet/roslyn/issues/40741")] + public async Task TestAwait(TestHost testHost) + { + await TestAsync( +@"using System.Threading.Tasks; + +class X +{ + async Task M() + { + await M(); + } +}", + testHost, + Keyword("using"), + Identifier("System"), + Operators.Dot, + Identifier("Threading"), + Operators.Dot, + Identifier("Tasks"), + Punctuation.Semicolon, + Keyword("class"), + Class("X"), + Punctuation.OpenCurly, + Keyword("async"), + Identifier("Task"), + Method("M"), + Punctuation.OpenParen, + Punctuation.CloseParen, + Punctuation.OpenCurly, + ControlKeyword("await"), + Identifier("M"), + Punctuation.OpenParen, + Punctuation.CloseParen, + Punctuation.Semicolon, + Punctuation.CloseCurly, + Punctuation.CloseCurly); + } + [Theory, CombinatorialData] public async Task PartialClass(TestHost testHost) { diff --git a/src/EditorFeatures/TestUtilities/Classification/AbstractClassifierTests.cs b/src/EditorFeatures/TestUtilities/Classification/AbstractClassifierTests.cs index cad3d31dd1edb..6e9f9b7b8dcee 100644 --- a/src/EditorFeatures/TestUtilities/Classification/AbstractClassifierTests.cs +++ b/src/EditorFeatures/TestUtilities/Classification/AbstractClassifierTests.cs @@ -64,8 +64,8 @@ protected async Task TestAsync( var actualOrdered = actual.OrderBy((t1, t2) => t1.TextSpan.Start - t2.TextSpan.Start); - var actualFormatted = actualOrdered.Select(a => new FormattedClassification(allCode.Substring(a.TextSpan.Start, a.TextSpan.Length), a.ClassificationType)); - AssertEx.Equal(expected, actualFormatted); + var actualFormatted = actualOrdered.SelectAsArray(a => new FormattedClassification(allCode.Substring(a.TextSpan.Start, a.TextSpan.Length), a.ClassificationType)); + AssertEx.Equal(expected.ToImmutableArray(), actualFormatted); } private async Task TestAsync( diff --git a/src/Workspaces/CSharp/Portable/Classification/ClassificationHelpers.cs b/src/Workspaces/CSharp/Portable/Classification/ClassificationHelpers.cs index 2ab98f79a04e1..f55c86feaaedc 100644 --- a/src/Workspaces/CSharp/Portable/Classification/ClassificationHelpers.cs +++ b/src/Workspaces/CSharp/Portable/Classification/ClassificationHelpers.cs @@ -59,39 +59,35 @@ internal static class ClassificationHelpers } private static bool IsControlKeyword(SyntaxToken token) - { - if (token.Parent is null || !IsControlKeywordKind(token.Kind())) - { - return false; - } - - return IsControlStatementKind(token.Parent.Kind()); - } + => token.Parent is not null && + IsControlKeywordKind(token.Kind()) && + IsControlStatementKind(token.Parent.Kind()); private static bool IsControlKeywordKind(SyntaxKind kind) { switch (kind) { - case SyntaxKind.IfKeyword: - case SyntaxKind.ElseKeyword: - case SyntaxKind.WhileKeyword: - case SyntaxKind.ForKeyword: - case SyntaxKind.ForEachKeyword: - case SyntaxKind.DoKeyword: - case SyntaxKind.SwitchKeyword: + case SyntaxKind.AwaitKeyword: + case SyntaxKind.BreakKeyword: case SyntaxKind.CaseKeyword: - case SyntaxKind.TryKeyword: case SyntaxKind.CatchKeyword: + case SyntaxKind.ContinueKeyword: + case SyntaxKind.DefaultKeyword: // Include DefaultKeyword as it can be part of a DefaultSwitchLabel + case SyntaxKind.DoKeyword: + case SyntaxKind.ElseKeyword: case SyntaxKind.FinallyKeyword: + case SyntaxKind.ForEachKeyword: + case SyntaxKind.ForKeyword: case SyntaxKind.GotoKeyword: - case SyntaxKind.BreakKeyword: - case SyntaxKind.ContinueKeyword: + case SyntaxKind.IfKeyword: + case SyntaxKind.InKeyword: // Include InKeyword as it can be part of an ForEachStatement case SyntaxKind.ReturnKeyword: + case SyntaxKind.SwitchKeyword: case SyntaxKind.ThrowKeyword: - case SyntaxKind.YieldKeyword: - case SyntaxKind.DefaultKeyword: // Include DefaultKeyword as it can be part of a DefaultSwitchLabel - case SyntaxKind.InKeyword: // Include InKeyword as it can be part of an ForEachStatement + case SyntaxKind.TryKeyword: case SyntaxKind.WhenKeyword: // Include WhenKeyword as it can be part of a CatchFilterClause or a pattern WhenClause + case SyntaxKind.WhileKeyword: + case SyntaxKind.YieldKeyword: return true; default: return false; @@ -103,34 +99,35 @@ private static bool IsControlStatementKind(SyntaxKind kind) switch (kind) { // Jump Statements - case SyntaxKind.GotoStatement: - case SyntaxKind.GotoCaseStatement: - case SyntaxKind.GotoDefaultStatement: case SyntaxKind.BreakStatement: case SyntaxKind.ContinueStatement: - case SyntaxKind.ReturnStatement: - case SyntaxKind.YieldReturnStatement: - case SyntaxKind.YieldBreakStatement: - case SyntaxKind.ThrowStatement: - case SyntaxKind.WhileStatement: case SyntaxKind.DoStatement: - case SyntaxKind.ForStatement: case SyntaxKind.ForEachStatement: case SyntaxKind.ForEachVariableStatement: + case SyntaxKind.ForStatement: + case SyntaxKind.GotoCaseStatement: + case SyntaxKind.GotoDefaultStatement: + case SyntaxKind.GotoStatement: + case SyntaxKind.ReturnStatement: + case SyntaxKind.ThrowStatement: + case SyntaxKind.WhileStatement: + case SyntaxKind.YieldBreakStatement: + case SyntaxKind.YieldReturnStatement: // Checked Statements - case SyntaxKind.IfStatement: - case SyntaxKind.ElseClause: - case SyntaxKind.SwitchStatement: - case SyntaxKind.SwitchSection: - case SyntaxKind.CaseSwitchLabel: + case SyntaxKind.AwaitExpression: case SyntaxKind.CasePatternSwitchLabel: - case SyntaxKind.DefaultSwitchLabel: - case SyntaxKind.TryStatement: + case SyntaxKind.CaseSwitchLabel: case SyntaxKind.CatchClause: case SyntaxKind.CatchFilterClause: + case SyntaxKind.DefaultSwitchLabel: + case SyntaxKind.ElseClause: case SyntaxKind.FinallyClause: + case SyntaxKind.IfStatement: case SyntaxKind.SwitchExpression: + case SyntaxKind.SwitchSection: + case SyntaxKind.SwitchStatement: case SyntaxKind.ThrowExpression: + case SyntaxKind.TryStatement: case SyntaxKind.WhenClause: return true; default: From b532b1c4060b99e7e91e1aa3ac8823a028bf0cad Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 5 Nov 2024 20:27:31 -0800 Subject: [PATCH 189/508] use raw strings --- .../SyntacticClassifierTests.cs | 2666 ++++++++++------- 1 file changed, 1535 insertions(+), 1131 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs b/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs index ab315ace15c42..e046229974102 100644 --- a/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs +++ b/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs @@ -16,7 +16,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Classification; -public partial class SyntacticClassifierTests : AbstractCSharpClassifierTests +public sealed partial class SyntacticClassifierTests : AbstractCSharpClassifierTests { protected override async Task> GetClassificationSpansAsync(string code, ImmutableArray spans, ParseOptions? options, TestHost testHost) { @@ -30,9 +30,11 @@ protected override async Task> GetClassificationS public async Task VarAtTypeMemberLevel(TestHost testHost) { await TestAsync( -@"class C -{ - var goo }", + """ + class C + { + var goo } + """, testHost, Keyword("class"), Class("C"), @@ -46,9 +48,11 @@ await TestAsync( public async Task TestNamespace(TestHost testHost) { await TestAsync( -@"namespace N -{ -}", + """ + namespace N + { + } + """, testHost, Keyword("namespace"), Namespace("N"), @@ -60,8 +64,10 @@ await TestAsync( public async Task TestFileScopedNamespace(TestHost testHost) { await TestAsync( -@"namespace N; -", + """ + namespace N; + + """, testHost, Keyword("namespace"), Namespace("N"), @@ -117,12 +123,14 @@ await TestInMethodAsync( public async Task VarAsMethodParameter(TestHost testHost) { await TestAsync( -@"class C -{ - void M(var v) - { - } -}", + """ + class C + { + void M(var v) + { + } + } + """, testHost, Keyword("class"), Class("C"), @@ -142,16 +150,18 @@ void M(var v) public async Task YieldYield(TestHost testHost) { await TestAsync( -@"using System.Collections.Generic; + """ + using System.Collections.Generic; -class yield -{ - IEnumerable M() - { - yield yield = new yield(); - yield return yield; - } -}", + class yield + { + IEnumerable M() + { + yield yield = new yield(); + yield return yield; + } + } + """, testHost, Keyword("using"), Identifier("System"), @@ -191,16 +201,18 @@ IEnumerable M() public async Task YieldYieldAsSpans(TestHost testHost) { await TestAsync( -@"using System.Collections.Generic; + """ + using System.Collections.Generic; -class yield -{ - IEnumerable M() - { - [|yield yield = new yield();|] - [|yield return yield;|] - } -}", + class yield + { + IEnumerable M() + { + [|yield yield = new yield();|] + [|yield return yield;|] + } + } + """, testHost, Identifier("yield"), Local("yield"), @@ -230,8 +242,10 @@ public async Task YieldReturn(TestHost testHost) public async Task YieldFixed(TestHost testHost) { await TestInMethodAsync( -@"yield return this.items[0]; yield break; fixed (int* i = 0) { -}", + """ + yield return this.items[0]; yield break; fixed (int* i = 0) { + } + """, testHost, ControlKeyword("yield"), ControlKeyword("return"), @@ -262,15 +276,17 @@ await TestInMethodAsync( public async Task TestAwait(TestHost testHost) { await TestAsync( -@"using System.Threading.Tasks; + """ + using System.Threading.Tasks; -class X -{ - async Task M() - { - await M(); - } -}", + class X + { + async Task M() + { + await M(); + } + } + """, testHost, Keyword("using"), Identifier("System"), @@ -312,9 +328,11 @@ await TestAsync("public partial class Goo", public async Task PartialMethod(TestHost testHost) { await TestInClassAsync( -@"public partial void M() -{ -}", + """ + public partial void M() + { + } + """, testHost, Keyword("public"), Keyword("partial"), @@ -347,17 +365,19 @@ await TestInMethodAsync( public async Task PartialClassStructInterface(TestHost testHost) { await TestAsync( -@"partial class T1 -{ -} + """ + partial class T1 + { + } -partial struct T2 -{ -} + partial struct T2 + { + } -partial interface T3 -{ -}", + partial interface T3 + { + } + """, testHost, Keyword("partial"), Keyword("class"), @@ -396,9 +416,13 @@ await TestInNamespaceAsync(kw + " goo", [Theory, CombinatorialData] public async Task VerbatimStringLiterals1(TestHost testHost) { - await TestInMethodAsync(@"@""goo""", + await TestInMethodAsync(""" + @"goo" + """, testHost, - Verbatim(@"@""goo""")); + Verbatim(""" + @"goo" + """)); } [Theory, CombinatorialData] @@ -406,7 +430,9 @@ public async Task VerbatimStringLiteralsUtf8_01(TestHost testHost) { await TestInMethodAsync(@"@""goo""u8", testHost, - Verbatim(@"@""goo"""), + Verbatim(""" + @"goo" + """), Keyword("u8")); } @@ -415,7 +441,9 @@ public async Task VerbatimStringLiteralsUtf8_02(TestHost testHost) { await TestInMethodAsync(@"@""goo""U8", testHost, - Verbatim(@"@""goo"""), + Verbatim(""" + @"goo" + """), Keyword("U8")); } @@ -425,9 +453,13 @@ await TestInMethodAsync(@"@""goo""U8", [Theory, CombinatorialData] public async Task VerbatimStringLiterals2(TestHost testHost) { - await TestAsync(@"@""", + await TestAsync(""" + @" + """, testHost, - Verbatim(@"@""")); + Verbatim(""" + @" + """)); } /// @@ -436,10 +468,14 @@ await TestAsync(@"@""", [Theory, CombinatorialData] public async Task VerbatimStringLiterals3(TestHost testHost) { - await TestAsync(@"goo @""", + await TestAsync(""" + goo @" + """, testHost, Identifier("goo"), - Verbatim(@"@""")); + Verbatim(""" + @" + """)); } /// @@ -448,32 +484,40 @@ await TestAsync(@"goo @""", [Theory, CombinatorialData] public async Task VerbatimStringLiterals4(TestHost testHost) { - var code = @" + var code = """ + + + @" goo bar -@"" goo bar -"; + """; await TestAsync(code, testHost, - Verbatim(@"@"" goo bar + Verbatim(""" + @" goo bar -")); + + """)); } [Theory, CombinatorialData] public async Task VerbatimStringLiterals5(TestHost testHost) { - var code = @" + var code = """ + -@"" goo bar -and -on a new line "" -more stuff"; + @" goo bar + and + on a new line " + more stuff + """; await TestInMethodAsync(code, testHost, - Verbatim(@"@"" goo bar -and -on a new line """), + Verbatim(""" + @" goo bar + and + on a new line " + """), Identifier("more"), Local("stuff")); } @@ -481,17 +525,21 @@ await TestInMethodAsync(code, [Theory, CombinatorialData] public async Task VerbatimStringLiteralsUtf8_03(TestHost testHost) { - var code = @" + var code = """ -@"" goo bar -and -on a new line ""u8 -more stuff"; + + @" goo bar + and + on a new line "u8 + more stuff + """; await TestInMethodAsync(code, testHost, - Verbatim(@"@"" goo bar -and -on a new line """), + Verbatim(""" + @" goo bar + and + on a new line " + """), Keyword("u8"), Identifier("more"), Local("stuff")); @@ -500,17 +548,21 @@ await TestInMethodAsync(code, [Theory, CombinatorialData] public async Task VerbatimStringLiteralsUtf8_04(TestHost testHost) { - var code = @" + var code = """ + -@"" goo bar -and -on a new line ""U8 -more stuff"; + @" goo bar + and + on a new line "U8 + more stuff + """; await TestInMethodAsync(code, testHost, - Verbatim(@"@"" goo bar -and -on a new line """), + Verbatim(""" + @" goo bar + and + on a new line " + """), Keyword("U8"), Identifier("more"), Local("stuff")); @@ -532,7 +584,9 @@ await TestAsync( Keyword("string"), script ? Field("s") : Local("s"), Operators.Equals, - Verbatim(@"@""""""/*"""), + Verbatim("""" + @"""/*" + """"), Punctuation.Semicolon); } @@ -551,7 +605,9 @@ await TestAsync( Keyword("string"), script ? Field("s") : Local("s"), Operators.Equals, - Verbatim(@"@""""""/*"""), + Verbatim("""" + @"""/*" + """"), Keyword("u8"), Punctuation.Semicolon); } @@ -571,7 +627,9 @@ await TestAsync( Keyword("string"), script ? Field("s") : Local("s"), Operators.Equals, - Verbatim(@"@""""""/*"""), + Verbatim("""" + @"""/*" + """"), Keyword("u8"), Punctuation.Semicolon); } @@ -579,52 +637,76 @@ await TestAsync( [Theory, CombinatorialData] public async Task StringLiteral1(TestHost testHost) { - await TestAsync(@"""goo""", + await TestAsync(""" + "goo" + """, testHost, - String(@"""goo""")); + String(""" + "goo" + """)); } [Theory, CombinatorialData] public async Task StringLiteralUtf8_01(TestHost testHost) { - await TestAsync(@"""goo""u8", + await TestAsync(""" + "goo"u8 + """, testHost, - String(@"""goo"""), + String(""" + "goo" + """), Keyword("u8")); } [Theory, CombinatorialData] public async Task StringLiteralUtf8_02(TestHost testHost) { - await TestAsync(@"""goo""U8", + await TestAsync(""" + "goo"U8 + """, testHost, - String(@"""goo"""), + String(""" + "goo" + """), Keyword("U8")); } [Theory, CombinatorialData] public async Task StringLiteral2(TestHost testHost) { - await TestAsync(@"""""", + await TestAsync(""" + "" + """, testHost, - String(@"""""")); + String(""" + "" + """)); } [Theory, CombinatorialData] public async Task StringLiteralUtf8_03(TestHost testHost) { - await TestAsync(@"""""u8", + await TestAsync(""" + ""u8 + """, testHost, - String(@""""""), + String(""" + "" + """), Keyword("u8")); } [Theory, CombinatorialData] public async Task StringLiteralUtf8_04(TestHost testHost) { - await TestAsync(@"""""U8", + await TestAsync(""" + ""U8 + """, testHost, - String(@""""""), + String(""" + "" + """), Keyword("U8")); } @@ -707,7 +789,9 @@ await TestInExpressionAsync(code, [Theory, CombinatorialData] public async Task LinqWhere2(TestHost testHost) { - var code = @"from it in goo where it > ""bar"""; + var code = """ + from it in goo where it > "bar" + """; await TestInExpressionAsync(code, testHost, Keyword("from"), @@ -717,7 +801,9 @@ await TestInExpressionAsync(code, Keyword("where"), Identifier("it"), Operators.GreaterThan, - String(@"""bar""")); + String(""" + "bar" + """)); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/44423")] @@ -744,13 +830,15 @@ await TestAsync(code, public async Task LinqKeywordsAtNamespaceLevel(bool script, TestHost testHost) { // the contextual keywords are actual keywords since we parse top level field declaration and only give a semantic error - var code = @"object goo = from goo in goo - join goo in goo on goo equals goo - group goo by goo into goo - let goo = goo - where goo - orderby goo ascending, goo descending - select goo;"; + var code = """ + object goo = from goo in goo + join goo in goo on goo equals goo + group goo by goo into goo + let goo = goo + where goo + orderby goo ascending, goo descending + select goo; + """; var parseOptions = script ? Options.Script : null; @@ -801,10 +889,12 @@ await TestAsync( public async Task ContextualKeywordsAsFieldName(TestHost testHost) { await TestAsync( -@"class C -{ - int yield, get, set, value, add, remove, global, partial, where, alias; -}", + """ + class C + { + int yield, get, set, value, add, remove, global, partial, where, alias; + } + """, testHost, Keyword("class"), Class("C"), @@ -837,16 +927,18 @@ await TestAsync( public async Task LinqKeywordsInFieldInitializer(TestHost testHost) { await TestAsync( -@"class C -{ - int a = from a in a - join a in a on a equals a - group a by a into a - let a = a - where a - orderby a ascending, a descending - select a; -}", + """ + class C + { + int a = from a in a + join a in a on a equals a + group a by a into a + let a = a + where a + orderby a ascending, a descending + select a; + } + """, testHost, Keyword("class"), Class("C"), @@ -894,58 +986,60 @@ where a public async Task LinqKeywordsAsTypeName(TestHost testHost) { await TestAsync( -@"class var -{ -} + """ + class var + { + } -struct from -{ -} + struct from + { + } -interface join -{ -} + interface join + { + } -enum on -{ -} + enum on + { + } -delegate equals { } -class group -{ -} + delegate equals { } + class group + { + } -class by -{ -} + class by + { + } -class into -{ -} + class into + { + } -class let -{ -} + class let + { + } -class where -{ -} + class where + { + } -class orderby -{ -} + class orderby + { + } -class ascending -{ -} + class ascending + { + } -class descending -{ -} + class descending + { + } -class select -{ -}", + class select + { + } + """, testHost, Keyword("class"), Class("var"), @@ -1009,12 +1103,14 @@ class select public async Task LinqKeywordsAsMethodParameters(TestHost testHost) { await TestAsync( -@"class C -{ - orderby M(var goo, from goo, join goo, on goo, equals goo, group goo, by goo, into goo, let goo, where goo, orderby goo, ascending goo, descending goo, select goo) - { - } -}", + """ + class C + { + orderby M(var goo, from goo, join goo, on goo, equals goo, group goo, by goo, into goo, let goo, where goo, orderby goo, ascending goo, descending goo, select goo) + { + } + } + """, testHost, Keyword("class"), Class("C"), @@ -1073,24 +1169,26 @@ orderby M(var goo, from goo, join goo, on goo, equals goo, group goo, by goo, in public async Task LinqKeywordsInLocalVariableDeclarations(TestHost testHost) { await TestAsync( -@"class C -{ - void M() - { - var goo = (var)goo as var; - from goo = (from)goo as from; - join goo = (join)goo as join; - on goo = (on)goo as on; - equals goo = (equals)goo as equals; - group goo = (group)goo as group; - by goo = (by)goo as by; - into goo = (into)goo as into; - orderby goo = (orderby)goo as orderby; - ascending goo = (ascending)goo as ascending; - descending goo = (descending)goo as descending; - select goo = (select)goo as select; - } -}", + """ + class C + { + void M() + { + var goo = (var)goo as var; + from goo = (from)goo as from; + join goo = (join)goo as join; + on goo = (on)goo as on; + equals goo = (equals)goo as equals; + group goo = (group)goo as group; + by goo = (by)goo as by; + into goo = (into)goo as into; + orderby goo = (orderby)goo as orderby; + ascending goo = (ascending)goo as ascending; + descending goo = (descending)goo as descending; + select goo = (select)goo as select; + } + } + """, testHost, Keyword("class"), Class("C"), @@ -1228,10 +1326,12 @@ void M() public async Task LinqKeywordsAsFieldNames(TestHost testHost) { await TestAsync( -@"class C -{ - int var, from, join, on, into, equals, let, orderby, ascending, descending, select, group, by, partial; -}", + """ + class C + { + int var, from, join, on, into, equals, let, orderby, ascending, descending, select, group, by, partial; + } + """, testHost, Keyword("class"), Class("C"), @@ -1272,11 +1372,13 @@ await TestAsync( public async Task LinqKeywordsAtFieldLevelInvalid(TestHost testHost) { await TestAsync( -@"class C -{ - string Property { from a in a join a in a on a equals a group a by a into a let a = a where a orderby a ascending, -a descending select a; } -}", + """ + class C + { + string Property { from a in a join a in a on a equals a group a by a into a let a = a where a orderby a ascending, + a descending select a; } + } + """, testHost, Keyword("class"), Class("C"), @@ -1346,11 +1448,13 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task CommentAsLeadingTrivia1(TestHost testHost) { - var code = @" -class Bar { - // goo - void Method1() { } -}"; + var code = """ + + class Bar { + // goo + void Method1() { } + } + """; await TestAsync(code, testHost, Keyword("class"), @@ -1369,8 +1473,10 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task ShebangAsFirstCommentInScript(TestHost testHost) { - var code = @"#!/usr/bin/env scriptcs -System.Console.WriteLine();"; + var code = """ + #!/usr/bin/env scriptcs + System.Console.WriteLine(); + """; var expected = new[] { @@ -1391,8 +1497,10 @@ public async Task ShebangAsFirstCommentInScript(TestHost testHost) [Theory, CombinatorialData] public async Task ShebangAsFirstCommentInNonScript(TestHost testHost) { - var code = @"#!/usr/bin/env scriptcs -System.Console.WriteLine();"; + var code = """ + #!/usr/bin/env scriptcs + System.Console.WriteLine(); + """; var expected = new[] { @@ -1414,8 +1522,10 @@ public async Task ShebangAsFirstCommentInNonScript(TestHost testHost) [Theory, CombinatorialData] public async Task ShebangNotAsFirstCommentInScript(TestHost testHost) { - var code = @" #!/usr/bin/env scriptcs -System.Console.WriteLine();"; + var code = """ + #!/usr/bin/env scriptcs + System.Console.WriteLine(); + """; var expected = new[] { @@ -1437,12 +1547,14 @@ public async Task ShebangNotAsFirstCommentInScript(TestHost testHost) [Theory, CombinatorialData] public async Task CommentAsMethodBodyContent(TestHost testHost) { - var code = @" -class Bar { - void Method1() { -// goo -} -}"; + var code = """ + + class Bar { + void Method1() { + // goo + } + } + """; await TestAsync(code, testHost, Keyword("class"), @@ -1462,11 +1574,13 @@ await TestAsync(code, public async Task CommentMix1(TestHost testHost) { await TestAsync( -@"// comment1 /* -class cl -{ -} -//comment2 */", + """ + // comment1 /* + class cl + { + } + //comment2 */ + """, testHost, Comment("// comment1 /*"), Keyword("class"), @@ -1494,9 +1608,11 @@ await TestInMethodAsync( [Theory, CombinatorialData] public async Task XmlDocCommentOnClass(TestHost testHost) { - var code = @" -/// something -class Bar { }"; + var code = """ + + /// something + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1517,11 +1633,13 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task XmlDocCommentOnClassWithIndent(TestHost testHost) { - var code = @" - /// - /// something - /// - class Bar { }"; + var code = """ + + /// + /// something + /// + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1545,9 +1663,11 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task XmlDocComment_EntityReference(TestHost testHost) { - var code = @" -/// A -class Bar { }"; + var code = """ + + /// A + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1568,10 +1688,12 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task XmlDocComment_ExteriorTriviaInsideCloseTag(TestHost testHost) { - var code = @" -/// something -class Bar { }"; + var code = """ + + /// something + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1594,12 +1716,14 @@ await TestAsync(code, [CombinatorialData] public async Task XmlDocComment_ExteriorTriviaInsideCRef(TestHost testHost) { - var code = @" -/// -class C -{ -}"; + var code = """ + + /// + class C + { + } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1608,12 +1732,16 @@ await TestAsync(code, XmlDoc.Name("see"), XmlDoc.AttributeName("cref"), XmlDoc.Delimiter("="), - XmlDoc.AttributeQuotes("\""), + XmlDoc.AttributeQuotes(""" + " + """), Identifier("System"), Operators.Dot, XmlDoc.Delimiter("///"), Identifier("Int32"), - XmlDoc.AttributeQuotes("\""), + XmlDoc.AttributeQuotes(""" + " + """), XmlDoc.Delimiter("/>"), Keyword("class"), Class("C"), @@ -1624,11 +1752,13 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task XmlDocCommentOnClassWithExteriorTrivia(TestHost testHost) { - var code = @" -/// -/// something -/// -class Bar { }"; + var code = """ + + /// + /// something + /// + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1653,10 +1783,12 @@ await TestAsync(code, public async Task XmlDocComment_ExteriorTriviaNoText(TestHost testHost) { var code = -@"/// -///something -/// -class Bar { }"; + """ + /// + ///something + /// + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1678,9 +1810,11 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task XmlDocComment_EmptyElement(TestHost testHost) { - var code = @" -/// -class Bar { }"; + var code = """ + + /// + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1697,9 +1831,11 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task XmlDocComment_Attribute(TestHost testHost) { - var code = @" -/// something -class Bar { }"; + var code = """ + + /// something + class Bar { } + """; await TestAsync(code, testHost, @@ -1709,9 +1845,13 @@ await TestAsync(code, XmlDoc.Name("summary"), XmlDoc.AttributeName("attribute"), XmlDoc.Delimiter("="), - XmlDoc.AttributeQuotes(@""""), + XmlDoc.AttributeQuotes(""" + " + """), XmlDoc.AttributeValue(@"value"), - XmlDoc.AttributeQuotes(@""""), + XmlDoc.AttributeQuotes(""" + " + """), XmlDoc.Delimiter(">"), XmlDoc.Text("something"), XmlDoc.Delimiter(" -class Bar { }"; + var code = """ + + /// + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1737,9 +1879,13 @@ await TestAsync(code, XmlDoc.Name("summary"), XmlDoc.AttributeName("attribute"), XmlDoc.Delimiter("="), - XmlDoc.AttributeQuotes(@""""), + XmlDoc.AttributeQuotes(""" + " + """), XmlDoc.AttributeValue(@"value"), - XmlDoc.AttributeQuotes(@""""), + XmlDoc.AttributeQuotes(""" + " + """), XmlDoc.Delimiter("/>"), Keyword("class"), Class("Bar"), @@ -1750,9 +1896,11 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task XmlDocComment_ExtraSpaces(TestHost testHost) { - var code = @" -/// < summary attribute = ""value"" /> -class Bar { }"; + var code = """ + + /// < summary attribute = "value" /> + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1761,9 +1909,13 @@ await TestAsync(code, XmlDoc.Name("summary"), XmlDoc.AttributeName("attribute"), XmlDoc.Delimiter("="), - XmlDoc.AttributeQuotes(@""""), + XmlDoc.AttributeQuotes(""" + " + """), XmlDoc.AttributeValue(@"value"), - XmlDoc.AttributeQuotes(@""""), + XmlDoc.AttributeQuotes(""" + " + """), XmlDoc.Delimiter("/>"), Keyword("class"), Class("Bar"), @@ -1774,9 +1926,11 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task XmlDocComment_XmlComment(TestHost testHost) { - var code = @" -/// -class Bar { }"; + var code = """ + + /// + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1792,10 +1946,12 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task XmlDocComment_XmlCommentWithExteriorTrivia(TestHost testHost) { - var code = @" -/// -class Bar { }"; + var code = """ + + /// + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1813,9 +1969,11 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task XmlDocComment_XmlCommentInElement(TestHost testHost) { - var code = @" -/// -class Bar { }"; + var code = """ + + /// + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1838,11 +1996,13 @@ await TestAsync(code, [WorkItem("https://github.com/dotnet/roslyn/pull/31410")] public async Task XmlDocComment_MalformedXmlDocComment(TestHost testHost) { - var code = @" -/// -///. -/// -class C { }"; + var code = """ + + /// + ///. + /// + class C { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1872,10 +2032,12 @@ await TestAsync(code, public async Task MultilineXmlDocComment_ExteriorTrivia(TestHost testHost) { var code = -@"/** -*comment -**/ -class Bar { }"; + """ + /** + *comment + **/ + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("/**"), @@ -1898,10 +2060,12 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task XmlDocComment_CDataWithExteriorTrivia(TestHost testHost) { - var code = @" -/// -class Bar { }"; + var code = """ + + /// + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1920,14 +2084,16 @@ await TestAsync(code, public async Task XmlDocComment_ProcessingDirective(TestHost testHost) { await TestAsync( -@"/// -public class Program -{ - static void Main() - { - } -}", + """ + /// + public class Program + { + static void Main() + { + } + } + """, testHost, XmlDoc.Delimiter("///"), XmlDoc.Text(" "), @@ -2182,10 +2348,12 @@ await TestAsync(code, public async Task GenericParameter_Method(TestHost testHost) { await TestInClassAsync( -@"T M(T t) -{ - return default(T); -}", + """ + T M(T t) + { + return default(T); + } + """, testHost, Identifier("T"), Method("M"), @@ -2222,9 +2390,11 @@ await TestInExpressionAsync("true ? 1 : 0", public async Task BaseClass(TestHost testHost) { await TestAsync( -@"class C : B -{ -}", + """ + class C : B + { + } + """, testHost, Keyword("class"), Class("C"), @@ -2260,9 +2430,11 @@ await TestAsync( public async Task TestAngleBracketsOnGenericConstraints_Bug932262(TestHost testHost) { await TestAsync( -@"class C where T : A -{ -}", + """ + class C where T : A + { + } + """, testHost, Keyword("class"), Class("C"), @@ -2356,9 +2528,11 @@ await TestAsync( public async Task AttributeTargetSpecifiersOnDelegate(TestHost testHost) { await TestInClassAsync( -@"[type: A] -[return: A] -delegate void M();", + """ + [type: A] + [return: A] + delegate void M(); + """, testHost, Punctuation.OpenBracket, Keyword("type"), @@ -2382,11 +2556,13 @@ await TestInClassAsync( public async Task AttributeTargetSpecifiersOnMethod(TestHost testHost) { await TestInClassAsync( -@"[return: A] -[method: A] -void M() -{ -}", + """ + [return: A] + [method: A] + void M() + { + } + """, testHost, Punctuation.OpenBracket, Keyword("return"), @@ -2410,18 +2586,20 @@ void M() public async Task AttributeTargetSpecifiersOnCtor(TestHost testHost) { await TestAsync( -@"class C -{ - [method: A] - C() - { - } -}", - testHost, - Keyword("class"), - Class("C"), - Punctuation.OpenCurly, - Punctuation.OpenBracket, + """ + class C + { + [method: A] + C() + { + } + } + """, + testHost, + Keyword("class"), + Class("C"), + Punctuation.OpenCurly, + Punctuation.OpenBracket, Keyword("method"), Punctuation.Colon, Identifier("A"), @@ -2438,13 +2616,15 @@ await TestAsync( public async Task AttributeTargetSpecifiersOnDtor(TestHost testHost) { await TestAsync( -@"class C -{ - [method: A] - ~C() - { - } -}", + """ + class C + { + [method: A] + ~C() + { + } + } + """, testHost, Keyword("class"), Class("C"), @@ -2467,11 +2647,13 @@ await TestAsync( public async Task AttributeTargetSpecifiersOnOperator(TestHost testHost) { await TestInClassAsync( -@"[method: A] -[return: A] -static T operator +(T a, T b) -{ -}", + """ + [method: A] + [return: A] + static T operator +(T a, T b) + { + } + """, testHost, Punctuation.OpenBracket, Keyword("method"), @@ -2502,21 +2684,23 @@ await TestInClassAsync( public async Task AttributeTargetSpecifiersOnEventDeclaration(TestHost testHost) { await TestInClassAsync( -@"[event: A] -event A E -{ - [param: Test] - [method: Test] - add - { - } + """ + [event: A] + event A E + { + [param: Test] + [method: Test] + add + { + } - [param: Test] - [method: Test] - remove - { - } -}", + [param: Test] + [method: Test] + remove + { + } + } + """, testHost, Punctuation.OpenBracket, Keyword("event"), @@ -2560,20 +2744,22 @@ event A E public async Task AttributeTargetSpecifiersOnPropertyAccessors(TestHost testHost) { await TestInClassAsync( -@"int P -{ - [return: T] - [method: T] - get - { - } + """ + int P + { + [return: T] + [method: T] + get + { + } - [param: T] - [method: T] - set - { - } -}", + [param: T] + [method: T] + set + { + } + } + """, testHost, Keyword("int"), Property("P"), @@ -2611,8 +2797,10 @@ await TestInClassAsync( public async Task AttributeTargetSpecifiersOnIndexers(TestHost testHost) { await TestInClassAsync( -@"[property: A] -int this[int i] { get; set; }", + """ + [property: A] + int this[int i] { get; set; } + """, testHost, Punctuation.OpenBracket, Keyword("property"), @@ -2637,20 +2825,22 @@ await TestInClassAsync( public async Task AttributeTargetSpecifiersOnIndexerAccessors(TestHost testHost) { await TestInClassAsync( -@"int this[int i] -{ - [return: T] - [method: T] - get - { - } + """ + int this[int i] + { + [return: T] + [method: T] + get + { + } - [param: T] - [method: T] - set - { - } -}", + [param: T] + [method: T] + set + { + } + } + """, testHost, Keyword("int"), Keyword("this"), @@ -2692,8 +2882,10 @@ await TestInClassAsync( public async Task AttributeTargetSpecifiersOnField(TestHost testHost) { await TestInClassAsync( -@"[field: A] -const int a = 0;", + """ + [field: A] + const int a = 0; + """, testHost, Punctuation.OpenBracket, Keyword("field"), @@ -2713,189 +2905,191 @@ await TestInClassAsync( public async Task TestAllKeywords(TestHost testHost) { await TestAsync( -@"using System; -#region TaoRegion -namespace MyNamespace -{ - abstract class Goo : Bar - { - bool goo = default(bool); - byte goo1; - char goo2; - const int goo3 = 999; - decimal goo4; - - delegate void D(); - delegate* managed mgdfun; - delegate* unmanaged unmgdfun; - - double goo5; - - enum MyEnum - { - one, - two, - three - }; - - event D MyEvent; - - float goo6; - static int x; - long goo7; - sbyte goo8; - short goo9; - int goo10 = sizeof(int); - string goo11; - uint goo12; - ulong goo13; - volatile ushort goo14; - - struct SomeStruct - { - } - - protected virtual void someMethod() - { - } - - public Goo(int i) - { - bool var = i is int; - try + """ + using System; + #region TaoRegion + namespace MyNamespace { - while (true) + abstract class Goo : Bar { - continue; - break; + bool goo = default(bool); + byte goo1; + char goo2; + const int goo3 = 999; + decimal goo4; + + delegate void D(); + delegate* managed mgdfun; + delegate* unmanaged unmgdfun; + + double goo5; + + enum MyEnum + { + one, + two, + three + }; + + event D MyEvent; + + float goo6; + static int x; + long goo7; + sbyte goo8; + short goo9; + int goo10 = sizeof(int); + string goo11; + uint goo12; + ulong goo13; + volatile ushort goo14; + + struct SomeStruct + { + } + + protected virtual void someMethod() + { + } + + public Goo(int i) + { + bool var = i is int; + try + { + while (true) + { + continue; + break; + } + + switch (goo) + { + case true: + break; + default: + break; + } + } + catch (System.Exception) + { + } + finally + { + } + + checked + { + int i2 = 10000; + i2++; + } + + do + { + } + while (true); + if (false) + { + } + else + { + } + + unsafe + { + fixed (int* p = &x) + { + } + + char* buffer = stackalloc char[16]; + } + + for (int i1 = 0; i1 < 10; i1++) + { + } + + System.Collections.ArrayList al = new System.Collections.ArrayList(); + foreach (object o in al) + { + object o1 = o; + } + + lock (this) + { + } + } + + Goo method(Bar i, out int z) + { + z = 5; + return i as Goo; + } + + public static explicit operator Goo(int i) + { + return new Baz(1); + } + + public static implicit operator Goo(double x) + { + return new Baz(1); + } + + public extern void doSomething(); + + internal void method2(object o) + { + if (o == null) + goto Output; + if (o is Baz) + return; + else + throw new System.Exception(); + Output: + Console.WriteLine("Finished"); + } } - switch (goo) + sealed class Baz : Goo { - case true: - break; - default: - break; + readonly int field; + + public Baz(int i) : base(i) + { + } + + public void someOtherMethod(ref int i, System.Type c) + { + int f = 1; + someOtherMethod(ref f, typeof(int)); + } + + protected override void someMethod() + { + unchecked + { + int i = 1; + i++; + } + } + + private void method(params object[] args) + { + } + + private string aMethod(object o) => o switch + { + int => string.Empty, + _ when true => throw new System.Exception() + }; } - } - catch (System.Exception) - { - } - finally - { - } - checked - { - int i2 = 10000; - i2++; - } - - do - { - } - while (true); - if (false) - { - } - else - { - } - - unsafe - { - fixed (int* p = &x) + interface Bar { } - - char* buffer = stackalloc char[16]; - } - - for (int i1 = 0; i1 < 10; i1++) - { - } - - System.Collections.ArrayList al = new System.Collections.ArrayList(); - foreach (object o in al) - { - object o1 = o; - } - - lock (this) - { - } - } - - Goo method(Bar i, out int z) - { - z = 5; - return i as Goo; - } - - public static explicit operator Goo(int i) - { - return new Baz(1); - } - - public static implicit operator Goo(double x) - { - return new Baz(1); - } - - public extern void doSomething(); - - internal void method2(object o) - { - if (o == null) - goto Output; - if (o is Baz) - return; - else - throw new System.Exception(); - Output: - Console.WriteLine(""Finished""); - } - } - - sealed class Baz : Goo - { - readonly int field; - - public Baz(int i) : base(i) - { - } - - public void someOtherMethod(ref int i, System.Type c) - { - int f = 1; - someOtherMethod(ref f, typeof(int)); - } - - protected override void someMethod() - { - unchecked - { - int i = 1; - i++; } - } - - private void method(params object[] args) - { - } - - private string aMethod(object o) => o switch - { - int => string.Empty, - _ when true => throw new System.Exception() - }; - } - - interface Bar - { - } -} -#endregion TaoRegion", + #endregion TaoRegion + """, testHost, [new CSharpParseOptions(LanguageVersion.CSharp8)], Keyword("using"), @@ -3292,7 +3486,9 @@ [new CSharpParseOptions(LanguageVersion.CSharp8)], Operators.Dot, Identifier("WriteLine"), Punctuation.OpenParen, - String(@"""Finished"""), + String(""" + "Finished" + """), Punctuation.CloseParen, Punctuation.Semicolon, Punctuation.CloseCurly, @@ -3427,60 +3623,62 @@ [new CSharpParseOptions(LanguageVersion.CSharp8)], public async Task TestAllOperators(TestHost testHost) { await TestAsync( -@"using IO = System.IO; - -public class Goo -{ - public void method() - { - int[] a = new int[5]; - int[] var = { - 1, - 2, - 3, - 4, - 5 - }; - int i = a[i]; - Goo f = new Goo(); - f.method(); - i = i + i - i * i / i % i & i | i ^ i; - bool b = true & false | true ^ false; - b = !b; - i = ~i; - b = i < i && i > i; - int? ii = 5; - int f = true ? 1 : 0; - i++; - i--; - b = true && false || true; - i << 5; - i >> 5; - i >>> 5; - b = i == i && i != i && i <= i && i >= i; - i += 5.0; - i -= i; - i *= i; - i /= i; - i %= i; - i &= i; - i |= i; - i ^= i; - i <<= i; - i >>= i; - i >>>= i; - i ??= i; - object s = x => x + 1; - Point point; - unsafe - { - Point* p = &point; - p->x = 10; - } + """ + using IO = System.IO; - IO::BinaryReader br = null; - } -}", + public class Goo + { + public void method() + { + int[] a = new int[5]; + int[] var = { + 1, + 2, + 3, + 4, + 5 + }; + int i = a[i]; + Goo f = new Goo(); + f.method(); + i = i + i - i * i / i % i & i | i ^ i; + bool b = true & false | true ^ false; + b = !b; + i = ~i; + b = i < i && i > i; + int? ii = 5; + int f = true ? 1 : 0; + i++; + i--; + b = true && false || true; + i << 5; + i >> 5; + i >>> 5; + b = i == i && i != i && i <= i && i >= i; + i += 5.0; + i -= i; + i *= i; + i /= i; + i %= i; + i &= i; + i |= i; + i ^= i; + i <<= i; + i >>= i; + i >>>= i; + i ??= i; + object s = x => x + 1; + Point point; + unsafe + { + Point* p = &point; + p->x = 10; + } + + IO::BinaryReader br = null; + } + } + """, testHost, Keyword("using"), Identifier("IO"), @@ -3759,22 +3957,24 @@ public void method() public async Task TestPartialMethodWithNamePartial(TestHost testHost) { await TestAsync( -@"partial class C -{ - partial void partial(string bar); + """ + partial class C + { + partial void partial(string bar); - partial void partial(string baz) - { - } + partial void partial(string baz) + { + } - partial int Goo(); + partial int Goo(); - partial int Goo() - { - } + partial int Goo() + { + } - public partial void partial void -}", + public partial void partial void + } + """, testHost, Keyword("partial"), Keyword("class"), @@ -3822,16 +4022,18 @@ public partial void partial void public async Task ValueInSetterAndAnonymousTypePropertyName(TestHost testHost) { await TestAsync( -@"class C -{ - int P - { - set - { - var t = new { value = value }; - } - } -}", + """ + class C + { + int P + { + set + { + var t = new { value = value }; + } + } + } + """, testHost, Keyword("class"), Class("C"), @@ -3861,17 +4063,19 @@ int P public async Task TestValueInLabel(TestHost testHost) { await TestAsync( -@"class C -{ - int X - { - set - { - value: - ; - } - } -}", + """ + class C + { + int X + { + set + { + value: + ; + } + } + } + """, testHost, Keyword("class"), Class("C"), @@ -3894,19 +4098,21 @@ int X public async Task TestGenericVar(TestHost testHost) { await TestAsync( -@"using System; + """ + using System; -static class Program -{ - static void Main() - { - var x = 1; - } -} + static class Program + { + static void Main() + { + var x = 1; + } + } -class var -{ -}", + class var + { + } + """, testHost, Keyword("using"), Identifier("System"), @@ -3944,22 +4150,24 @@ class var public async Task TestInaccessibleVar(TestHost testHost) { await TestAsync( -@"using System; + """ + using System; -class A -{ - private class var - { - } -} + class A + { + private class var + { + } + } -class B : A -{ - static void Main() - { - var x = 1; - } -}", + class B : A + { + static void Main() + { + var x = 1; + } + } + """, testHost, Keyword("using"), Identifier("System"), @@ -3999,13 +4207,15 @@ static void Main() public async Task TestEscapedVar(TestHost testHost) { await TestAsync( -@"class Program -{ - static void Main(string[] args) - { - @var v = 1; - } -}", + """ + class Program + { + static void Main(string[] args) + { + @var v = 1; + } + } + """, testHost, Keyword("class"), Class("Program"), @@ -4035,23 +4245,25 @@ static void Main(string[] args) public async Task TestVar(TestHost testHost) { await TestAsync( -@"class Program -{ - class var - { - } + """ + class Program + { + class var + { + } - static var GetVarT() - { - return null; - } + static var GetVarT() + { + return null; + } - static void Main() - { - var x = GetVarT(); - var y = new var(); - } -}", + static void Main() + { + var x = GetVarT(); + var y = new var(); + } + } + """, testHost, Keyword("class"), Class("Program"), @@ -4111,15 +4323,17 @@ static void Main() public async Task TestVar2(TestHost testHost) { await TestAsync( -@"class Program -{ - void Main(string[] args) - { - foreach (var v in args) - { - } - } -}", + """ + class Program + { + void Main(string[] args) + { + foreach (var v in args) + { + } + } + } + """, testHost, Keyword("class"), Class("Program"), @@ -4149,53 +4363,69 @@ void Main(string[] args) [Theory, CombinatorialData] public async Task InterpolatedStrings1(TestHost testHost) { - var code = @" -var x = ""World""; -var y = $""Hello, {x}""; -"; + var code = """ + + var x = "World"; + var y = $"Hello, {x}"; + + """; await TestInMethodAsync(code, testHost, Keyword("var"), Local("x"), Operators.Equals, - String("\"World\""), + String(""" + "World" + """), Punctuation.Semicolon, Keyword("var"), Local("y"), Operators.Equals, - String("$\""), + String(""" + $" + """), String("Hello, "), Punctuation.OpenCurly, Identifier("x"), Punctuation.CloseCurly, - String("\""), + String(""" + " + """), Punctuation.Semicolon); } [Theory, CombinatorialData] public async Task InterpolatedStrings2(TestHost testHost) { - var code = @" -var a = ""Hello""; -var b = ""World""; -var c = $""{a}, {b}""; -"; + var code = """ + + var a = "Hello"; + var b = "World"; + var c = $"{a}, {b}"; + + """; await TestInMethodAsync(code, testHost, Keyword("var"), Local("a"), Operators.Equals, - String("\"Hello\""), + String(""" + "Hello" + """), Punctuation.Semicolon, Keyword("var"), Local("b"), Operators.Equals, - String("\"World\""), + String(""" + "World" + """), Punctuation.Semicolon, Keyword("var"), Local("c"), Operators.Equals, - String("$\""), + String(""" + $" + """), Punctuation.OpenCurly, Identifier("a"), Punctuation.CloseCurly, @@ -4203,34 +4433,44 @@ await TestInMethodAsync(code, Punctuation.OpenCurly, Identifier("b"), Punctuation.CloseCurly, - String("\""), + String(""" + " + """), Punctuation.Semicolon); } [Theory, CombinatorialData] public async Task InterpolatedStrings3(TestHost testHost) { - var code = @" -var a = ""Hello""; -var b = ""World""; -var c = $@""{a}, {b}""; -"; + var code = """ + + var a = "Hello"; + var b = "World"; + var c = $@"{a}, {b}"; + + """; await TestInMethodAsync(code, testHost, Keyword("var"), Local("a"), Operators.Equals, - String("\"Hello\""), + String(""" + "Hello" + """), Punctuation.Semicolon, Keyword("var"), Local("b"), Operators.Equals, - String("\"World\""), + String(""" + "World" + """), Punctuation.Semicolon, Keyword("var"), Local("c"), Operators.Equals, - Verbatim("$@\""), + Verbatim(""" + $@" + """), Punctuation.OpenCurly, Identifier("a"), Punctuation.CloseCurly, @@ -4238,21 +4478,25 @@ await TestInMethodAsync(code, Punctuation.OpenCurly, Identifier("b"), Punctuation.CloseCurly, - Verbatim("\""), + Verbatim(""" + " + """), Punctuation.Semicolon); } [Theory, CombinatorialData] public async Task ExceptionFilter1(TestHost testHost) { - var code = @" -try -{ -} -catch when (true) -{ -} -"; + var code = """ + + try + { + } + catch when (true) + { + } + + """; await TestInMethodAsync(code, testHost, ControlKeyword("try"), @@ -4270,14 +4514,16 @@ await TestInMethodAsync(code, [Theory, CombinatorialData] public async Task ExceptionFilter2(TestHost testHost) { - var code = @" -try -{ -} -catch (System.Exception) when (true) -{ -} -"; + var code = """ + + try + { + } + catch (System.Exception) when (true) + { + } + + """; await TestInMethodAsync(code, testHost, ControlKeyword("try"), @@ -4300,8 +4546,10 @@ await TestInMethodAsync(code, [Theory, CombinatorialData] public async Task OutVar(TestHost testHost) { - var code = @" -F(out var);"; + var code = """ + + F(out var); + """; await TestInMethodAsync(code, testHost, Identifier("F"), @@ -4315,35 +4563,45 @@ await TestInMethodAsync(code, [Theory, CombinatorialData] public async Task ReferenceDirective(TestHost testHost) { - var code = @" -#r ""file.dll"""; + var code = """ + + #r "file.dll" + """; await TestAsync(code, testHost, PPKeyword("#"), PPKeyword("r"), - String("\"file.dll\"")); + String(""" + "file.dll" + """)); } [Theory, CombinatorialData] public async Task LoadDirective(TestHost testHost) { - var code = @" -#load ""file.csx"""; + var code = """ + + #load "file.csx" + """; await TestAsync(code, testHost, PPKeyword("#"), PPKeyword("load"), - String("\"file.csx\"")); + String(""" + "file.csx" + """)); } [Theory, CombinatorialData] public async Task IncompleteAwaitInNonAsyncContext(TestHost testHost) { - var code = @" -void M() -{ - var x = await -}"; + var code = """ + + void M() + { + var x = await + } + """; await TestInClassAsync(code, testHost, Keyword("void"), @@ -4361,11 +4619,13 @@ await TestInClassAsync(code, [Theory, CombinatorialData] public async Task CompleteAwaitInNonAsyncContext(TestHost testHost) { - var code = @" -void M() -{ - var x = await; -}"; + var code = """ + + void M() + { + var x = await; + } + """; await TestInClassAsync(code, testHost, Keyword("void"), @@ -4451,14 +4711,16 @@ await TestInMethodAsync("var values = (a: 1, b: 2)", public async Task TestConflictMarkers1(TestHost testHost) { await TestAsync( -@"class C -{ -<<<<<<< Start - public void Goo(); -======= - public void Bar(); ->>>>>>> End -}", + """ + class C + { + <<<<<<< Start + public void Goo(); + ======= + public void Bar(); + >>>>>>> End + } + """, testHost, Keyword("class"), Class("C"), @@ -4485,16 +4747,18 @@ await TestAsync( public async Task TestConflictMarkers2(TestHost testHost) { await TestAsync( -@"class C -{ -<<<<<<< Start - public void Goo(); -||||||| Baseline - int removed; -======= - public void Bar(); ->>>>>>> End -}", + """ + class C + { + <<<<<<< Start + public void Goo(); + ||||||| Baseline + int removed; + ======= + public void Bar(); + >>>>>>> End + } + """, testHost, Keyword("class"), Class("C"), @@ -4524,9 +4788,11 @@ await TestAsync( [Theory, CombinatorialData] public async Task TestUnmanagedConstraint_InsideMethod(TestHost testHost) { - await TestInMethodAsync(@" -var unmanaged = 0; -unmanaged++;", + await TestInMethodAsync(""" + + var unmanaged = 0; + unmanaged++; + """, testHost, Keyword("var"), Local("unmanaged"), @@ -4560,9 +4826,11 @@ await TestAsync( [Theory, CombinatorialData] public async Task TestUnmanagedConstraint_Type_ExistingInterface(TestHost testHost) { - await TestAsync(@" -interface unmanaged {} -class X where T : unmanaged { }", + await TestAsync(""" + + interface unmanaged {} + class X where T : unmanaged { } + """, testHost, Keyword("interface"), Interface("unmanaged"), @@ -4584,12 +4852,14 @@ class X where T : unmanaged { }", [Theory, CombinatorialData] public async Task TestUnmanagedConstraint_Type_ExistingInterfaceButOutOfScope(TestHost testHost) { - await TestAsync(@" -namespace OtherScope -{ - interface unmanaged {} -} -class X where T : unmanaged { }", + await TestAsync(""" + + namespace OtherScope + { + interface unmanaged {} + } + class X where T : unmanaged { } + """, testHost, Keyword("namespace"), Namespace("OtherScope"), @@ -4615,11 +4885,13 @@ class X where T : unmanaged { }", [Theory, CombinatorialData] public async Task TestUnmanagedConstraint_Method_Keyword(TestHost testHost) { - await TestAsync(@" -class X -{ - void M() where T : unmanaged { } -}", + await TestAsync(""" + + class X + { + void M() where T : unmanaged { } + } + """, testHost, Keyword("class"), Class("X"), @@ -4643,12 +4915,14 @@ void M() where T : unmanaged { } [Theory, CombinatorialData] public async Task TestUnmanagedConstraint_Method_ExistingInterface(TestHost testHost) { - await TestAsync(@" -interface unmanaged {} -class X -{ - void M() where T : unmanaged { } -}", + await TestAsync(""" + + interface unmanaged {} + class X + { + void M() where T : unmanaged { } + } + """, testHost, Keyword("interface"), Interface("unmanaged"), @@ -4676,15 +4950,17 @@ void M() where T : unmanaged { } [Theory, CombinatorialData] public async Task TestUnmanagedConstraint_Method_ExistingInterfaceButOutOfScope(TestHost testHost) { - await TestAsync(@" -namespace OtherScope -{ - interface unmanaged {} -} -class X -{ - void M() where T : unmanaged { } -}", + await TestAsync(""" + + namespace OtherScope + { + interface unmanaged {} + } + class X + { + void M() where T : unmanaged { } + } + """, testHost, Keyword("namespace"), Namespace("OtherScope"), @@ -4737,9 +5013,11 @@ await TestAsync( [Theory, CombinatorialData] public async Task TestUnmanagedConstraint_Delegate_ExistingInterface(TestHost testHost) { - await TestAsync(@" -interface unmanaged {} -delegate void D() where T : unmanaged;", + await TestAsync(""" + + interface unmanaged {} + delegate void D() where T : unmanaged; + """, testHost, Keyword("interface"), Interface("unmanaged"), @@ -4763,12 +5041,14 @@ interface unmanaged {} [Theory, CombinatorialData] public async Task TestUnmanagedConstraint_Delegate_ExistingInterfaceButOutOfScope(TestHost testHost) { - await TestAsync(@" -namespace OtherScope -{ - interface unmanaged {} -} -delegate void D() where T : unmanaged;", + await TestAsync(""" + + namespace OtherScope + { + interface unmanaged {} + } + delegate void D() where T : unmanaged; + """, testHost, Keyword("namespace"), Namespace("OtherScope"), @@ -4796,14 +5076,16 @@ interface unmanaged {} [Theory, CombinatorialData] public async Task TestUnmanagedConstraint_LocalFunction_Keyword(TestHost testHost) { - await TestAsync(@" -class X -{ - void N() - { - void M() where T : unmanaged { } - } -}", + await TestAsync(""" + + class X + { + void N() + { + void M() where T : unmanaged { } + } + } + """, testHost, Keyword("class"), Class("X"), @@ -4833,15 +5115,17 @@ void M() where T : unmanaged { } [Theory, CombinatorialData] public async Task TestUnmanagedConstraint_LocalFunction_ExistingInterface(TestHost testHost) { - await TestAsync(@" -interface unmanaged {} -class X -{ - void N() - { - void M() where T : unmanaged { } - } -}", + await TestAsync(""" + + interface unmanaged {} + class X + { + void N() + { + void M() where T : unmanaged { } + } + } + """, testHost, Keyword("interface"), Interface("unmanaged"), @@ -4875,18 +5159,20 @@ void M() where T : unmanaged { } [Theory, CombinatorialData] public async Task TestUnmanagedConstraint_LocalFunction_ExistingInterfaceButOutOfScope(TestHost testHost) { - await TestAsync(@" -namespace OtherScope -{ - interface unmanaged {} -} -class X -{ - void N() - { - void M() where T : unmanaged { } - } -}", + await TestAsync(""" + + namespace OtherScope + { + interface unmanaged {} + } + class X + { + void N() + { + void M() where T : unmanaged { } + } + } + """, testHost, Keyword("namespace"), Namespace("OtherScope"), @@ -4924,12 +5210,14 @@ void M() where T : unmanaged { } [Theory, CombinatorialData] public async Task TestDeclarationIsPattern(TestHost testHost) { - await TestInMethodAsync(@" -object foo; + await TestInMethodAsync(""" -if (foo is Action action) -{ -}", + object foo; + + if (foo is Action action) + { + } + """, testHost, Keyword("object"), Local("foo"), @@ -4948,14 +5236,16 @@ await TestInMethodAsync(@" [Theory, CombinatorialData] public async Task TestDeclarationSwitchPattern(TestHost testHost) { - await TestInMethodAsync(@" -object y; + await TestInMethodAsync(""" -switch (y) -{ - case int x: - break; -}", + object y; + + switch (y) + { + case int x: + break; + } + """, testHost, Keyword("object"), Local("y"), @@ -4977,8 +5267,10 @@ await TestInMethodAsync(@" [Theory, CombinatorialData] public async Task TestDeclarationExpression(TestHost testHost) { - await TestInMethodAsync(@" -int (foo, bar) = (1, 2);", + await TestInMethodAsync(""" + + int (foo, bar) = (1, 2); + """, testHost, Keyword("int"), Punctuation.OpenParen, @@ -4999,14 +5291,16 @@ await TestInMethodAsync(@" [WorkItem("https://github.com/dotnet/roslyn/issues/18956")] public async Task TestListPattern(TestHost testHost) { - await TestInMethodAsync(@" -switch (new int[0]) -{ - case [1, 2]: - break; - case [1, .. var end]: - break; -}", + await TestInMethodAsync(""" + + switch (new int[0]) + { + case [1, 2]: + break; + case [1, .. var end]: + break; + } + """, testHost, ControlKeyword("switch"), Punctuation.OpenParen, @@ -5044,11 +5338,13 @@ await TestInMethodAsync(@" [WorkItem("https://github.com/dotnet/roslyn/issues/18956")] public async Task TestListPattern2(TestHost testHost) { - await TestInMethodAsync(@" -_ = x switch -{ - [var start, .. var end] => 1 -}", + await TestInMethodAsync(""" + + _ = x switch + { + [var start, .. var end] => 1 + } + """, testHost, Identifier("_"), Operators.Equals, @@ -5072,9 +5368,11 @@ await TestInMethodAsync(@" [WorkItem("https://github.com/dotnet/roslyn/issues/18956")] public async Task TestVarPattern(TestHost testHost) { - await TestInMethodAsync(@" -_ = 1 is var x; -", + await TestInMethodAsync(""" + + _ = 1 is var x; + + """, testHost, Identifier("_"), Operators.Equals, @@ -5088,8 +5386,10 @@ await TestInMethodAsync(@" [Theory, CombinatorialData] public async Task TestTupleTypeSyntax(TestHost testHost) { - await TestInClassAsync(@" -public (int a, int b) Get() => null;", + await TestInClassAsync(""" + + public (int a, int b) Get() => null; + """, testHost, Keyword("public"), Punctuation.OpenParen, @@ -5110,10 +5410,12 @@ await TestInClassAsync(@" [Theory, CombinatorialData] public async Task TestOutParameter(TestHost testHost) { - await TestInMethodAsync(@" -if (int.TryParse(""1"", out int x)) -{ -}", + await TestInMethodAsync(""" + + if (int.TryParse("1", out int x)) + { + } + """, testHost, ControlKeyword("if"), Punctuation.OpenParen, @@ -5121,7 +5423,9 @@ await TestInMethodAsync(@" Operators.Dot, Identifier("TryParse"), Punctuation.OpenParen, - String(@"""1"""), + String(""" + "1" + """), Punctuation.Comma, Keyword("out"), Keyword("int"), @@ -5135,9 +5439,11 @@ await TestInMethodAsync(@" [Theory, CombinatorialData] public async Task TestOutParameter2(TestHost testHost) { - await TestInClassAsync(@" -int F = int.TryParse(""1"", out int x) ? x : -1; -", + await TestInClassAsync(""" + + int F = int.TryParse("1", out int x) ? x : -1; + + """, testHost, Keyword("int"), Field("F"), @@ -5146,7 +5452,9 @@ await TestInClassAsync(@" Operators.Dot, Identifier("TryParse"), Punctuation.OpenParen, - String(@"""1"""), + String(""" + "1" + """), Punctuation.Comma, Keyword("out"), Keyword("int"), @@ -5227,9 +5535,11 @@ await TestAsync(code, [CombinatorialData] public async Task ForEachVariableStatement(TestHost testHost) { - await TestInMethodAsync(@" -foreach (var (x, y) in new[] { (1, 2) }); -", + await TestInMethodAsync(""" + + foreach (var (x, y) in new[] { (1, 2) }); + + """, testHost, ControlKeyword("foreach"), Punctuation.OpenParen, @@ -5257,9 +5567,11 @@ await TestInMethodAsync(@" [Theory, CombinatorialData] public async Task CatchDeclarationStatement(TestHost testHost) { - await TestInMethodAsync(@" -try { } catch (Exception ex) { } -", + await TestInMethodAsync(""" + + try { } catch (Exception ex) { } + + """, testHost, ControlKeyword("try"), Punctuation.OpenCurly, @@ -5276,9 +5588,11 @@ await TestInMethodAsync(@" [Theory, CombinatorialData] public async Task TestNotNullConstraint_InsideMethod(TestHost testHost) { - await TestInMethodAsync(@" -var notnull = 0; -notnull++;", + await TestInMethodAsync(""" + + var notnull = 0; + notnull++; + """, testHost, Keyword("var"), Local("notnull"), @@ -5312,9 +5626,11 @@ await TestAsync( [Theory, CombinatorialData] public async Task TestNotNullConstraint_Type_ExistingInterface(TestHost testHost) { - await TestAsync(@" -interface notnull {} -class X where T : notnull { }", + await TestAsync(""" + + interface notnull {} + class X where T : notnull { } + """, testHost, Keyword("interface"), Interface("notnull"), @@ -5336,12 +5652,14 @@ class X where T : notnull { }", [Theory, CombinatorialData] public async Task TestNotNullConstraint_Type_ExistingInterfaceButOutOfScope(TestHost testHost) { - await TestAsync(@" -namespace OtherScope -{ - interface notnull {} -} -class X where T : notnull { }", + await TestAsync(""" + + namespace OtherScope + { + interface notnull {} + } + class X where T : notnull { } + """, testHost, Keyword("namespace"), Namespace("OtherScope"), @@ -5367,11 +5685,13 @@ class X where T : notnull { }", [Theory, CombinatorialData] public async Task TestNotNullConstraint_Method_Keyword(TestHost testHost) { - await TestAsync(@" -class X -{ - void M() where T : notnull { } -}", + await TestAsync(""" + + class X + { + void M() where T : notnull { } + } + """, testHost, Keyword("class"), Class("X"), @@ -5395,12 +5715,14 @@ void M() where T : notnull { } [Theory, CombinatorialData] public async Task TestNotNullConstraint_Method_ExistingInterface(TestHost testHost) { - await TestAsync(@" -interface notnull {} -class X -{ - void M() where T : notnull { } -}", + await TestAsync(""" + + interface notnull {} + class X + { + void M() where T : notnull { } + } + """, testHost, Keyword("interface"), Interface("notnull"), @@ -5428,15 +5750,17 @@ void M() where T : notnull { } [Theory, CombinatorialData] public async Task TestNotNullConstraint_Method_ExistingInterfaceButOutOfScope(TestHost testHost) { - await TestAsync(@" -namespace OtherScope -{ - interface notnull {} -} -class X -{ - void M() where T : notnull { } -}", + await TestAsync(""" + + namespace OtherScope + { + interface notnull {} + } + class X + { + void M() where T : notnull { } + } + """, testHost, Keyword("namespace"), Namespace("OtherScope"), @@ -5489,9 +5813,11 @@ await TestAsync( [Theory, CombinatorialData] public async Task TestNotNullConstraint_Delegate_ExistingInterface(TestHost testHost) { - await TestAsync(@" -interface notnull {} -delegate void D() where T : notnull;", + await TestAsync(""" + + interface notnull {} + delegate void D() where T : notnull; + """, testHost, Keyword("interface"), Interface("notnull"), @@ -5515,12 +5841,14 @@ interface notnull {} [Theory, CombinatorialData] public async Task TestNotNullConstraint_Delegate_ExistingInterfaceButOutOfScope(TestHost testHost) { - await TestAsync(@" -namespace OtherScope -{ - interface notnull {} -} -delegate void D() where T : notnull;", + await TestAsync(""" + + namespace OtherScope + { + interface notnull {} + } + delegate void D() where T : notnull; + """, testHost, Keyword("namespace"), Namespace("OtherScope"), @@ -5548,14 +5876,16 @@ interface notnull {} [Theory, CombinatorialData] public async Task TestNotNullConstraint_LocalFunction_Keyword(TestHost testHost) { - await TestAsync(@" -class X -{ - void N() - { - void M() where T : notnull { } - } -}", + await TestAsync(""" + + class X + { + void N() + { + void M() where T : notnull { } + } + } + """, testHost, Keyword("class"), Class("X"), @@ -5585,15 +5915,17 @@ void M() where T : notnull { } [Theory, CombinatorialData] public async Task TestNotNullConstraint_LocalFunction_ExistingInterface(TestHost testHost) { - await TestAsync(@" -interface notnull {} -class X -{ - void N() - { - void M() where T : notnull { } - } -}", + await TestAsync(""" + + interface notnull {} + class X + { + void N() + { + void M() where T : notnull { } + } + } + """, testHost, Keyword("interface"), Interface("notnull"), @@ -5627,18 +5959,20 @@ void M() where T : notnull { } [Theory, CombinatorialData] public async Task TestNotNullConstraint_LocalFunction_ExistingInterfaceButOutOfScope(TestHost testHost) { - await TestAsync(@" -namespace OtherScope -{ - interface notnull {} -} -class X -{ - void N() - { - void M() where T : notnull { } - } -}", + await TestAsync(""" + + namespace OtherScope + { + interface notnull {} + } + class X + { + void N() + { + void M() where T : notnull { } + } + } + """, testHost, Keyword("namespace"), Namespace("OtherScope"), @@ -5677,11 +6011,13 @@ void M() where T : notnull { } [WorkItem("https://github.com/dotnet/roslyn/issues/45807")] public async Task FunctionPointer(TestHost testHost) { - var code = @" -class C -{ - delegate* unmanaged[Stdcall, SuppressGCTransition] x; -}"; + var code = """ + + class C + { + delegate* unmanaged[Stdcall, SuppressGCTransition] x; + } + """; await TestAsync(code, testHost, @@ -5735,9 +6071,11 @@ public async Task TestXmlAttributeNameSpan1() [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48094")] public async Task TestXmlAttributeNameSpan2() { - var source = @" -/// "; + var source = """ + + /// + """; using var workspace = CreateWorkspace(source, options: null, TestHost.InProcess); var document = workspace.CurrentSolution.Projects.First().Documents.First(); @@ -5765,14 +6103,16 @@ public async Task TestXmlAttributeNameSpan2() [CombinatorialData] public async Task TestStaticLocalFunction(TestHost testHost) { - var code = @" -class C -{ - public static void M() - { - static void LocalFunc() { } - } -}"; + var code = """ + + class C + { + public static void M() + { + static void LocalFunc() { } + } + } + """; await TestAsync(code, testHost, @@ -5803,14 +6143,16 @@ await TestAsync(code, [CombinatorialData] public async Task TestConstantLocalVariable(TestHost testHost) { - var code = @" -class C -{ - public static void M() - { - const int Zero = 0; - } -}"; + var code = """ + + class C + { + public static void M() + { + const int Zero = 0; + } + } + """; await TestAsync(code, testHost, @@ -5839,14 +6181,16 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task TestRawStringLiteral(TestHost testHost) { - var code = @" -class C -{ - public static void M(int x) - { - var s = """"""Hello world""""""; - } -}"; + var code = """" + + class C + { + public static void M(int x) + { + var s = """Hello world"""; + } + } + """"; await TestAsync(code, testHost, @@ -5866,7 +6210,9 @@ await TestAsync(code, Keyword("var"), Local("s"), Operators.Equals, - String("\"\"\"Hello world\"\"\""), + String("""" + """Hello world""" + """"), Punctuation.Semicolon, Punctuation.CloseCurly, Punctuation.CloseCurly); @@ -5875,14 +6221,16 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task TestRawStringLiteralUtf8_01(TestHost testHost) { - var code = @" -class C -{ - public static void M(int x) - { - var s = """"""Hello world""""""u8; - } -}"; + var code = """" + + class C + { + public static void M(int x) + { + var s = """Hello world"""u8; + } + } + """"; await TestAsync(code, testHost, @@ -5902,7 +6250,9 @@ await TestAsync(code, Keyword("var"), Local("s"), Operators.Equals, - String("\"\"\"Hello world\"\"\""), + String("""" + """Hello world""" + """"), Keyword("u8"), Punctuation.Semicolon, Punctuation.CloseCurly, @@ -5912,14 +6262,16 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task TestRawStringLiteralUtf8_02(TestHost testHost) { - var code = @" -class C -{ - public static void M(int x) - { - var s = """"""Hello world""""""U8; - } -}"; + var code = """" + + class C + { + public static void M(int x) + { + var s = """Hello world"""U8; + } + } + """"; await TestAsync(code, testHost, @@ -5939,7 +6291,9 @@ await TestAsync(code, Keyword("var"), Local("s"), Operators.Equals, - String("\"\"\"Hello world\"\"\""), + String("""" + """Hello world""" + """"), Keyword("U8"), Punctuation.Semicolon, Punctuation.CloseCurly, @@ -5949,16 +6303,18 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task TestRawStringLiteralMultiline(TestHost testHost) { - var code = @" -class C -{ - public static void M(int x) - { - var s = """""" - Hello world - """"""; - } -}"; + var code = """" + + class C + { + public static void M(int x) + { + var s = """ + Hello world + """; + } + } + """"; await TestAsync(code, testHost, @@ -5978,9 +6334,11 @@ await TestAsync(code, Keyword("var"), Local("s"), Operators.Equals, - String(@""""""" - Hello world - """""""), + String("""" + """ + Hello world + """ + """"), Punctuation.Semicolon, Punctuation.CloseCurly, Punctuation.CloseCurly); @@ -5989,16 +6347,18 @@ Hello world [Theory, CombinatorialData] public async Task TestRawStringLiteralMultilineUtf8_01(TestHost testHost) { - var code = @" -class C -{ - public static void M(int x) - { - var s = """""" - Hello world - """"""u8; - } -}"; + var code = """" + + class C + { + public static void M(int x) + { + var s = """ + Hello world + """u8; + } + } + """"; await TestAsync(code, testHost, @@ -6018,9 +6378,11 @@ await TestAsync(code, Keyword("var"), Local("s"), Operators.Equals, - String(@""""""" - Hello world - """""""), + String("""" + """ + Hello world + """ + """"), Keyword("u8"), Punctuation.Semicolon, Punctuation.CloseCurly, @@ -6030,16 +6392,18 @@ Hello world [Theory, CombinatorialData] public async Task TestRawStringLiteralMultilineUtf8_02(TestHost testHost) { - var code = @" -class C -{ - public static void M(int x) - { - var s = """""" - Hello world - """"""U8; - } -}"; + var code = """" + + class C + { + public static void M(int x) + { + var s = """ + Hello world + """U8; + } + } + """"; await TestAsync(code, testHost, @@ -6059,9 +6423,11 @@ await TestAsync(code, Keyword("var"), Local("s"), Operators.Equals, - String(@""""""" - Hello world - """""""), + String("""" + """ + Hello world + """ + """"), Keyword("U8"), Punctuation.Semicolon, Punctuation.CloseCurly, @@ -6071,14 +6437,16 @@ Hello world [Theory, CombinatorialData] public async Task TestRawStringLiteralInterpolation1(TestHost testHost) { - var code = @" -class C -{ - public static void M(int x) - { - var s = $""""""{x}""""""; - } -}"; + var code = """" + + class C + { + public static void M(int x) + { + var s = $"""{x}"""; + } + } + """"; await TestAsync(code, testHost, @@ -6098,11 +6466,15 @@ await TestAsync(code, Keyword("var"), Local("s"), Operators.Equals, - String("$\"\"\""), + String("""" + $""" + """"), Punctuation.OpenCurly, Identifier("x"), Punctuation.CloseCurly, - String("\"\"\""), + String("""" + """ + """"), Punctuation.Semicolon, Punctuation.CloseCurly, Punctuation.CloseCurly); @@ -6111,14 +6483,16 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task TestRawStringLiteralInterpolation2(TestHost testHost) { - var code = @" -class C -{ - public static void M(int x) - { - var s = $$""""""{{x}}""""""; - } -}"; + var code = """" + + class C + { + public static void M(int x) + { + var s = $$"""{{x}}"""; + } + } + """"; await TestAsync(code, testHost, @@ -6138,11 +6512,15 @@ await TestAsync(code, Keyword("var"), Local("s"), Operators.Equals, - String("$$\"\"\""), + String("""" + $$""" + """"), PunctuationText("{{"), Identifier("x"), PunctuationText("}}"), - String("\"\"\""), + String("""" + """ + """"), Punctuation.Semicolon, Punctuation.CloseCurly, Punctuation.CloseCurly); @@ -6151,14 +6529,16 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task TestRawStringLiteralInterpolation3(TestHost testHost) { - var code = @" -class C -{ - public static void M(int x) - { - var s = $$""""""{{{x}}}""""""; - } -}"; + var code = """" + + class C + { + public static void M(int x) + { + var s = $$"""{{{x}}}"""; + } + } + """"; await TestAsync(code, testHost, @@ -6178,13 +6558,17 @@ await TestAsync(code, Keyword("var"), Local("s"), Operators.Equals, - String("$$\"\"\""), + String("""" + $$""" + """"), String("{"), PunctuationText("{{"), Identifier("x"), PunctuationText("}}"), String("}"), - String("\"\"\""), + String("""" + """ + """"), Punctuation.Semicolon, Punctuation.CloseCurly, Punctuation.CloseCurly); @@ -6194,10 +6578,12 @@ await TestAsync(code, public async Task CheckedUserDefinedOperators_01(TestHost testHost) { await TestInClassAsync( -@" -static T operator checked -(T a) -{ -}", + """ + + static T operator checked -(T a) + { + } + """, testHost, Keyword("static"), Identifier("T"), @@ -6216,10 +6602,12 @@ await TestInClassAsync( public async Task CheckedUserDefinedOperators_02(TestHost testHost) { await TestInClassAsync( -@" -static T operator checked +(T a, T b) -{ -}", + """ + + static T operator checked +(T a, T b) + { + } + """, testHost, Keyword("static"), Identifier("T"), @@ -6241,10 +6629,12 @@ await TestInClassAsync( public async Task CheckedUserDefinedOperators_03(TestHost testHost) { await TestInClassAsync( -@" -static explicit operator checked T(T a) -{ -}", + """ + + static explicit operator checked T(T a) + { + } + """, testHost, Keyword("static"), Keyword("explicit"), @@ -6263,10 +6653,12 @@ static explicit operator checked T(T a) public async Task CheckedUserDefinedOperators_04(TestHost testHost) { await TestInClassAsync( -@" -static T I1.operator checked -(T a) -{ -}", + """ + + static T I1.operator checked -(T a) + { + } + """, testHost, Keyword("static"), Identifier("T"), @@ -6287,10 +6679,12 @@ await TestInClassAsync( public async Task CheckedUserDefinedOperators_05(TestHost testHost) { await TestInClassAsync( -@" -static T I1.operator checked +(T a, T b) -{ -}", + """ + + static T I1.operator checked +(T a, T b) + { + } + """, testHost, Keyword("static"), Identifier("T"), @@ -6314,10 +6708,12 @@ await TestInClassAsync( public async Task CheckedUserDefinedOperators_06(TestHost testHost) { await TestInClassAsync( -@" -static explicit I1.operator checked T(T a) -{ -}", + """ + + static explicit I1.operator checked T(T a) + { + } + """, testHost, Keyword("static"), Keyword("explicit"), @@ -6338,10 +6734,12 @@ static explicit I1.operator checked T(T a) public async Task UnsignedRightShift_01(TestHost testHost) { await TestInClassAsync( -@" -static T operator >>>(T a, int b) -{ -}", + """ + + static T operator >>>(T a, int b) + { + } + """, testHost, Keyword("static"), Identifier("T"), @@ -6362,10 +6760,12 @@ await TestInClassAsync( public async Task UnsignedRightShift_02(TestHost testHost) { await TestInClassAsync( -@" -static T I1.operator checked >>>(T a, T b) -{ -}", + """ + + static T I1.operator checked >>>(T a, T b) + { + } + """, testHost, Keyword("static"), Identifier("T"), @@ -6389,12 +6789,14 @@ await TestInClassAsync( public async Task TestExclamationExclamation(TestHost testHost) { await TestAsync( -@"class C -{ - void M(string v!!) - { - } -}", + """ + class C + { + void M(string v!!) + { + } + } + """, testHost, Keyword("class"), Class("C"), @@ -6610,7 +7012,9 @@ void M() Keyword("string"), Parameter("s"), Operators.Equals, - String(@"""a string"""), + String(""" + "a string" + """), Punctuation.CloseParen, Operators.EqualsGreaterThan, Identifier("s"), From a0dce1daada73c352a9f233a909a72a542ac3ebc Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 6 Nov 2024 11:37:51 +0100 Subject: [PATCH 190/508] Update dependencies from https://github.com/dotnet/source-build-reference-packages build 20241104.1 (#75725) Microsoft.SourceBuild.Intermediate.source-build-reference-packages From Version 10.0.0-alpha.1.24530.1 -> To Version 10.0.0-alpha.1.24554.1 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 3cbf1c8666460..c356039a3b19e 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -8,9 +8,9 @@ - + https://github.com/dotnet/source-build-reference-packages - 136e43e45e20bd58bf86eeabba0a0fa7e1a4b3ae + e781442b88c4d939f06a94f38f24a5dc18c383d4 From a1f2017258e9bc3d348fd75b531808251abeb8ec Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Wed, 6 Nov 2024 09:10:19 -0800 Subject: [PATCH 191/508] Update Language Feature Status.md with VB information (#75763) --- docs/Language Feature Status.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/Language Feature Status.md b/docs/Language Feature Status.md index 46ad586959a4b..0ebb6d44a313e 100644 --- a/docs/Language Feature Status.md +++ b/docs/Language Feature Status.md @@ -6,7 +6,7 @@ and will be updated as work progresses, features are added / removed, and as wor This is not an exhaustive list of our features but rather the ones which have active development efforts behind them. -# Working Set +# Working Set C# | Feature | Branch | State | Developer | Reviewer | IDE Buddy | LDM Champ | | ------- | ------ | ----- | --------- | -------- | --------- | --------- | @@ -16,6 +16,13 @@ efforts behind them. | [`field` keyword in properties](https://github.com/dotnet/csharplang/issues/140) | [field-keyword](https://github.com/dotnet/roslyn/tree/features/field-keyword) | [Merged into 17.12p3](https://github.com/dotnet/roslyn/issues/57012) | [Youssef1313](https://github.com/Youssef1313), [cston](https://github.com/cston) | [333fred](https://github.com/333fred), [RikkiGibson](https://github.com/RikkiGibson) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | | [First-class Span Types](https://github.com/dotnet/csharplang/issues/7905) | [FirstClassSpan](https://github.com/dotnet/roslyn/tree/features/FirstClassSpan) | [Merged into 17.13p1](https://github.com/dotnet/roslyn/issues/73445) | [jjonescz](https://github.com/jjonescz) | [cston](https://github.com/cston), [333fred](https://github.com/333fred) | | [333fred](https://github.com/333fred), [stephentoub](https://github.com/stephentoub) | +# Working Set VB + +| Feature | Branch | State | Developer | Reviewer | IDE Buddy | LDM Champ | +| ------- | ------ | ----- | --------- | -------- | --------- | --------- | +| Recognizing 'unmanaged' constraint | main | [Merged into 17.13 P2](https://github.com/dotnet/roslyn/pull/75665) | [AlekseyTs](https://github.com/AlekseyTs) | [cston](https://github.com/cston), [333fred](https://github.com/333fred) | (no IDE impact) | [jaredpar](https://github.com/jaredpar) | + + # C# 13.0 | Feature | Branch | State | Developer | Reviewer | IDE Buddy | LDM Champ | From c0c1fa2256819238acfcee8d3339854b313197f5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 6 Nov 2024 09:28:09 -0800 Subject: [PATCH 192/508] In progress --- .../AbstractUseAutoPropertyCodeFixProvider.cs | 54 +++++++++++++++++-- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs b/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs index 77ef502baac75..6c7314afa89ac 100644 --- a/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs +++ b/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; @@ -24,13 +25,46 @@ namespace Microsoft.CodeAnalysis.UseAutoProperty; using static UseAutoPropertiesHelpers; -internal abstract class AbstractUseAutoPropertyCodeFixProvider : CodeFixProvider + +internal abstract class AbstractUseAutoPropertyCodeFixProvider + : CodeFixProvider + where TProvider : AbstractUseAutoPropertyCodeFixProvider where TTypeDeclarationSyntax : SyntaxNode where TPropertyDeclaration : SyntaxNode where TVariableDeclarator : SyntaxNode where TConstructorDeclaration : SyntaxNode where TExpression : SyntaxNode { + private sealed class UseAutoPropertyFixAllProvider(TProvider provider) : FixAllProvider + { + public override Task GetFixAsync(FixAllContext fixAllContext) + { + return DefaultFixAllProviderHelpers.GetFixAsync( + fixAllContext.GetDefaultFixAllTitle(), fixAllContext, FixAllContextsHelperAsync); + } + + private async Task FixAllContextsHelperAsync(FixAllContext originalContext, ImmutableArray contexts) + { + var cancellationToken = originalContext.CancellationToken; + + var solutionEditor = new SolutionEditor(originalContext.Solution); + + foreach (var currentContext in contexts) + { + var documentToDiagnostics = await FixAllContextHelper.GetDocumentDiagnosticsToFixAsync(currentContext).ConfigureAwait(false); + foreach (var (_, diagnostics) in documentToDiagnostics) + { + foreach (var diagnostic in diagnostics) + { + await provider.ProcessResultAsync(solutionEditor, diagnostic, cancellationToken).ConfigureAwait(false); + } + } + } + + return solutionEditor.GetChangedSolution(); + } + } + protected static SyntaxAnnotation SpecializedFormattingAnnotation = new(); public sealed override ImmutableArray FixableDiagnosticIds @@ -60,6 +94,8 @@ protected abstract Task UpdatePropertyAsync( public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) { + var solution = context.Document.Project.Solution; + foreach (var diagnostic in context.Diagnostics) { var priority = diagnostic.Severity == DiagnosticSeverity.Hidden @@ -68,7 +104,7 @@ public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) context.RegisterCodeFix(CodeAction.SolutionChangeAction.Create( AnalyzersResources.Use_auto_property, - cancellationToken => ProcessResultAsync(context, diagnostic, cancellationToken), + cancellationToken => ProcessResultAsync(solution, diagnostic, cancellationToken), equivalenceKey: nameof(AnalyzersResources.Use_auto_property), priority), diagnostic); @@ -77,14 +113,24 @@ public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) return Task.CompletedTask; } - private async Task ProcessResultAsync(CodeFixContext context, Diagnostic diagnostic, CancellationToken cancellationToken) + private async Task ProcessResultAsync( + Solution solution, Diagnostic diagnostic, CancellationToken cancellationToken) + { + var editor = new SolutionEditor(solution); + await ProcessResultAsync(editor, diagnostic, cancellationToken).ConfigureAwait(false); + return editor.GetChangedSolution(); + } + + private async Task ProcessResultAsync( + SolutionEditor solutionEditor, Diagnostic diagnostic, CancellationToken cancellationToken) { var locations = diagnostic.AdditionalLocations; var propertyLocation = locations[0]; var declaratorLocation = locations[1]; - var solution = context.Document.Project.Solution; + var solution = solutionEditor.OriginalSolution; + var declarator = (TVariableDeclarator)declaratorLocation.FindNode(cancellationToken); var fieldDocument = solution.GetRequiredDocument(declarator.SyntaxTree); var fieldSemanticModel = await fieldDocument.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); From a408e6275919ded992ac61489cec21e3990c1adc Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 6 Nov 2024 11:04:33 -0800 Subject: [PATCH 193/508] Add tests --- .../UseAutoProperty/UseAutoPropertyTests.cs | 50 ++++- .../UseAutoPropertyTests_Field.cs | 68 +++++++ .../CSharpUseAutoPropertyCodeFixProvider.cs | 5 +- .../AbstractUseAutoPropertyCodeFixProvider.cs | 177 ++++++++++-------- .../UseAutoPropertyFixAllProvider.cs | 56 ++++++ ...sualBasicUseAutoPropertyCodeFixProvider.vb | 2 +- .../CodeGeneration/CSharpSyntaxTokens.cs | 1 + 7 files changed, 278 insertions(+), 81 deletions(-) create mode 100644 src/Features/Core/Portable/UseAutoProperty/UseAutoPropertyFixAllProvider.cs diff --git a/src/Analyzers/CSharp/Tests/UseAutoProperty/UseAutoPropertyTests.cs b/src/Analyzers/CSharp/Tests/UseAutoProperty/UseAutoPropertyTests.cs index c819c4aad6ad3..96306663905c7 100644 --- a/src/Analyzers/CSharp/Tests/UseAutoProperty/UseAutoPropertyTests.cs +++ b/src/Analyzers/CSharp/Tests/UseAutoProperty/UseAutoPropertyTests.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.CSharp.UseAutoProperty; using Microsoft.CodeAnalysis.Diagnostics; @@ -1877,7 +1878,7 @@ class Class [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23216")] [WorkItem("https://github.com/dotnet/roslyn/issues/23215")] - public async Task TestFixAllInDocument() + public async Task TestFixAllInDocument1() { await TestInRegularAndScript1Async( """ @@ -1914,6 +1915,53 @@ class Class """); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26527")] + public async Task TestFixAllInDocument2() + { + await TestInRegularAndScript1Async( + """ + internal struct StringFormat + { + private readonly object {|FixAllInDocument:_argument1|}; + private readonly object _argument2; + private readonly object _argument3; + private readonly object[] _arguments; + + public object Argument1 + { + get { return _argument1; } + } + + public object Argument2 + { + get { return _argument2; } + } + + public object Argument3 + { + get { return _argument3; } + } + + public object[] Arguments + { + get { return _arguments; } + } + } + """, + """ + internal struct StringFormat + { + public object Argument1 { get; } + + public object Argument2 { get; } + + public object Argument3 { get; } + + public object[] Arguments { get; } + } + """); + } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23735")] public async Task ExplicitInterfaceImplementationGetterOnly() { diff --git a/src/Analyzers/CSharp/Tests/UseAutoProperty/UseAutoPropertyTests_Field.cs b/src/Analyzers/CSharp/Tests/UseAutoProperty/UseAutoPropertyTests_Field.cs index f738c92812ca5..85687a5791c4b 100644 --- a/src/Analyzers/CSharp/Tests/UseAutoProperty/UseAutoPropertyTests_Field.cs +++ b/src/Analyzers/CSharp/Tests/UseAutoProperty/UseAutoPropertyTests_Field.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; @@ -68,6 +69,38 @@ string P """, parseOptions: Preview); } + [Fact] + public async Task TestFieldWithInitializer() + { + await TestInRegularAndScriptAsync( + """ + class Class + { + [|string s = ""|]; + + string P + { + get + { + return s.Trim(); + } + } + } + """, + """ + class Class + { + string P + { + get + { + return field.Trim(); + } + } = ""; + } + """, parseOptions: Preview); + } + [Fact] public async Task TestFieldAccessOffOfThis() { @@ -1471,4 +1504,39 @@ void M(ref int i) { } } """, parseOptions: Preview); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26527")] + public async Task TestFixAllInDocument3() + { + await TestInRegularAndScript1Async( + """ + using System; + + public sealed class SomeViewModel + { + private bool {|FixAllInDocument:a|} = true; + public bool A { get => a; set => Set(ref a, value); } + + private bool b = true; + public bool B { get => b; set => Set(ref b, value); } + + private bool c = true; + public bool C { get => c; set => Set(ref c, value); } + + private void Set(ref T field, T value) => throw new NotImplementedException(); + } + """, + """ + using System; + + public sealed class SomeViewModel + { + public bool A { get; set => Set(ref field, value); } = true; + public bool B { get; set => Set(ref field, value); } = true; + public bool C { get; set => Set(ref field, value); } = true; + + private void Set(ref T field, T value) => throw new NotImplementedException(); + } + """, new TestParameters(parseOptions: Preview)); + } } diff --git a/src/Features/CSharp/Portable/UseAutoProperty/CSharpUseAutoPropertyCodeFixProvider.cs b/src/Features/CSharp/Portable/UseAutoProperty/CSharpUseAutoPropertyCodeFixProvider.cs index 8a9422c4e0a2a..a14ed3349d2bb 100644 --- a/src/Features/CSharp/Portable/UseAutoProperty/CSharpUseAutoPropertyCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseAutoProperty/CSharpUseAutoPropertyCodeFixProvider.cs @@ -32,6 +32,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseAutoProperty; [method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] internal sealed partial class CSharpUseAutoPropertyCodeFixProvider() : AbstractUseAutoPropertyCodeFixProvider< + CSharpUseAutoPropertyCodeFixProvider, TypeDeclarationSyntax, PropertyDeclarationSyntax, VariableDeclaratorSyntax, @@ -130,8 +131,8 @@ protected override Task UpdatePropertyAsync( // Move any field initializer over to the property as well. if (fieldInitializer != null) { - updatedProperty = updatedProperty - .WithInitializer(EqualsValueClause(fieldInitializer)) + updatedProperty = updatedProperty.WithoutTrailingTrivia() + .WithInitializer(EqualsValueClause(EqualsToken.WithLeadingTrivia(Space).WithTrailingTrivia(Space), fieldInitializer)) .WithSemicolonToken(SemicolonToken); } diff --git a/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs b/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs index 6c7314afa89ac..63b0741f9df5c 100644 --- a/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs +++ b/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs @@ -10,7 +10,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; @@ -25,8 +24,7 @@ namespace Microsoft.CodeAnalysis.UseAutoProperty; using static UseAutoPropertiesHelpers; - -internal abstract class AbstractUseAutoPropertyCodeFixProvider +internal abstract partial class AbstractUseAutoPropertyCodeFixProvider : CodeFixProvider where TProvider : AbstractUseAutoPropertyCodeFixProvider where TTypeDeclarationSyntax : SyntaxNode @@ -35,42 +33,13 @@ internal abstract class AbstractUseAutoPropertyCodeFixProvider GetFixAsync(FixAllContext fixAllContext) - { - return DefaultFixAllProviderHelpers.GetFixAsync( - fixAllContext.GetDefaultFixAllTitle(), fixAllContext, FixAllContextsHelperAsync); - } - - private async Task FixAllContextsHelperAsync(FixAllContext originalContext, ImmutableArray contexts) - { - var cancellationToken = originalContext.CancellationToken; - - var solutionEditor = new SolutionEditor(originalContext.Solution); - - foreach (var currentContext in contexts) - { - var documentToDiagnostics = await FixAllContextHelper.GetDocumentDiagnosticsToFixAsync(currentContext).ConfigureAwait(false); - foreach (var (_, diagnostics) in documentToDiagnostics) - { - foreach (var diagnostic in diagnostics) - { - await provider.ProcessResultAsync(solutionEditor, diagnostic, cancellationToken).ConfigureAwait(false); - } - } - } - - return solutionEditor.GetChangedSolution(); - } - } - protected static SyntaxAnnotation SpecializedFormattingAnnotation = new(); public sealed override ImmutableArray FixableDiagnosticIds => [IDEDiagnosticIds.UseAutoPropertyDiagnosticId]; - public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; + public sealed override FixAllProvider GetFixAllProvider() + => new UseAutoPropertyFixAllProvider((TProvider)this); protected abstract TPropertyDeclaration GetPropertyDeclaration(SyntaxNode node); protected abstract SyntaxNode GetNodeToRemove(TVariableDeclarator declarator); @@ -104,7 +73,7 @@ public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) context.RegisterCodeFix(CodeAction.SolutionChangeAction.Create( AnalyzersResources.Use_auto_property, - cancellationToken => ProcessResultAsync(solution, diagnostic, cancellationToken), + cancellationToken => ProcessResultAsync(solution, solution, diagnostic, cancellationToken), equivalenceKey: nameof(AnalyzersResources.Use_auto_property), priority), diagnostic); @@ -113,33 +82,46 @@ public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) return Task.CompletedTask; } - private async Task ProcessResultAsync( - Solution solution, Diagnostic diagnostic, CancellationToken cancellationToken) - { - var editor = new SolutionEditor(solution); - await ProcessResultAsync(editor, diagnostic, cancellationToken).ConfigureAwait(false); - return editor.GetChangedSolution(); - } + //private async Task ProcessResultAsync( + // Solution solution, Diagnostic diagnostic, CancellationToken cancellationToken) + //{ + // var editor = new SolutionEditor(solution); + // await ProcessResultAsync(editor, diagnostic, cancellationToken).ConfigureAwait(false); + // return editor.GetChangedSolution(); + //} //private async Task ProcessResultAsync( + // Solution solution, Diagnostic diagnostic, CancellationToken cancellationToken) + //{ + // var editor = new SolutionEditor(solution); + // await ProcessResultAsync(editor, diagnostic, cancellationToken).ConfigureAwait(false); + // return editor.GetChangedSolution(); + //} private async Task ProcessResultAsync( - SolutionEditor solutionEditor, Diagnostic diagnostic, CancellationToken cancellationToken) + Solution originalSolution, Solution currentSolution, Diagnostic diagnostic, CancellationToken cancellationToken) { + var (field, property) = await MapDiagnosticToCurrentSolutionAsync( + diagnostic, originalSolution, currentSolution, cancellationToken).ConfigureAwait(false); + + if (field == null || property == null) + return currentSolution; + var locations = diagnostic.AdditionalLocations; - var propertyLocation = locations[0]; - var declaratorLocation = locations[1]; + //var propertyLocation = locations[0]; + //var declaratorLocation = locations[1]; - var solution = solutionEditor.OriginalSolution; + //var declarator = (TVariableDeclarator)declaratorLocation.FindNode(cancellationToken); + //var fieldDocument = originalSolution.GetRequiredDocument(declarator.SyntaxTree); + //var fieldSemanticModel = await fieldDocument.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + //var fieldSymbol = (IFieldSymbol)fieldSemanticModel.GetRequiredDeclaredSymbol(declarator, cancellationToken); - var declarator = (TVariableDeclarator)declaratorLocation.FindNode(cancellationToken); - var fieldDocument = solution.GetRequiredDocument(declarator.SyntaxTree); - var fieldSemanticModel = await fieldDocument.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var fieldSymbol = (IFieldSymbol)fieldSemanticModel.GetRequiredDeclaredSymbol(declarator, cancellationToken); + //var property = GetPropertyDeclaration(propertyLocation.FindNode(cancellationToken)); + //var propertyDocument = originalSolution.GetRequiredDocument(property.SyntaxTree); + //var propertySemanticModel = await propertyDocument.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + //var propertySymbol = (IPropertySymbol)propertySemanticModel.GetRequiredDeclaredSymbol(property, cancellationToken); - var property = GetPropertyDeclaration(propertyLocation.FindNode(cancellationToken)); - var propertyDocument = solution.GetRequiredDocument(property.SyntaxTree); - var propertySemanticModel = await propertyDocument.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var propertySymbol = (IPropertySymbol)propertySemanticModel.GetRequiredDeclaredSymbol(property, cancellationToken); + var fieldDocument = currentSolution.GetRequiredDocument(field.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken).SyntaxTree); + var propertyDocument = currentSolution.GetRequiredDocument(property.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken).SyntaxTree); var isTrivialGetAccessor = diagnostic.Properties.ContainsKey(IsTrivialGetAccessor); var isTrivialSetAccessor = diagnostic.Properties.ContainsKey(IsTrivialSetAccessor); @@ -151,24 +133,27 @@ private async Task ProcessResultAsync( var renameOptions = new SymbolRenameOptions(); var fieldLocations = await Renamer.FindRenameLocationsAsync( - solution, fieldSymbol, renameOptions, cancellationToken).ConfigureAwait(false); + currentSolution, field, renameOptions, cancellationToken).ConfigureAwait(false); + + var declarator = (TVariableDeclarator)field.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken); + var propertyDeclaration = GetPropertyDeclaration(property.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken)); // First, create the updated property we want to replace the old property with var isWrittenToOutsideOfConstructor = IsWrittenToOutsideOfConstructorOrProperty( - fieldSymbol, fieldLocations, property, cancellationToken); + field, fieldLocations, propertyDeclaration, cancellationToken); if (!isTrivialGetAccessor || - (propertySymbol.SetMethod != null && !isTrivialSetAccessor)) + (property.SetMethod != null && !isTrivialSetAccessor)) { // We have at least a non-trivial getter/setter. Those will not be rewritten to `get;/set;`. As such, we // need to update the property to reference `field` or itself instead of the actual field. - property = RewriteFieldReferencesInProperty(property, fieldLocations, cancellationToken); + propertyDeclaration = RewriteFieldReferencesInProperty(propertyDeclaration, fieldLocations, cancellationToken); } var updatedProperty = await UpdatePropertyAsync( propertyDocument, compilation, - fieldSymbol, propertySymbol, - declarator, property, + field, property, + declarator, propertyDeclaration, isWrittenToOutsideOfConstructor, isTrivialGetAccessor, isTrivialSetAccessor, cancellationToken).ConfigureAwait(false); @@ -207,31 +192,31 @@ private async Task ProcessResultAsync( var filteredLocations = fieldLocations.Filter( (documentId, span) => - fieldDocument.Id == documentId ? !span.IntersectsWith(declaratorLocation.SourceSpan) : true && // The span check only makes sense if we are in the same file - CanEditDocument(solution, documentId, linkedFiles, canEdit)); + fieldDocument.Id == documentId ? !span.IntersectsWith(declarator.Span) : true && // The span check only makes sense if we are in the same file + CanEditDocument(currentSolution, documentId, linkedFiles, canEdit)); var resolution = await filteredLocations.ResolveConflictsAsync( - fieldSymbol, propertySymbol.Name, - nonConflictSymbolKeys: [propertySymbol.GetSymbolKey(cancellationToken)], + field, property.Name, + nonConflictSymbolKeys: [property.GetSymbolKey(cancellationToken)], cancellationToken).ConfigureAwait(false); Contract.ThrowIfFalse(resolution.IsSuccessful); - solution = resolution.NewSolution; + currentSolution = resolution.NewSolution; // Now find the field and property again post rename. - fieldDocument = solution.GetRequiredDocument(fieldDocument.Id); - propertyDocument = solution.GetRequiredDocument(propertyDocument.Id); + fieldDocument = currentSolution.GetRequiredDocument(fieldDocument.Id); + propertyDocument = currentSolution.GetRequiredDocument(propertyDocument.Id); Debug.Assert(fieldDocument.Project == propertyDocument.Project); compilation = await fieldDocument.Project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); - fieldSymbol = (IFieldSymbol?)fieldSymbol.GetSymbolKey(cancellationToken).Resolve(compilation, cancellationToken: cancellationToken).Symbol; - propertySymbol = (IPropertySymbol?)propertySymbol.GetSymbolKey(cancellationToken).Resolve(compilation, cancellationToken: cancellationToken).Symbol; - Contract.ThrowIfTrue(fieldSymbol == null || propertySymbol == null); + field = (IFieldSymbol?)field.GetSymbolKey(cancellationToken).Resolve(compilation, cancellationToken: cancellationToken).Symbol; + property = (IPropertySymbol?)property.GetSymbolKey(cancellationToken).Resolve(compilation, cancellationToken: cancellationToken).Symbol; + Contract.ThrowIfTrue(field == null || property == null); - declarator = (TVariableDeclarator)await fieldSymbol.DeclaringSyntaxReferences[0].GetSyntaxAsync(cancellationToken).ConfigureAwait(false); - property = GetPropertyDeclaration(await propertySymbol.DeclaringSyntaxReferences[0].GetSyntaxAsync(cancellationToken).ConfigureAwait(false)); + declarator = (TVariableDeclarator)field.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken); + propertyDeclaration = GetPropertyDeclaration(property.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken)); var nodeToRemove = GetNodeToRemove(declarator); @@ -268,7 +253,7 @@ private async Task ProcessResultAsync( { var syntaxFacts = fieldDocument.GetRequiredLanguageService(); var bannerService = fieldDocument.GetRequiredLanguageService(); - if (WillRemoveFirstFieldInTypeDirectlyAboveProperty(syntaxFacts, property, nodeToRemove) && + if (WillRemoveFirstFieldInTypeDirectlyAboveProperty(syntaxFacts, propertyDeclaration, nodeToRemove) && bannerService.GetLeadingBlankLines(nodeToRemove).Length == 0) { updatedProperty = bannerService.GetNodeWithoutLeadingBlankLines(updatedProperty); @@ -282,13 +267,13 @@ private async Task ProcessResultAsync( var declaratorTreeRoot = await fieldDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var editor = new SyntaxEditor(declaratorTreeRoot, fieldDocument.Project.Solution.Services); - editor.ReplaceNode(property, updatedProperty); + editor.ReplaceNode(propertyDeclaration, updatedProperty); editor.RemoveNode(nodeToRemove, syntaxRemoveOptions); var newRoot = editor.GetChangedRoot(); newRoot = await FormatAsync(newRoot, fieldDocument, updatedProperty, cancellationToken).ConfigureAwait(false); - return solution.WithDocumentSyntaxRoot(fieldDocument.Id, newRoot); + return currentSolution.WithDocumentSyntaxRoot(fieldDocument.Id, newRoot); } else { @@ -298,18 +283,56 @@ private async Task ProcessResultAsync( var newFieldTreeRoot = fieldTreeRoot.RemoveNode(nodeToRemove, syntaxRemoveOptions); Contract.ThrowIfNull(newFieldTreeRoot); - var newPropertyTreeRoot = propertyTreeRoot.ReplaceNode(property, updatedProperty); + var newPropertyTreeRoot = propertyTreeRoot.ReplaceNode(propertyDeclaration, updatedProperty); newFieldTreeRoot = await FormatAsync(newFieldTreeRoot, fieldDocument, updatedProperty, cancellationToken).ConfigureAwait(false); newPropertyTreeRoot = await FormatAsync(newPropertyTreeRoot, propertyDocument, updatedProperty, cancellationToken).ConfigureAwait(false); - var updatedSolution = solution.WithDocumentSyntaxRoot(fieldDocument.Id, newFieldTreeRoot); + var updatedSolution = currentSolution.WithDocumentSyntaxRoot(fieldDocument.Id, newFieldTreeRoot); updatedSolution = updatedSolution.WithDocumentSyntaxRoot(propertyDocument.Id, newPropertyTreeRoot); return updatedSolution; } } + private async Task<(IFieldSymbol? fieldSymbol, IPropertySymbol? propertySymbol)> MapDiagnosticToCurrentSolutionAsync( + Diagnostic diagnostic, + Solution originalSolution, + Solution currentSolution, + CancellationToken cancellationToken) + { + var locations = diagnostic.AdditionalLocations; + + var propertyLocation = locations[0]; + var declaratorLocation = locations[1]; + + // Look up everything in the original solution. + + var declarator = (TVariableDeclarator)declaratorLocation.FindNode(cancellationToken); + var fieldDocument = originalSolution.GetRequiredDocument(declarator.SyntaxTree); + var fieldSemanticModel = await fieldDocument.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var fieldSymbol = (IFieldSymbol)fieldSemanticModel.GetRequiredDeclaredSymbol(declarator, cancellationToken); + + var property = GetPropertyDeclaration(propertyLocation.FindNode(cancellationToken)); + var propertyDocument = originalSolution.GetRequiredDocument(property.SyntaxTree); + var propertySemanticModel = await propertyDocument.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var propertySymbol = (IPropertySymbol)propertySemanticModel.GetRequiredDeclaredSymbol(property, cancellationToken); + + Contract.ThrowIfFalse(fieldDocument.Project == propertyDocument.Project); + + // If we're just starting, no need to map anything. + if (originalSolution != currentSolution) + { + var currentProject = currentSolution.GetRequiredProject(fieldDocument.Project.Id); + var currentCompilation = await currentProject.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); + + fieldSymbol = fieldSymbol.GetSymbolKey(cancellationToken).Resolve(currentCompilation, cancellationToken: cancellationToken).GetAnySymbol() as IFieldSymbol; + propertySymbol = propertySymbol.GetSymbolKey(cancellationToken).Resolve(currentCompilation, cancellationToken: cancellationToken).GetAnySymbol() as IPropertySymbol; + } + + return (fieldSymbol, propertySymbol); + } + private static SyntaxRemoveOptions CreateSyntaxRemoveOptions(SyntaxNode nodeToRemove) { var syntaxRemoveOptions = SyntaxGenerator.DefaultRemoveOptions; diff --git a/src/Features/Core/Portable/UseAutoProperty/UseAutoPropertyFixAllProvider.cs b/src/Features/Core/Portable/UseAutoProperty/UseAutoPropertyFixAllProvider.cs new file mode 100644 index 0000000000000..333052f575da3 --- /dev/null +++ b/src/Features/Core/Portable/UseAutoProperty/UseAutoPropertyFixAllProvider.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.UseAutoProperty; + +internal abstract partial class AbstractUseAutoPropertyCodeFixProvider< + TProvider, + TTypeDeclarationSyntax, + TPropertyDeclaration, + TVariableDeclarator, + TConstructorDeclaration, + TExpression> +{ + private sealed class UseAutoPropertyFixAllProvider(TProvider provider) : FixAllProvider + { + public override Task GetFixAsync(FixAllContext fixAllContext) + => DefaultFixAllProviderHelpers.GetFixAsync( + fixAllContext.GetDefaultFixAllTitle(), fixAllContext, FixAllContextsHelperAsync); + + private async Task FixAllContextsHelperAsync(FixAllContext originalContext, ImmutableArray contexts) + { + var cancellationToken = originalContext.CancellationToken; + + // Very slow approach, but the only way we know how to do this correctly and without colliding edits. We + // effectively apply each fix one at a time, moving the solution forward each time. As we process each + // diagnostic, we attempt to re-recover the field/property it was referring to in the original solution to + // the current solution. + var originalSolution = originalContext.Solution; + var currentSolution = originalSolution; + + foreach (var currentContext in contexts) + { + var documentToDiagnostics = await FixAllContextHelper.GetDocumentDiagnosticsToFixAsync(currentContext).ConfigureAwait(false); + foreach (var (_, diagnostics) in documentToDiagnostics) + { + foreach (var diagnostic in diagnostics) + { + currentSolution = await provider.ProcessResultAsync( + originalSolution, currentSolution, diagnostic, cancellationToken).ConfigureAwait(false); + } + } + } + + return currentSolution; + } + } +} diff --git a/src/Features/VisualBasic/Portable/UseAutoProperty/VisualBasicUseAutoPropertyCodeFixProvider.vb b/src/Features/VisualBasic/Portable/UseAutoProperty/VisualBasicUseAutoPropertyCodeFixProvider.vb index 0ba154e235d0f..7bbfd6ed38090 100644 --- a/src/Features/VisualBasic/Portable/UseAutoProperty/VisualBasicUseAutoPropertyCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/UseAutoProperty/VisualBasicUseAutoPropertyCodeFixProvider.vb @@ -16,7 +16,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.UseAutoProperty Friend NotInheritable Class VisualBasicUseAutoPropertyCodeFixProvider - Inherits AbstractUseAutoPropertyCodeFixProvider(Of TypeBlockSyntax, PropertyBlockSyntax, ModifiedIdentifierSyntax, ConstructorBlockSyntax, ExpressionSyntax) + Inherits AbstractUseAutoPropertyCodeFixProvider(Of VisualBasicUseAutoPropertyCodeFixProvider, TypeBlockSyntax, PropertyBlockSyntax, ModifiedIdentifierSyntax, ConstructorBlockSyntax, ExpressionSyntax) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeGeneration/CSharpSyntaxTokens.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeGeneration/CSharpSyntaxTokens.cs index 9b920f587a42d..2106a867bae00 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeGeneration/CSharpSyntaxTokens.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeGeneration/CSharpSyntaxTokens.cs @@ -27,6 +27,7 @@ internal static class CSharpSyntaxTokens public static readonly SyntaxToken DotDotToken = Token(SyntaxKind.DotDotToken); public static readonly SyntaxToken DoubleKeyword = Token(SyntaxKind.DoubleKeyword); public static readonly SyntaxToken EndOfDocumentationCommentToken = Token(SyntaxKind.EndOfDocumentationCommentToken); + public static readonly SyntaxToken EqualsToken = Token(SyntaxKind.EqualsToken); public static readonly SyntaxToken ExplicitKeyword = Token(SyntaxKind.ExplicitKeyword); public static readonly SyntaxToken ExternKeyword = Token(SyntaxKind.ExternKeyword); public static readonly SyntaxToken FileKeyword = Token(SyntaxKind.FileKeyword); From 78e5b6791df0d40a3f29b357e13a332b4c1c6606 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 6 Nov 2024 12:20:32 -0800 Subject: [PATCH 194/508] Fix issue classifying while performing a layout --- ...mbeddedClassificationViewTaggerProvider.cs | 6 ++ .../Core/Copilot/CopilotTaggerProvider.cs | 6 +- .../InlineHintsDataTaggerProvider.cs | 19 +++--- ...ReferenceHighlightingViewTaggerProvider.cs | 6 +- ...ousTaggerProvider.TagSource_ProduceTags.cs | 19 +++++- .../AbstractAsynchronousTaggerProvider.cs | 5 +- ...erProvider.SingleViewportTaggerProvider.cs | 62 ++++++++++--------- .../Core/Tagging/TaggerMainThreadManager.cs | 14 ++--- 8 files changed, 87 insertions(+), 50 deletions(-) diff --git a/src/EditorFeatures/Core/Classification/Semantic/AbstractSemanticOrEmbeddedClassificationViewTaggerProvider.cs b/src/EditorFeatures/Core/Classification/Semantic/AbstractSemanticOrEmbeddedClassificationViewTaggerProvider.cs index b26cd16e6f34a..6e73107d66443 100644 --- a/src/EditorFeatures/Core/Classification/Semantic/AbstractSemanticOrEmbeddedClassificationViewTaggerProvider.cs +++ b/src/EditorFeatures/Core/Classification/Semantic/AbstractSemanticOrEmbeddedClassificationViewTaggerProvider.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Immutable; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor; @@ -251,6 +252,11 @@ await classificationService.AddEmbeddedLanguageClassificationsAsync( throw ExceptionUtilities.UnexpectedValue(_type); } + //if (span.IntersectsWith(407) && !classifiedSpans.Any(cs => cs.TextSpan.Start == 407)) + //{ + // Console.WriteLine(); + //} + foreach (var classifiedSpan in classifiedSpans) context.AddTag(ClassificationUtilities.Convert(_typeMap, snapshot, classifiedSpan)); diff --git a/src/EditorFeatures/Core/Copilot/CopilotTaggerProvider.cs b/src/EditorFeatures/Core/Copilot/CopilotTaggerProvider.cs index 36f255d8fd3fc..41b0f8c5090a8 100644 --- a/src/EditorFeatures/Core/Copilot/CopilotTaggerProvider.cs +++ b/src/EditorFeatures/Core/Copilot/CopilotTaggerProvider.cs @@ -48,7 +48,7 @@ protected override ITaggerEventSource CreateEventSource(ITextView textView, ITex TaggerEventSources.OnTextChanged(subjectBuffer)); } - protected override void AddSpansToTag(ITextView? textView, ITextBuffer subjectBuffer, ref TemporaryArray result) + protected override bool TryAddSpansToTag(ITextView? textView, ITextBuffer subjectBuffer, ref TemporaryArray result) { this.ThreadingContext.ThrowIfNotOnUIThread(); Contract.ThrowIfNull(textView); @@ -56,6 +56,10 @@ protected override void AddSpansToTag(ITextView? textView, ITextBuffer subjectBu // We only care about the cases where we have caret. if (textView.GetCaretPoint(subjectBuffer) is { } caret) result.Add(new SnapshotSpan(caret, 0)); + + // Note: we report 'true' unconditionally here. This is because we want to ensure that we still compute 'no tags' + // in the case that we don't have a caret point, as opposed to bailing out. + return true; } protected override async Task ProduceTagsAsync(TaggerContext context, DocumentSnapshotSpan spanToTag, int? caretPosition, CancellationToken cancellationToken) diff --git a/src/EditorFeatures/Core/InlineHints/InlineHintsDataTaggerProvider.cs b/src/EditorFeatures/Core/InlineHints/InlineHintsDataTaggerProvider.cs index 76f1ab0e43683..697b7f0444f43 100644 --- a/src/EditorFeatures/Core/InlineHints/InlineHintsDataTaggerProvider.cs +++ b/src/EditorFeatures/Core/InlineHints/InlineHintsDataTaggerProvider.cs @@ -74,22 +74,25 @@ protected override ITaggerEventSource CreateEventSource(ITextView textView, ITex option.Equals(InlineHintsOptionsStorage.ForCollectionExpressions))); } - protected override void AddSpansToTag(ITextView? textView, ITextBuffer subjectBuffer, ref TemporaryArray result) + protected override bool TryAddSpansToTag(ITextView? textView, ITextBuffer subjectBuffer, ref TemporaryArray result) { this.ThreadingContext.ThrowIfNotOnUIThread(); Contract.ThrowIfNull(textView); - // Find the visible span some 100 lines +/- what's actually in view. This way - // if the user scrolls up/down, we'll already have the results. + // Find the visible span some 100 lines +/- what's actually in view. This way if the user scrolls up/down, + // we'll already have the results. var visibleSpanOpt = textView.GetVisibleLinesSpan(subjectBuffer, extraLines: 100); + + // If we can't even determine what our visible span is, bail out immediately. We may be in something like a + // layout pass, and we don't want to suddenly flip to tagging everything, then go back to tagging a small + // subset of the view afterwards. + // + // In this case we literally do not know what is visible, so we want to bail and try again later. if (visibleSpanOpt == null) - { - // Couldn't find anything visible, just fall back to tagging all hint locations - base.AddSpansToTag(textView, subjectBuffer, ref result); - return; - } + return false; result.Add(visibleSpanOpt.Value); + return true; } protected override async Task ProduceTagsAsync( diff --git a/src/EditorFeatures/Core/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs b/src/EditorFeatures/Core/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs index 4b57b1206c487..ceb74bae26bfb 100644 --- a/src/EditorFeatures/Core/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs +++ b/src/EditorFeatures/Core/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs @@ -77,12 +77,16 @@ protected override ITaggerEventSource CreateEventSource(ITextView textView, ITex return textViewOpt.BufferGraph.MapDownToFirstMatch(textViewOpt.Selection.Start.Position, PointTrackingMode.Positive, b => IsSupportedContentType(b.ContentType), PositionAffinity.Successor); } - protected override void AddSpansToTag(ITextView textViewOpt, ITextBuffer subjectBuffer, ref TemporaryArray result) + protected override bool TryAddSpansToTag(ITextView textViewOpt, ITextBuffer subjectBuffer, ref TemporaryArray result) { // Note: this may return no snapshot spans. We have to be resilient to that // when processing the TaggerContext<>.SpansToTag below. foreach (var buffer in textViewOpt.BufferGraph.GetTextBuffers(b => IsSupportedContentType(b.ContentType))) result.Add(buffer.CurrentSnapshot.GetFullSpan()); + + // Fine to always return 'true' here. If we get no spans to tag, we can move ourselves to the no-tags state and + // update the editor. + return true; } protected override Task ProduceTagsAsync( diff --git a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs index f97fa6470a41e..baa6b50ad5e03 100644 --- a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs +++ b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs @@ -294,9 +294,19 @@ private async ValueTask ProcessEventChangeAsync( // us avoid hammering the dispatcher queue with lots of work that causes contention. Additionally, use // a no-throw awaitable so that in the common case where we cancel before, we don't throw an exception // that can exacerbate cross process debugging scenarios. - var (isVisible, caretPosition, snapshotSpansToTag) = await _dataSource.MainThreadManager.PerformWorkOnMainThreadAsync( + var valueOpt = await _dataSource.MainThreadManager.PerformWorkOnMainThreadAsync( GetTaggerUIData, cancellationToken).ConfigureAwait(true); + if (valueOpt is null) + { + // We failed to get the UI data we need. This can happen in cases like trying to get that during a layout pass. + // In that case, we just bail out and try again later. + this.EnqueueWork(highPriority); + return null; + } + + var (isVisible, caretPosition, snapshotSpansToTag) = valueOpt.Value; + // Since we don't ever throw above, check and see if the await completed due to cancellation and do not // proceed. if (cancellationToken.IsCancellationRequested) @@ -379,7 +389,9 @@ static async (oldTagTrees, args, cancellationToken) => return newTagTrees; } - (bool isVisible, SnapshotPoint? caretPosition, OneOrMany spansToTag) GetTaggerUIData() + // Returns null if we we're currently in a state where we can't get the tags to span and we should bail out + // from producing tags on this turn of the crank. + (bool isVisible, SnapshotPoint? caretPosition, OneOrMany spansToTag)? GetTaggerUIData() { _dataSource.ThreadingContext.ThrowIfNotOnUIThread(); @@ -393,7 +405,8 @@ static async (oldTagTrees, args, cancellationToken) => var caretPosition = _dataSource.GetCaretPoint(_textView, _subjectBuffer); using var spansToTag = TemporaryArray.Empty; - _dataSource.AddSpansToTag(_textView, _subjectBuffer, ref spansToTag.AsRef()); + if (!_dataSource.TryAddSpansToTag(_textView, _subjectBuffer, ref spansToTag.AsRef())) + return null; #if DEBUG foreach (var snapshotSpan in spansToTag) diff --git a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.cs b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.cs index 43881d20097b2..bba24584f0f90 100644 --- a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.cs +++ b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.cs @@ -215,11 +215,14 @@ private void StoreTagSource(ITextView? textView, ITextBuffer subjectBuffer, TagS /// and will asynchronously call into at some point in /// the future to produce tags for these spans. /// - protected virtual void AddSpansToTag( + /// if spans could not be added and if tagging should abort and wait until its next + /// run. + protected virtual bool TryAddSpansToTag( ITextView? textView, ITextBuffer subjectBuffer, ref TemporaryArray result) { // For a standard tagger, the spans to tag is the span of the entire snapshot. result.Add(subjectBuffer.CurrentSnapshot.GetFullSpan()); + return true; } /// diff --git a/src/EditorFeatures/Core/Tagging/AsynchronousViewportTaggerProvider.SingleViewportTaggerProvider.cs b/src/EditorFeatures/Core/Tagging/AsynchronousViewportTaggerProvider.SingleViewportTaggerProvider.cs index 4f0c94b6abcd2..9c77c5ce888c5 100644 --- a/src/EditorFeatures/Core/Tagging/AsynchronousViewportTaggerProvider.SingleViewportTaggerProvider.cs +++ b/src/EditorFeatures/Core/Tagging/AsynchronousViewportTaggerProvider.SingleViewportTaggerProvider.cs @@ -62,7 +62,7 @@ protected override bool CancelOnNewWork // This can save a lot of CPU time for things that may never even be looked at. => _viewPortToTag != ViewPortToTag.InView; - protected override void AddSpansToTag(ITextView? textView, ITextBuffer subjectBuffer, ref TemporaryArray result) + protected override bool TryAddSpansToTag(ITextView? textView, ITextBuffer subjectBuffer, ref TemporaryArray result) { this.ThreadingContext.ThrowIfNotOnUIThread(); Contract.ThrowIfNull(textView); @@ -70,15 +70,14 @@ protected override void AddSpansToTag(ITextView? textView, ITextBuffer subjectBu // if we're the current view, attempt to just get what's visible, plus 10 lines above and below. This will // ensure that moving up/down a few lines tends to have immediate accurate results. var visibleSpanOpt = textView.GetVisibleLinesSpan(subjectBuffer, extraLines: s_standardLineCountAroundViewportToTag); - if (visibleSpanOpt is null) - { - // couldn't figure out the visible span. So the InView tagger will need to tag everything, and the - // above/below tagger should tag nothing. - if (_viewPortToTag == ViewPortToTag.InView) - base.AddSpansToTag(textView, subjectBuffer, ref result); - return; - } + // If we can't even determine what our visible span is, bail out immediately. We may be in something like a + // layout pass, and we don't want to suddenly flip to tagging everything, then go back to tagging a small + // subset of the view afterwards. + // + // In this case we literally do not know what is visible, so we want to bail and try again later. + if (visibleSpanOpt is null) + return false; var visibleSpan = visibleSpanOpt.Value; @@ -86,29 +85,34 @@ protected override void AddSpansToTag(ITextView? textView, ITextBuffer subjectBu if (_viewPortToTag is ViewPortToTag.InView) { result.Add(visibleSpan); - return; - } - - // For the above/below tagger, broaden the span to to the requested portion above/below what's visible, then - // subtract out the visible range. - var widenedSpanOpt = textView.GetVisibleLinesSpan(subjectBuffer, extraLines: _callback._extraLinesAroundViewportToTag); - Contract.ThrowIfNull(widenedSpanOpt, "Should not ever fail getting the widened span as we were able to get the normal visible span"); - - var widenedSpan = widenedSpanOpt.Value; - Contract.ThrowIfFalse(widenedSpan.Span.Contains(visibleSpan.Span), "The widened span must be at least as large as the visible one."); - - if (_viewPortToTag is ViewPortToTag.Above) - { - var aboveSpan = new SnapshotSpan(visibleSpan.Snapshot, Span.FromBounds(widenedSpan.Span.Start, visibleSpan.Span.Start)); - if (!aboveSpan.IsEmpty) - result.Add(aboveSpan); } - else if (_viewPortToTag is ViewPortToTag.Below) + else { - var belowSpan = new SnapshotSpan(visibleSpan.Snapshot, Span.FromBounds(visibleSpan.Span.End, widenedSpan.Span.End)); - if (!belowSpan.IsEmpty) - result.Add(belowSpan); + // For the above/below tagger, broaden the span to to the requested portion above/below what's visible, then + // subtract out the visible range. + var widenedSpanOpt = textView.GetVisibleLinesSpan(subjectBuffer, extraLines: _callback._extraLinesAroundViewportToTag); + Contract.ThrowIfNull(widenedSpanOpt, "Should not ever fail getting the widened span as we were able to get the normal visible span"); + + var widenedSpan = widenedSpanOpt.Value; + Contract.ThrowIfFalse(widenedSpan.Span.Contains(visibleSpan.Span), "The widened span must be at least as large as the visible one."); + + if (_viewPortToTag is ViewPortToTag.Above) + { + var aboveSpan = new SnapshotSpan(visibleSpan.Snapshot, Span.FromBounds(widenedSpan.Span.Start, visibleSpan.Span.Start)); + if (!aboveSpan.IsEmpty) + result.Add(aboveSpan); + } + else if (_viewPortToTag is ViewPortToTag.Below) + { + var belowSpan = new SnapshotSpan(visibleSpan.Snapshot, Span.FromBounds(visibleSpan.Span.End, widenedSpan.Span.End)); + if (!belowSpan.IsEmpty) + result.Add(belowSpan); + } } + + // Unilaterally return true here, even if we determine we don't have a span to tag. In this case, we've + // computed that there really is nothing visible, in which case we *do* want to move to having no tags. + return true; } protected override async Task ProduceTagsAsync( diff --git a/src/EditorFeatures/Core/Tagging/TaggerMainThreadManager.cs b/src/EditorFeatures/Core/Tagging/TaggerMainThreadManager.cs index e0c8e958b5508..5378232757c2d 100644 --- a/src/EditorFeatures/Core/Tagging/TaggerMainThreadManager.cs +++ b/src/EditorFeatures/Core/Tagging/TaggerMainThreadManager.cs @@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.Editor.Tagging; -using QueueData = (Func action, TaskCompletionSource taskCompletionSource, CancellationToken cancellationToken); +using QueueData = (Func action, TaskCompletionSource taskCompletionSource, CancellationToken cancellationToken); internal sealed class TaggerMainThreadManager { @@ -36,8 +36,8 @@ public TaggerMainThreadManager( /// This will not ever throw. private static void RunActionAndUpdateCompletionSource_NoThrow( - Func action, - TaskCompletionSource taskCompletionSource, + Func action, + TaskCompletionSource taskCompletionSource, CancellationToken cancellationToken) { try @@ -60,7 +60,7 @@ private static void RunActionAndUpdateCompletionSource_NoThrow( } finally { - taskCompletionSource.TrySetResult(default); + taskCompletionSource.TrySetResult(null); Contract.ThrowIfFalse(taskCompletionSource.Task.IsCompleted); } } @@ -69,9 +69,9 @@ private static void RunActionAndUpdateCompletionSource_NoThrow( /// Adds the provided action to a queue that will run on the UI thread in the near future (batched with other /// registered actions). If the cancellation token is triggered before the action runs, it will not be run. /// - public async ValueTask PerformWorkOnMainThreadAsync(Func action, CancellationToken cancellationToken) + public async ValueTask PerformWorkOnMainThreadAsync(Func action, CancellationToken cancellationToken) { - var taskSource = new TaskCompletionSource(); + var taskSource = new TaskCompletionSource(); // If we're already on the main thread, just run the action directly without any delay. This is important // for cases where the tagger is performing a blocking call to get tags synchronously on the UI thread (for @@ -84,7 +84,7 @@ public async ValueTask PerformWorkOnMainThreadAsync(Func ((TaskCompletionSource)taskSourceObj!).TrySetCanceled(), taskSource); + static taskSourceObj => ((TaskCompletionSource)taskSourceObj!).TrySetCanceled(), taskSource); _workQueue.AddWork((action, taskSource, cancellationToken)); From c93176ca29e94983c27bc82a915058146e294679 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 6 Nov 2024 12:25:38 -0800 Subject: [PATCH 195/508] Remove --- ...ractSemanticOrEmbeddedClassificationViewTaggerProvider.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/EditorFeatures/Core/Classification/Semantic/AbstractSemanticOrEmbeddedClassificationViewTaggerProvider.cs b/src/EditorFeatures/Core/Classification/Semantic/AbstractSemanticOrEmbeddedClassificationViewTaggerProvider.cs index 6e73107d66443..745a2197ec964 100644 --- a/src/EditorFeatures/Core/Classification/Semantic/AbstractSemanticOrEmbeddedClassificationViewTaggerProvider.cs +++ b/src/EditorFeatures/Core/Classification/Semantic/AbstractSemanticOrEmbeddedClassificationViewTaggerProvider.cs @@ -252,11 +252,6 @@ await classificationService.AddEmbeddedLanguageClassificationsAsync( throw ExceptionUtilities.UnexpectedValue(_type); } - //if (span.IntersectsWith(407) && !classifiedSpans.Any(cs => cs.TextSpan.Start == 407)) - //{ - // Console.WriteLine(); - //} - foreach (var classifiedSpan in classifiedSpans) context.AddTag(ClassificationUtilities.Convert(_typeMap, snapshot, classifiedSpan)); From ee93c3299aa854a16ab36aec9c1c167f86ea1a1e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 6 Nov 2024 12:38:55 -0800 Subject: [PATCH 196/508] lint --- ...AbstractSemanticOrEmbeddedClassificationViewTaggerProvider.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/EditorFeatures/Core/Classification/Semantic/AbstractSemanticOrEmbeddedClassificationViewTaggerProvider.cs b/src/EditorFeatures/Core/Classification/Semantic/AbstractSemanticOrEmbeddedClassificationViewTaggerProvider.cs index 745a2197ec964..b26cd16e6f34a 100644 --- a/src/EditorFeatures/Core/Classification/Semantic/AbstractSemanticOrEmbeddedClassificationViewTaggerProvider.cs +++ b/src/EditorFeatures/Core/Classification/Semantic/AbstractSemanticOrEmbeddedClassificationViewTaggerProvider.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Immutable; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor; From 04e8cab5032f1b2bf0b4eace2762ab6323e614ad Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 6 Nov 2024 12:49:29 -0800 Subject: [PATCH 197/508] Remove --- .../AbstractUseAutoPropertyCodeFixProvider.cs | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs b/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs index 63b0741f9df5c..77bc02712d424 100644 --- a/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs +++ b/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs @@ -82,20 +82,6 @@ public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) return Task.CompletedTask; } - //private async Task ProcessResultAsync( - // Solution solution, Diagnostic diagnostic, CancellationToken cancellationToken) - //{ - // var editor = new SolutionEditor(solution); - // await ProcessResultAsync(editor, diagnostic, cancellationToken).ConfigureAwait(false); - // return editor.GetChangedSolution(); - //} //private async Task ProcessResultAsync( - // Solution solution, Diagnostic diagnostic, CancellationToken cancellationToken) - //{ - // var editor = new SolutionEditor(solution); - // await ProcessResultAsync(editor, diagnostic, cancellationToken).ConfigureAwait(false); - // return editor.GetChangedSolution(); - //} - private async Task ProcessResultAsync( Solution originalSolution, Solution currentSolution, Diagnostic diagnostic, CancellationToken cancellationToken) { @@ -107,19 +93,6 @@ private async Task ProcessResultAsync( var locations = diagnostic.AdditionalLocations; - //var propertyLocation = locations[0]; - //var declaratorLocation = locations[1]; - - //var declarator = (TVariableDeclarator)declaratorLocation.FindNode(cancellationToken); - //var fieldDocument = originalSolution.GetRequiredDocument(declarator.SyntaxTree); - //var fieldSemanticModel = await fieldDocument.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - //var fieldSymbol = (IFieldSymbol)fieldSemanticModel.GetRequiredDeclaredSymbol(declarator, cancellationToken); - - //var property = GetPropertyDeclaration(propertyLocation.FindNode(cancellationToken)); - //var propertyDocument = originalSolution.GetRequiredDocument(property.SyntaxTree); - //var propertySemanticModel = await propertyDocument.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - //var propertySymbol = (IPropertySymbol)propertySemanticModel.GetRequiredDeclaredSymbol(property, cancellationToken); - var fieldDocument = currentSolution.GetRequiredDocument(field.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken).SyntaxTree); var propertyDocument = currentSolution.GetRequiredDocument(property.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken).SyntaxTree); From 7df3e023de4ea6b052b3a06f7f49a954d123af23 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 6 Nov 2024 12:51:33 -0800 Subject: [PATCH 198/508] Apply suggestions from code review --- src/EditorFeatures/Core/Copilot/CopilotTaggerProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/Core/Copilot/CopilotTaggerProvider.cs b/src/EditorFeatures/Core/Copilot/CopilotTaggerProvider.cs index 41b0f8c5090a8..5c3157ba29539 100644 --- a/src/EditorFeatures/Core/Copilot/CopilotTaggerProvider.cs +++ b/src/EditorFeatures/Core/Copilot/CopilotTaggerProvider.cs @@ -58,7 +58,7 @@ protected override bool TryAddSpansToTag(ITextView? textView, ITextBuffer subjec result.Add(new SnapshotSpan(caret, 0)); // Note: we report 'true' unconditionally here. This is because we want to ensure that we still compute 'no tags' - // in the case that we don't have a caret point, as opposed to bailing out. + // in the case that we don't have a caret point, as opposed to bailing out and keeping the current set of tags. return true; } From 103fe0c128bbf0dc8d95d4672497576f86ec812e Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 6 Nov 2024 17:00:59 -0500 Subject: [PATCH 199/508] Update breaking change docs (#75789) * Update breaking change docs Offline, we had a discussion that the title and H1 were verbose and could be misunderstood. I've updated the most recent 4 documents to match that discussion. Notes for reviewers: All edits in each file should be the same (modulo typos and version numbers). Let's agree on the language on one of them, then make sure they all match. * typo * respond to feedback * respond to feedback. --- .../compilers/CSharp/Compiler Breaking Changes - DotNet 10.md | 4 +++- docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md | 4 +++- docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md | 4 +++- docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md | 3 ++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md index 9debb96483dcd..b1f6e72765761 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md @@ -1,4 +1,6 @@ -# This document lists known breaking changes in Roslyn after .NET 9 all the way to .NET 10. +# Breaking changes in Roslyn after .NET 9.0.100 through .NET 10.0.100 + +This document lists known breaking changes in Roslyn after .NET 9 general release (.NET SDK version 9.0.100) through .NET 10 general release (.NET SDK version 10.0.100). ## `Span` and `ReadOnlySpan` overloads are applicable in more scenarios in C# 14 and newer diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md index 16ff18c9ad43f..6c6b64d41eb5d 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md @@ -1,4 +1,6 @@ -# This document lists known breaking changes in Roslyn after .NET 6 all the way to .NET 7. +# Breaking changes in Roslyn after .NET 6.0.100 through .NET 7.0.100 + +This document lists known breaking changes in Roslyn after .NET 6 general release (.NET SDK version 6.0.100) through .NET 7 general release (.NET SDK version 7.0.100). ## All locals of restricted types are disallowed in async methods diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md index 351e636ac80db..470853cd3c8f8 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md @@ -1,4 +1,6 @@ -# This document lists known breaking changes in Roslyn after .NET 7 all the way to .NET 8. +# Breaking changes in Roslyn after .NET 7.0.100 through .NET 8.0.100 + +This document lists known breaking changes in Roslyn after .NET 7 general release (.NET SDK version 7.0.100) through .NET 8 general release (.NET SDK version 8.0.100). ## Ref modifiers of dynamic arguments should be compatible with ref modifiers of corresponding parameters diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md index b1890cfa20293..ce0ac3ee11fe5 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md @@ -1,5 +1,6 @@ -# This document lists known breaking changes in Roslyn after .NET 8 all the way to .NET 9. +# Breaking changes in Roslyn after .NET 8.0.100 through .NET 9.0.100 +This document lists known breaking changes in Roslyn after .NET 8 general release (.NET SDK version 8.0.100) through .NET 9 general release (.NET SDK version 9.0.100). ## InlineArray attribute on a record struct type is no longer allowed. From 253657f159d6ab463d4e2727d92291fe481f5934 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Thu, 7 Nov 2024 09:18:01 +1100 Subject: [PATCH 200/508] Bump Razor to 9.0.0-preview.24555.12 --- eng/Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/Directory.Packages.props b/eng/Directory.Packages.props index 67159fa6d5ecc..f48b1aa2c17ec 100644 --- a/eng/Directory.Packages.props +++ b/eng/Directory.Packages.props @@ -144,7 +144,7 @@ - + - + - + https://github.com/dotnet/source-build-reference-packages - e781442b88c4d939f06a94f38f24a5dc18c383d4 + 1ebd9ce245112164207d961c0d2faea741c7c489 From 9856657757c48ef41fefab6eccf92da43cf02f05 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 11 Nov 2024 08:47:39 -0800 Subject: [PATCH 252/508] Re-add the IEntryPointFinderService. It is used through IVT --- .../CSharpEntryPointFinderService.cs | 21 ++++++++++++++++ .../AbstractEntryPointFinderService.cs | 18 ++++++++++++++ .../ProjectSystem/IEntryPointFinderService.cs | 22 +++++++++++++++++ .../VisualBasicEntryPointFinderService.vb | 24 +++++++++++++++++++ 4 files changed, 85 insertions(+) create mode 100644 src/VisualStudio/CSharp/Impl/ProjectSystemShim/CSharpEntryPointFinderService.cs create mode 100644 src/VisualStudio/Core/Def/ProjectSystem/AbstractEntryPointFinderService.cs create mode 100644 src/VisualStudio/Core/Def/ProjectSystem/IEntryPointFinderService.cs create mode 100644 src/VisualStudio/VisualBasic/Impl/ProjectSystemShim/VisualBasicEntryPointFinderService.vb diff --git a/src/VisualStudio/CSharp/Impl/ProjectSystemShim/CSharpEntryPointFinderService.cs b/src/VisualStudio/CSharp/Impl/ProjectSystemShim/CSharpEntryPointFinderService.cs new file mode 100644 index 0000000000000..9c20550382e30 --- /dev/null +++ b/src/VisualStudio/CSharp/Impl/ProjectSystemShim/CSharpEntryPointFinderService.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Composition; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; + +namespace Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim; + +[ExportLanguageService(typeof(IEntryPointFinderService), LanguageNames.CSharp), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CSharpEntryPointFinderService() : AbstractEntryPointFinderService +{ + protected override IEnumerable FindEntryPoints(Compilation compilation, bool findFormsOnly) + => CSharpEntryPointFinder.FindEntryPoints(compilation); +} diff --git a/src/VisualStudio/Core/Def/ProjectSystem/AbstractEntryPointFinderService.cs b/src/VisualStudio/Core/Def/ProjectSystem/AbstractEntryPointFinderService.cs new file mode 100644 index 0000000000000..cf28888a703c5 --- /dev/null +++ b/src/VisualStudio/Core/Def/ProjectSystem/AbstractEntryPointFinderService.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Microsoft.CodeAnalysis; + +namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; + +internal abstract class AbstractEntryPointFinderService : IEntryPointFinderService +{ + protected abstract IEnumerable FindEntryPoints(Compilation compilation, bool findFormsOnly); + + public IEnumerable FindEntryPoints(INamespaceSymbol symbol, bool findFormsOnly) + => symbol is not { ContainingAssembly: ISourceAssemblySymbol sourceAssembly } + ? [] + : FindEntryPoints(sourceAssembly.Compilation, findFormsOnly); +} diff --git a/src/VisualStudio/Core/Def/ProjectSystem/IEntryPointFinderService.cs b/src/VisualStudio/Core/Def/ProjectSystem/IEntryPointFinderService.cs new file mode 100644 index 0000000000000..363e908327e8d --- /dev/null +++ b/src/VisualStudio/Core/Def/ProjectSystem/IEntryPointFinderService.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Host; + +namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; + +/// +/// Note: This type is exposed through IVT to WebTools. +/// +internal interface IEntryPointFinderService : ILanguageService +{ + /// + /// Finds the types that contain entry points like the Main method in a give namespace. + /// + /// The namespace to search. + /// Restrict the search to only Windows Forms classes. Note that this is only implemented for VisualBasic + IEnumerable FindEntryPoints(INamespaceSymbol symbol, bool findFormsOnly); +} diff --git a/src/VisualStudio/VisualBasic/Impl/ProjectSystemShim/VisualBasicEntryPointFinderService.vb b/src/VisualStudio/VisualBasic/Impl/ProjectSystemShim/VisualBasicEntryPointFinderService.vb new file mode 100644 index 0000000000000..b2ddf22cc26a3 --- /dev/null +++ b/src/VisualStudio/VisualBasic/Impl/ProjectSystemShim/VisualBasicEntryPointFinderService.vb @@ -0,0 +1,24 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports System.Composition +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Host.Mef +Imports Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem + +Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.ProjectSystemShim + + Friend NotInheritable Class VisualBasicEntryPointFinderService + Inherits AbstractEntryPointFinderService + + + + Public Sub New() + End Sub + + Protected Overrides Function FindEntryPoints(compilation As Compilation, findFormsOnly As Boolean) As IEnumerable(Of INamedTypeSymbol) + Return VisualBasicEntryPointFinder.FindEntryPoints(compilation, findFormsOnly) + End Function + End Class +End Namespace From 597573778c9fc3a11fcf1d165665328ac59d72bb Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 11 Nov 2024 09:14:33 -0800 Subject: [PATCH 253/508] Update src/VisualStudio/Core/Def/ProjectSystem/IEntryPointFinderService.cs --- .../Core/Def/ProjectSystem/IEntryPointFinderService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VisualStudio/Core/Def/ProjectSystem/IEntryPointFinderService.cs b/src/VisualStudio/Core/Def/ProjectSystem/IEntryPointFinderService.cs index 363e908327e8d..56b0abe12a737 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/IEntryPointFinderService.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/IEntryPointFinderService.cs @@ -9,7 +9,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; /// -/// Note: This type is exposed through IVT to WebTools. +/// Note: This type is exposed through IVT to dotnet-project-system. /// internal interface IEntryPointFinderService : ILanguageService { From 3d0442bc9fa682fe086df46d47707c17ec3de67e Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Mon, 11 Nov 2024 12:27:24 -0800 Subject: [PATCH 254/508] Modify segmented list to grow by growth rate. (#75756) By using a growth rate, we can control how often the outer array grows (each growth involves not only reallocating the outer array, but as SegmentedArray doesn't allow nulls, it requires alllocating each inner array). So, by controlling this growth rate, we balance the allocation costs of the outer and inner arrays. Benchmarking (not checked in) demonstrated a growth factor of 12.5% as a good rate for arrays containing up to 64K segments. --- .../SegmentedList.Generic.Tests.Capacity.cs | 6 ++- .../Collections/SegmentedList`1.cs | 43 ++++++++++++++++--- .../SegmentedListBenchmarks_Add.cs | 25 ++++------- 3 files changed, 51 insertions(+), 23 deletions(-) diff --git a/src/Compilers/Core/CodeAnalysisTest/Collections/List/SegmentedList.Generic.Tests.Capacity.cs b/src/Compilers/Core/CodeAnalysisTest/Collections/List/SegmentedList.Generic.Tests.Capacity.cs index 1eb3fad3c8089..ec2347a570b8e 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Collections/List/SegmentedList.Generic.Tests.Capacity.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Collections/List/SegmentedList.Generic.Tests.Capacity.cs @@ -134,7 +134,11 @@ public void EnsureCapacity_MatchesSizeWithLargeCapacityRequest(int segmentCount) var requestedCapacity = 2 * elementCount + 10; list.EnsureCapacity(requestedCapacity); - Assert.Equal(requestedCapacity, list.Capacity); + + var lastSegmentLength = requestedCapacity % SegmentedArray.TestAccessor.SegmentSize; + var expectedCapacity = (requestedCapacity - lastSegmentLength) + SegmentedArray.TestAccessor.SegmentSize; + + Assert.Equal(expectedCapacity, list.Capacity); } } } diff --git a/src/Dependencies/Collections/SegmentedList`1.cs b/src/Dependencies/Collections/SegmentedList`1.cs index 5a7ffb9202924..f00c76a6897df 100644 --- a/src/Dependencies/Collections/SegmentedList`1.cs +++ b/src/Dependencies/Collections/SegmentedList`1.cs @@ -512,18 +512,49 @@ internal void Grow(int capacity) { Debug.Assert(_items.Length < capacity); - var newCapacity = _items.Length == 0 ? DefaultCapacity : 2 * _items.Length; + var newCapacity = 0; - // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow. - // Note that this check works even when _items.Length overflowed thanks to the (uint) cast - if ((uint)newCapacity > MaxLength) - newCapacity = MaxLength; + if (_items.Length < SegmentedArrayHelper.GetSegmentSize() / 2) + { + // The array isn't near the maximum segment size. If the array is empty, the new capacity + // should be DefaultCapacity. Otherwise, the new capacity should be double the current array size. + newCapacity = _items.Length == 0 ? DefaultCapacity : _items.Length * 2; + } + else + { + // If the last segment is fully sized, increase the number of segments by the desired growth rate + if (0 == (_items.Length & SegmentedArrayHelper.GetOffsetMask())) + { + // This value determines the growth rate of the number of segments to use. + // For a value of 3, this means the segment count will grow at a rate of + // 1 + (1 >> 3) or 12.5% + const int segmentGrowthShiftValue = 3; + + var oldSegmentCount = (_items.Length + SegmentedArrayHelper.GetSegmentSize() - 1) >> SegmentedArrayHelper.GetSegmentShift(); + var newSegmentCount = oldSegmentCount + Math.Max(1, oldSegmentCount >> segmentGrowthShiftValue); - // If the computed capacity is still less than specified, set to the original argument. + newCapacity = SegmentedArrayHelper.GetSegmentSize() * newSegmentCount; + } + } + + // If the computed capacity is less than specified, set to the original argument. // Capacities exceeding Array.MaxLength will be surfaced as OutOfMemoryException by Array.Resize. if (newCapacity < capacity) newCapacity = capacity; + if (newCapacity > SegmentedArrayHelper.GetSegmentSize()) + { + // If the last segment isn't fully sized, increase the new capacity such that it will be. + var lastSegmentLength = newCapacity & SegmentedArrayHelper.GetOffsetMask(); + if (lastSegmentLength > 0) + newCapacity = (newCapacity - lastSegmentLength) + SegmentedArrayHelper.GetSegmentSize(); + + // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow. + // Note that this check works even when _items.Length overflowed thanks to the (uint) cast + if ((uint)newCapacity > MaxLength) + newCapacity = MaxLength; + } + Capacity = newCapacity; } diff --git a/src/Tools/IdeCoreBenchmarks/SegmentedListBenchmarks_Add.cs b/src/Tools/IdeCoreBenchmarks/SegmentedListBenchmarks_Add.cs index ff9629c2302ac..5c5d77bbfd221 100644 --- a/src/Tools/IdeCoreBenchmarks/SegmentedListBenchmarks_Add.cs +++ b/src/Tools/IdeCoreBenchmarks/SegmentedListBenchmarks_Add.cs @@ -38,28 +38,21 @@ private void AddToList(T item) array.Add(item); } - private struct LargeStruct + private struct MediumStruct { public int i1 { get; set; } public int i2 { get; set; } public int i3 { get; set; } public int i4 { get; set; } public int i5 { get; set; } - public int i6 { get; set; } - public int i7 { get; set; } - public int i8 { get; set; } - public int i9 { get; set; } - public int i10 { get; set; } - public int i11 { get; set; } - public int i12 { get; set; } - public int i13 { get; set; } - public int i14 { get; set; } - public int i15 { get; set; } - public int i16 { get; set; } - public int i17 { get; set; } - public int i18 { get; set; } - public int i19 { get; set; } - public int i20 { get; set; } + } + + private struct LargeStruct + { + public MediumStruct s1 { get; set; } + public MediumStruct s2 { get; set; } + public MediumStruct s3 { get; set; } + public MediumStruct s4 { get; set; } } private struct EnormousStruct From 569b62b6df8121c64f82337103f0b9f5c3d3e8d6 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 11 Nov 2024 12:48:33 -0800 Subject: [PATCH 255/508] Fix issue where doc comment completion was coming up inside a string literal --- ...tractDocumentationCommentCommandHandler.cs | 21 +++++---- .../Core/Remote/SolutionChecksumUpdater.cs | 4 +- ...tractDocumentationCommentSnippetService.cs | 44 +++++++++++++------ .../IDocumentationCommentSnippetService.cs | 11 ++--- ...harpDocumentationCommentsSnippetService.cs | 5 ++- .../OnAutoInsert/OnAutoInsertHandler.cs | 10 ++--- 6 files changed, 53 insertions(+), 42 deletions(-) diff --git a/src/EditorFeatures/Core/DocumentationComments/AbstractDocumentationCommentCommandHandler.cs b/src/EditorFeatures/Core/DocumentationComments/AbstractDocumentationCommentCommandHandler.cs index f3084ed46cdb9..04db5a505e483 100644 --- a/src/EditorFeatures/Core/DocumentationComments/AbstractDocumentationCommentCommandHandler.cs +++ b/src/EditorFeatures/Core/DocumentationComments/AbstractDocumentationCommentCommandHandler.cs @@ -57,14 +57,14 @@ private char TriggerCharacter public string DisplayName => EditorFeaturesResources.Documentation_Comment; - private static DocumentationCommentSnippet? InsertOnCharacterTyped(IDocumentationCommentSnippetService service, SyntaxTree syntaxTree, SourceText text, int position, DocumentationCommentOptions options, CancellationToken cancellationToken) - => service.GetDocumentationCommentSnippetOnCharacterTyped(syntaxTree, text, position, options, cancellationToken); + private static DocumentationCommentSnippet? InsertOnCharacterTyped(IDocumentationCommentSnippetService service, ParsedDocument document, int position, DocumentationCommentOptions options, CancellationToken cancellationToken) + => service.GetDocumentationCommentSnippetOnCharacterTyped(document, position, options, cancellationToken); - private static DocumentationCommentSnippet? InsertOnEnterTyped(IDocumentationCommentSnippetService service, SyntaxTree syntaxTree, SourceText text, int position, DocumentationCommentOptions options, CancellationToken cancellationToken) - => service.GetDocumentationCommentSnippetOnEnterTyped(syntaxTree, text, position, options, cancellationToken); + private static DocumentationCommentSnippet? InsertOnEnterTyped(IDocumentationCommentSnippetService service, ParsedDocument document, int position, DocumentationCommentOptions options, CancellationToken cancellationToken) + => service.GetDocumentationCommentSnippetOnEnterTyped(document, position, options, cancellationToken); - private static DocumentationCommentSnippet? InsertOnCommandInvoke(IDocumentationCommentSnippetService service, SyntaxTree syntaxTree, SourceText text, int position, DocumentationCommentOptions options, CancellationToken cancellationToken) - => service.GetDocumentationCommentSnippetOnCommandInvoke(syntaxTree, text, position, options, cancellationToken); + private static DocumentationCommentSnippet? InsertOnCommandInvoke(IDocumentationCommentSnippetService service, ParsedDocument document, int position, DocumentationCommentOptions options, CancellationToken cancellationToken) + => service.GetDocumentationCommentSnippetOnCommandInvoke(document, position, options, cancellationToken); private static void ApplySnippet(DocumentationCommentSnippet snippet, ITextBuffer subjectBuffer, ITextView textView) { @@ -76,7 +76,7 @@ private static void ApplySnippet(DocumentationCommentSnippet snippet, ITextBuffe private bool CompleteComment( ITextBuffer subjectBuffer, ITextView textView, - Func getSnippetAction, + Func getSnippetAction, CancellationToken cancellationToken) { var caretPosition = textView.GetCaretPoint(subjectBuffer) ?? -1; @@ -100,7 +100,7 @@ private bool CompleteComment( var returnValue = false; foreach (var snapshot in snapshots) { - var snippet = getSnippetAction(service, parsedDocument.SyntaxTree, parsedDocument.Text, snapshot.Span.Start, options, cancellationToken); + var snippet = getSnippetAction(service, parsedDocument, snapshot.Span.Start, options, cancellationToken); if (snippet != null) { ApplySnippet(snippet, subjectBuffer, textView); @@ -209,9 +209,8 @@ public CommandState GetCommandState(InsertCommentCommandArgs args) var isValidTargetMember = false; _uiThreadOperationExecutor.Execute("IntelliSense", defaultDescription: "", allowCancellation: true, showProgress: false, action: c => { - var syntaxTree = document.GetRequiredSyntaxTreeSynchronously(c.UserCancellationToken); - var text = syntaxTree.GetText(c.UserCancellationToken); - isValidTargetMember = service.IsValidTargetMember(syntaxTree, text, caretPosition, c.UserCancellationToken); + var parsedDocument = ParsedDocument.CreateSynchronously(document, c.UserCancellationToken); + isValidTargetMember = service.IsValidTargetMember(parsedDocument, caretPosition, c.UserCancellationToken); }); return isValidTargetMember diff --git a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs index 9c288cc54b153..39ae59106d4e9 100644 --- a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs +++ b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs @@ -121,8 +121,9 @@ private void PauseWork() // progress to synchronize the solution. lock (_gate) { + // We only pause expensive work (like updating the full solution checksum). Cheaper work (like sending over + // text edits and active doc changes can proceed). _synchronizeWorkspaceQueue.CancelExistingWork(); - _synchronizeActiveDocumentQueue.CancelExistingWork(); _isPaused = true; } } @@ -132,7 +133,6 @@ private void ResumeWork() lock (_gate) { _isPaused = false; - _synchronizeActiveDocumentQueue.AddWork(); _synchronizeWorkspaceQueue.AddWork(); } } diff --git a/src/Features/Core/Portable/DocumentationComments/AbstractDocumentationCommentSnippetService.cs b/src/Features/Core/Portable/DocumentationComments/AbstractDocumentationCommentSnippetService.cs index c02d964a5992d..a4653c79e9698 100644 --- a/src/Features/Core/Portable/DocumentationComments/AbstractDocumentationCommentSnippetService.cs +++ b/src/Features/Core/Portable/DocumentationComments/AbstractDocumentationCommentSnippetService.cs @@ -8,6 +8,7 @@ using System.Diagnostics.CodeAnalysis; using System.Threading; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -39,8 +40,7 @@ internal abstract class AbstractDocumentationCommentSnippetService GetTargetMember(syntaxTree, text, position, cancellationToken) != null; + public bool IsValidTargetMember(ParsedDocument document, int position, CancellationToken cancellationToken) + => GetTargetMember(document, position, cancellationToken) != null; - private TMemberNode? GetTargetMember(SyntaxTree syntaxTree, SourceText text, int position, CancellationToken cancellationToken) + private TMemberNode? GetTargetMember(ParsedDocument document, int position, CancellationToken cancellationToken) { + var syntaxTree = document.SyntaxTree; + var text = document.Text; + var member = GetContainingMember(syntaxTree, position, cancellationToken); if (member == null) { @@ -226,7 +232,7 @@ private static void IndentLines(List lines, string? indentText) } } - public DocumentationCommentSnippet? GetDocumentationCommentSnippetOnEnterTyped(SyntaxTree syntaxTree, SourceText text, int position, in DocumentationCommentOptions options, CancellationToken cancellationToken) + public DocumentationCommentSnippet? GetDocumentationCommentSnippetOnEnterTyped(ParsedDocument document, int position, in DocumentationCommentOptions options, CancellationToken cancellationToken) { // Don't attempt to generate a new XML doc comment on ENTER if the option to auto-generate // them isn't set. Regardless of the option, we should generate exterior trivia (i.e. /// or ''') @@ -234,19 +240,20 @@ private static void IndentLines(List lines, string? indentText) if (options.AutoXmlDocCommentGeneration) { - var result = GenerateDocumentationCommentAfterEnter(syntaxTree, text, position, options, cancellationToken); + var result = GenerateDocumentationCommentAfterEnter(document, position, options, cancellationToken); if (result != null) - { return result; - } } - return GenerateExteriorTriviaAfterEnter(syntaxTree, text, position, options, cancellationToken); + return GenerateExteriorTriviaAfterEnter(document, position, options, cancellationToken); } - private DocumentationCommentSnippet? GenerateDocumentationCommentAfterEnter(SyntaxTree syntaxTree, SourceText text, int position, in DocumentationCommentOptions options, CancellationToken cancellationToken) + private DocumentationCommentSnippet? GenerateDocumentationCommentAfterEnter(ParsedDocument document, int position, in DocumentationCommentOptions options, CancellationToken cancellationToken) { // Find the documentation comment before the new line that was just pressed + var syntaxTree = document.SyntaxTree; + var text = document.Text; + var token = GetTokenToLeft(syntaxTree, position, cancellationToken); if (!IsDocCommentNewLine(token)) { @@ -286,9 +293,11 @@ private static void IndentLines(List lines, string? indentText) return new DocumentationCommentSnippet(replaceSpan, newText, offset); } - public DocumentationCommentSnippet? GetDocumentationCommentSnippetOnCommandInvoke(SyntaxTree syntaxTree, SourceText text, int position, in DocumentationCommentOptions options, CancellationToken cancellationToken) + public DocumentationCommentSnippet? GetDocumentationCommentSnippetOnCommandInvoke(ParsedDocument document, int position, in DocumentationCommentOptions options, CancellationToken cancellationToken) { - var targetMember = GetTargetMember(syntaxTree, text, position, cancellationToken); + var text = document.Text; + + var targetMember = GetTargetMember(document, position, cancellationToken); if (targetMember == null) { return null; @@ -323,9 +332,16 @@ private static void IndentLines(List lines, string? indentText) return new DocumentationCommentSnippet(replaceSpan, comments, offset); } - private DocumentationCommentSnippet? GenerateExteriorTriviaAfterEnter(SyntaxTree syntaxTree, SourceText text, int position, in DocumentationCommentOptions options, CancellationToken cancellationToken) + private DocumentationCommentSnippet? GenerateExteriorTriviaAfterEnter(ParsedDocument document, int position, in DocumentationCommentOptions options, CancellationToken cancellationToken) { + var syntaxTree = document.SyntaxTree; + var text = document.Text; + // Find the documentation comment before the new line that was just pressed + var syntaxFacts = document.LanguageServices.GetRequiredService(); + if (syntaxFacts.IsEntirelyWithinStringOrCharOrNumericLiteral(syntaxTree, position, cancellationToken)) + return null; + var token = GetTokenToLeft(syntaxTree, position, cancellationToken); if (!IsDocCommentNewLine(token) && HasSkippedTrailingTrivia(token)) { diff --git a/src/Features/Core/Portable/DocumentationComments/IDocumentationCommentSnippetService.cs b/src/Features/Core/Portable/DocumentationComments/IDocumentationCommentSnippetService.cs index a5adede4653ff..5eb3d9607cb70 100644 --- a/src/Features/Core/Portable/DocumentationComments/IDocumentationCommentSnippetService.cs +++ b/src/Features/Core/Portable/DocumentationComments/IDocumentationCommentSnippetService.cs @@ -16,23 +16,20 @@ internal interface IDocumentationCommentSnippetService : ILanguageService string DocumentationCommentCharacter { get; } DocumentationCommentSnippet? GetDocumentationCommentSnippetOnCharacterTyped( - SyntaxTree syntaxTree, - SourceText text, + ParsedDocument document, int position, in DocumentationCommentOptions options, CancellationToken cancellationToken, bool addIndentation = true); DocumentationCommentSnippet? GetDocumentationCommentSnippetOnCommandInvoke( - SyntaxTree syntaxTree, - SourceText text, + ParsedDocument document, int position, in DocumentationCommentOptions options, CancellationToken cancellationToken); DocumentationCommentSnippet? GetDocumentationCommentSnippetOnEnterTyped( - SyntaxTree syntaxTree, - SourceText text, + ParsedDocument document, int position, in DocumentationCommentOptions options, CancellationToken cancellationToken); @@ -42,5 +39,5 @@ internal interface IDocumentationCommentSnippetService : ILanguageService TextLine currentLine, TextLine previousLine); - bool IsValidTargetMember(SyntaxTree syntaxTree, SourceText text, int caretPosition, CancellationToken cancellationToken); + bool IsValidTargetMember(ParsedDocument document, int caretPosition, CancellationToken cancellationToken); } diff --git a/src/Features/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentsSnippetService.cs b/src/Features/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentsSnippetService.cs index 33c5570262629..7dacd039767e7 100644 --- a/src/Features/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentsSnippetService.cs +++ b/src/Features/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentsSnippetService.cs @@ -12,6 +12,7 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.DocumentationComments { internal static class OmniSharpDocumentationCommentsSnippetService { +#pragma warning disable IDE0060 // Remove unused parameter public static OmniSharpDocumentationCommentSnippet? GetDocumentationCommentSnippetOnCharacterTyped( Document document, SyntaxTree syntaxTree, @@ -21,7 +22,7 @@ internal static class OmniSharpDocumentationCommentsSnippetService CancellationToken cancellationToken) { var service = document.GetRequiredLanguageService(); - return Translate(service.GetDocumentationCommentSnippetOnCharacterTyped(syntaxTree, text, position, options.UnderlyingObject, cancellationToken)); + return Translate(service.GetDocumentationCommentSnippetOnCharacterTyped(ParsedDocument.CreateSynchronously(document, cancellationToken), position, options.UnderlyingObject, cancellationToken)); } public static OmniSharpDocumentationCommentSnippet? GetDocumentationCommentSnippetOnEnterTyped( @@ -33,7 +34,7 @@ internal static class OmniSharpDocumentationCommentsSnippetService CancellationToken cancellationToken) { var service = document.GetRequiredLanguageService(); - return Translate(service.GetDocumentationCommentSnippetOnEnterTyped(syntaxTree, text, position, options.UnderlyingObject, cancellationToken)); + return Translate(service.GetDocumentationCommentSnippetOnEnterTyped(ParsedDocument.CreateSynchronously(document, cancellationToken), position, options.UnderlyingObject, cancellationToken)); } private static OmniSharpDocumentationCommentSnippet? Translate(DocumentationCommentSnippet? result) diff --git a/src/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs b/src/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs index 0e8cfb9fe6f41..cc6c5cd737189 100644 --- a/src/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs +++ b/src/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs @@ -114,19 +114,17 @@ internal sealed class OnAutoInsertHandler( DocumentationCommentOptions options, CancellationToken cancellationToken) { - var syntaxTree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); - var sourceText = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); + var parsedDocument = await ParsedDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false); + var sourceText = parsedDocument.Text; var position = sourceText.Lines.GetPosition(linePosition); var result = character == "\n" - ? service.GetDocumentationCommentSnippetOnEnterTyped(syntaxTree, sourceText, position, options, cancellationToken) - : service.GetDocumentationCommentSnippetOnCharacterTyped(syntaxTree, sourceText, position, options, cancellationToken, addIndentation: false); + ? service.GetDocumentationCommentSnippetOnEnterTyped(parsedDocument, position, options, cancellationToken) + : service.GetDocumentationCommentSnippetOnCharacterTyped(parsedDocument, position, options, cancellationToken, addIndentation: false); if (result == null) - { return null; - } return new LSP.VSInternalDocumentOnAutoInsertResponseItem { From a4fb15c17612447eb57fdf12d847ab285dfb1629 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 11 Nov 2024 12:52:54 -0800 Subject: [PATCH 256/508] Update tests --- .../DocumentationCommentTests.cs | 3072 ++++++++++------- .../AbstractDocumentationCommentTests.cs | 2 +- 2 files changed, 1751 insertions(+), 1323 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/DocumentationComments/DocumentationCommentTests.cs b/src/EditorFeatures/CSharpTest/DocumentationComments/DocumentationCommentTests.cs index ec08b003714f0..00c36eca01d29 100644 --- a/src/EditorFeatures/CSharpTest/DocumentationComments/DocumentationCommentTests.cs +++ b/src/EditorFeatures/CSharpTest/DocumentationComments/DocumentationCommentTests.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using Microsoft.CodeAnalysis.DocumentationComments; using Microsoft.CodeAnalysis.Editor.CSharp.DocumentationComments; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; @@ -17,24 +15,28 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.DocumentationComments; [Trait(Traits.Feature, Traits.Features.DocumentationComments)] -public class DocumentationCommentTests : AbstractDocumentationCommentTests +public sealed class DocumentationCommentTests : AbstractDocumentationCommentTests { [WpfFact] public void TypingCharacter_Class() { var code = -@"//$$ -class C -{ -}"; + """ + //$$ + class C + { + } + """; var expected = -@"/// -/// $$ -/// -class C -{ -}"; + """ + /// + /// $$ + /// + class C + { + } + """; VerifyTypingCharacter(code, expected); } @@ -43,14 +45,18 @@ class C public void TypingCharacter_Record() { var code = -@"//$$ -record R;"; + """ + //$$ + record R; + """; var expected = -@"/// -/// $$ -/// -record R;"; + """ + /// + /// $$ + /// + record R; + """; VerifyTypingCharacter(code, expected); } @@ -59,14 +65,18 @@ record R;"; public void TypingCharacter_RecordStruct() { var code = -@"//$$ -record struct R;"; + """ + //$$ + record struct R; + """; var expected = -@"/// -/// $$ -/// -record struct R;"; + """ + /// + /// $$ + /// + record struct R; + """; VerifyTypingCharacter(code, expected); } @@ -75,16 +85,20 @@ record struct R;"; public void TypingCharacter_RecordWithPositionalParameters() { var code = -@"//$$ -record R(string S, int I);"; + """ + //$$ + record R(string S, int I); + """; var expected = -@"/// -/// $$ -/// -/// -/// -record R(string S, int I);"; + """ + /// + /// $$ + /// + /// + /// + record R(string S, int I); + """; VerifyTypingCharacter(code, expected); } @@ -93,16 +107,20 @@ record R(string S, int I);"; public void TypingCharacter_ClassParameters() { var code = -@"//$$ -class R(string S, int I);"; + """ + //$$ + class R(string S, int I); + """; var expected = -@"/// -/// $$ -/// -/// -/// -class R(string S, int I);"; + """ + /// + /// $$ + /// + /// + /// + class R(string S, int I); + """; VerifyTypingCharacter(code, expected); } @@ -111,16 +129,20 @@ class R(string S, int I);"; public void TypingCharacter_RecordStructWithPositionalParameters() { var code = -@"//$$ -record struct R(string S, int I);"; + """ + //$$ + record struct R(string S, int I); + """; var expected = -@"/// -/// $$ -/// -/// -/// -record struct R(string S, int I);"; + """ + /// + /// $$ + /// + /// + /// + record struct R(string S, int I); + """; VerifyTypingCharacter(code, expected); } @@ -129,16 +151,20 @@ record struct R(string S, int I);"; public void TypingCharacter_StructParameters() { var code = -@"//$$ -struct R(string S, int I);"; + """ + //$$ + struct R(string S, int I); + """; var expected = -@"/// -/// $$ -/// -/// -/// -struct R(string S, int I);"; + """ + /// + /// $$ + /// + /// + /// + struct R(string S, int I); + """; VerifyTypingCharacter(code, expected); } @@ -146,33 +172,67 @@ struct R(string S, int I);"; [WpfFact] public void TypingCharacter_Class_NewLine() { - var code = "//$$\r\nclass C\r\n{\r\n}"; + var code = """ + //$$ + class C + { + } + """; + + var expected = """ + /// + /// $$ + /// + class C + { + } + """; + + VerifyTypingCharacter(code, expected, newLine: """ + - var expected = "/// \n/// $$\n/// \r\nclass C\r\n{\r\n}"; + """); - VerifyTypingCharacter(code, expected, newLine: "\n"); + code = """ + //$$ + class C + { + } + """; - code = "//$$\r\nclass C\r\n{\r\n}"; + expected = """ + /// + /// $$ + /// + class C + { + } + """; - expected = "/// \r\n/// $$\r\n/// \r\nclass C\r\n{\r\n}"; + VerifyTypingCharacter(code, expected, newLine: """ - VerifyTypingCharacter(code, expected, newLine: "\r\n"); + + """); } [WpfFact] public void TypingCharacter_Class_AutoGenerateXmlDocCommentsOff() { var code = -@"//$$ -class C -{ -}"; + """ + //$$ + class C + { + } + """; var expected = -@"///$$ -class C -{ -}"; + """ + ///$$ + class C + { + } + """; VerifyTypingCharacter(code, expected, globalOptions: new OptionsCollection(LanguageNames.CSharp) { @@ -184,23 +244,27 @@ class C public void TypingCharacter_Method() { var code = -@"class C -{ - //$$ - int M(int goo) { return 0; } -}"; - - var expected = -@"class C -{ - /// - /// $$ - /// - /// - /// - /// - int M(int goo) { return 0; } -}"; + """ + class C + { + //$$ + int M(int goo) { return 0; } + } + """; + + var expected = + """ + class C + { + /// + /// $$ + /// + /// + /// + /// + int M(int goo) { return 0; } + } + """; VerifyTypingCharacter(code, expected); } @@ -209,32 +273,36 @@ public void TypingCharacter_Method() public void TypingCharacter_Method_WithExceptions() { var code = -@"class C -{ - //$$ - int M(int goo) - { - if (goo < 0) throw new /*leading trivia*/Exception/*trailing trivia*/(); - return 0; - } -}"; - - var expected = -@"class C -{ - /// - /// $$ - /// - /// - /// - /// - /// - int M(int goo) - { - if (goo < 0) throw new /*leading trivia*/Exception/*trailing trivia*/(); - return 0; - } -}"; + """ + class C + { + //$$ + int M(int goo) + { + if (goo < 0) throw new /*leading trivia*/Exception/*trailing trivia*/(); + return 0; + } + } + """; + + var expected = + """ + class C + { + /// + /// $$ + /// + /// + /// + /// + /// + int M(int goo) + { + if (goo < 0) throw new /*leading trivia*/Exception/*trailing trivia*/(); + return 0; + } + } + """; VerifyTypingCharacter(code, expected); } @@ -243,33 +311,37 @@ int M(int goo) public void TypingCharacter_Constructor_WithExceptions() { var code = -@"class C -{ - //$$ - public C(int goo) - { - if (goo < 0) throw new /*leading trivia*/Exception/*trailing trivia*/(); - throw null; - throw null; - } -}"; - - var expected = -@"class C -{ - /// - /// $$ - /// - /// - /// - /// - public C(int goo) - { - if (goo < 0) throw new /*leading trivia*/Exception/*trailing trivia*/(); - throw null; - throw null; - } -}"; + """ + class C + { + //$$ + public C(int goo) + { + if (goo < 0) throw new /*leading trivia*/Exception/*trailing trivia*/(); + throw null; + throw null; + } + } + """; + + var expected = + """ + class C + { + /// + /// $$ + /// + /// + /// + /// + public C(int goo) + { + if (goo < 0) throw new /*leading trivia*/Exception/*trailing trivia*/(); + throw null; + throw null; + } + } + """; VerifyTypingCharacter(code, expected); } @@ -278,59 +350,63 @@ public C(int goo) public void TypingCharacter_Constructor_WithExceptions_Caught() { // This result is wrong, but we can't do better as long as we only check syntax. - var code = @" -using System; - -class C -{ - //$$ - public C(int goo) - { - try - { - if (goo == 10) - throw new Exception(); - if (goo == 9) - throw new ArgumentOutOfRangeException(); - } - catch (ArgumentException) - { - } - - throw null; - throw null; - } -}"; - - var expected = @" -using System; - -class C -{ - /// - /// $$ - /// - /// - /// - /// - /// - public C(int goo) - { - try - { - if (goo == 10) - throw new Exception(); - if (goo == 9) - throw new ArgumentOutOfRangeException(); - } - catch (ArgumentException) - { - } - - throw null; - throw null; - } -}"; + var code = """ + + using System; + + class C + { + //$$ + public C(int goo) + { + try + { + if (goo == 10) + throw new Exception(); + if (goo == 9) + throw new ArgumentOutOfRangeException(); + } + catch (ArgumentException) + { + } + + throw null; + throw null; + } + } + """; + + var expected = """ + + using System; + + class C + { + /// + /// $$ + /// + /// + /// + /// + /// + public C(int goo) + { + try + { + if (goo == 10) + throw new Exception(); + if (goo == 9) + throw new ArgumentOutOfRangeException(); + } + catch (ArgumentException) + { + } + + throw null; + throw null; + } + } + """; VerifyTypingCharacter(code, expected); } @@ -339,23 +415,27 @@ public C(int goo) public void TypingCharacter_Method_WithVerbatimParams() { var code = -@"class C -{ - //$$ - int M<@int>(int @goo) { return 0; } -}"; - - var expected = -@"class C -{ - /// - /// $$ - /// - /// - /// - /// - int M<@int>(int @goo) { return 0; } -}"; + """ + class C + { + //$$ + int M<@int>(int @goo) { return 0; } + } + """; + + var expected = + """ + class C + { + /// + /// $$ + /// + /// + /// + /// + int M<@int>(int @goo) { return 0; } + } + """; VerifyTypingCharacter(code, expected); } @@ -364,20 +444,24 @@ public void TypingCharacter_Method_WithVerbatimParams() public void TypingCharacter_AutoProperty() { var code = -@"class C -{ - //$$ - int P { get; set; } -}"; + """ + class C + { + //$$ + int P { get; set; } + } + """; var expected = -@"class C -{ - /// - /// $$ - /// - int P { get; set; } -}"; + """ + class C + { + /// + /// $$ + /// + int P { get; set; } + } + """; VerifyTypingCharacter(code, expected); } @@ -386,28 +470,32 @@ public void TypingCharacter_AutoProperty() public void TypingCharacter_Property() { var code = -@"class C -{ - //$$ - int P - { - get { return 0; } - set { } - } -}"; - - var expected = -@"class C -{ - /// - /// $$ - /// - int P - { - get { return 0; } - set { } - } -}"; + """ + class C + { + //$$ + int P + { + get { return 0; } + set { } + } + } + """; + + var expected = + """ + class C + { + /// + /// $$ + /// + int P + { + get { return 0; } + set { } + } + } + """; VerifyTypingCharacter(code, expected); } @@ -416,30 +504,34 @@ int P public void TypingCharacter_Indexer() { var code = -@"class C -{ - //$$ - int this[int index] - { - get { return 0; } - set { } - } -}"; - - var expected = -@"class C -{ - /// - /// $$ - /// - /// - /// - int this[int index] - { - get { return 0; } - set { } - } -}"; + """ + class C + { + //$$ + int this[int index] + { + get { return 0; } + set { } + } + } + """; + + var expected = + """ + class C + { + /// + /// $$ + /// + /// + /// + int this[int index] + { + get { return 0; } + set { } + } + } + """; VerifyTypingCharacter(code, expected); } @@ -448,22 +540,26 @@ int this[int index] public void TypingCharacter_VoidMethod1() { var code = -@"class C -{ - //$$ - void M(int goo) { } -}"; + """ + class C + { + //$$ + void M(int goo) { } + } + """; var expected = -@"class C -{ - /// - /// $$ - /// - /// - /// - void M(int goo) { } -}"; + """ + class C + { + /// + /// $$ + /// + /// + /// + void M(int goo) { } + } + """; VerifyTypingCharacter(code, expected); } @@ -472,22 +568,26 @@ void M(int goo) { } public void TypingCharacter_VoidMethod_WithVerbatimParams() { var code = -@"class C -{ - //$$ - void M<@T>(int @int) { } -}"; + """ + class C + { + //$$ + void M<@T>(int @int) { } + } + """; var expected = -@"class C -{ - /// - /// $$ - /// - /// - /// - void M<@T>(int @int) { } -}"; + """ + class C + { + /// + /// $$ + /// + /// + /// + void M<@T>(int @int) { } + } + """; VerifyTypingCharacter(code, expected); } @@ -496,38 +596,46 @@ void M<@T>(int @int) { } public void TypingCharacter_VoidMethod2() { var code = -@"class C -{ - //$$ - void Method() { } -}"; - var expected = -@"class C -{ - /// - /// $$ - /// - void Method() { } -}"; + """ + class C + { + //$$ + void Method() { } + } + """; + var expected = + """ + class C + { + /// + /// $$ + /// + void Method() { } + } + """; VerifyTypingCharacter(code, expected); } [WpfFact] public void TypingCharacter_NotWhenDocCommentExists1() { - var code = @" -/// -//$$ -class C -{ -}"; + var code = """ - var expected = @" -/// -///$$ -class C -{ -}"; + /// + //$$ + class C + { + } + """; + + var expected = """ + + /// + ///$$ + class C + { + } + """; VerifyTypingCharacter(code, expected); } @@ -535,21 +643,25 @@ class C [WpfFact] public void TypingCharacter_NotWhenDocCommentExists2() { - var code = @" -/// + var code = """ -//$$ -class C -{ -}"; + /// - var expected = @" -/// + //$$ + class C + { + } + """; -///$$ -class C -{ -}"; + var expected = """ + + /// + + ///$$ + class C + { + } + """; VerifyTypingCharacter(code, expected); } @@ -557,21 +669,25 @@ class C [WpfFact] public void TypingCharacter_NotWhenDocCommentExists3() { - var code = @" -class B { } /// + var code = """ -//$$ -class C -{ -}"; + class B { } /// - var expected = @" -class B { } /// + //$$ + class C + { + } + """; -///$$ -class C -{ -}"; + var expected = """ + + class B { } /// + + ///$$ + class C + { + } + """; VerifyTypingCharacter(code, expected); } @@ -580,18 +696,22 @@ class C public void TypingCharacter_NotWhenDocCommentExists4() { var code = -@"//$$ -/// -class C -{ -}"; + """ + //$$ + /// + class C + { + } + """; var expected = -@"///$$ -/// -class C -{ -}"; + """ + ///$$ + /// + class C + { + } + """; VerifyTypingCharacter(code, expected); } @@ -600,20 +720,24 @@ class C public void TypingCharacter_NotWhenDocCommentExists5() { var code = -@"class C -{ - //$$ - /// - int M(int goo) { return 0; } -}"; + """ + class C + { + //$$ + /// + int M(int goo) { return 0; } + } + """; var expected = -@"class C -{ - ///$$ - /// - int M(int goo) { return 0; } -}"; + """ + class C + { + ///$$ + /// + int M(int goo) { return 0; } + } + """; VerifyTypingCharacter(code, expected); } @@ -622,22 +746,26 @@ public void TypingCharacter_NotWhenDocCommentExists5() public void TypingCharacter_NotInsideMethodBody1() { var code = -@"class C -{ - void M(int goo) - { - //$$ - } -}"; + """ + class C + { + void M(int goo) + { + //$$ + } + } + """; var expected = -@"class C -{ - void M(int goo) - { - ///$$ - } -}"; + """ + class C + { + void M(int goo) + { + ///$$ + } + } + """; VerifyTypingCharacter(code, expected); } @@ -646,24 +774,28 @@ void M(int goo) public void TypingCharacter_NotInsideMethodBody2() { var code = -@"class C -{ - /// - void M(int goo) - { - //$$ - } -}"; - - var expected = -@"class C -{ - /// - void M(int goo) - { - ///$$ - } -}"; + """ + class C + { + /// + void M(int goo) + { + //$$ + } + } + """; + + var expected = + """ + class C + { + /// + void M(int goo) + { + ///$$ + } + } + """; VerifyTypingCharacter(code, expected); } @@ -672,14 +804,18 @@ void M(int goo) public void TypingCharacter_NotAfterClassName() { var code = -@"class C//$$ -{ -}"; + """ + class C//$$ + { + } + """; var expected = -@"class C///$$ -{ -}"; + """ + class C///$$ + { + } + """; VerifyTypingCharacter(code, expected); } @@ -688,14 +824,18 @@ public void TypingCharacter_NotAfterClassName() public void TypingCharacter_NotAfterOpenBrace() { var code = -@"class C -{//$$ -}"; + """ + class C + {//$$ + } + """; var expected = -@"class C -{///$$ -}"; + """ + class C + {///$$ + } + """; VerifyTypingCharacter(code, expected); } @@ -704,16 +844,20 @@ public void TypingCharacter_NotAfterOpenBrace() public void TypingCharacter_NotAfterCtorName() { var code = -@"class C -{ -C() //$$ -}"; + """ + class C + { + C() //$$ + } + """; var expected = -@"class C -{ -C() ///$$ -}"; + """ + class C + { + C() ///$$ + } + """; VerifyTypingCharacter(code, expected); } @@ -722,22 +866,26 @@ public void TypingCharacter_NotAfterCtorName() public void TypingCharacter_NotInsideCtor() { var code = -@"class C -{ -C() -{ -//$$ -} -}"; + """ + class C + { + C() + { + //$$ + } + } + """; var expected = -@"class C -{ -C() -{ -///$$ -} -}"; + """ + class C + { + C() + { + ///$$ + } + } + """; VerifyTypingCharacter(code, expected); } @@ -745,19 +893,23 @@ public void TypingCharacter_NotInsideCtor() [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/59081")] public void TypingCharacter_NotInTopLevel() { - var code = @" -using System; + var code = """ + + using System; -//$$ -Console.WriteLine(); -"; + //$$ + Console.WriteLine(); - var expected = @" -using System; + """; -///$$ -Console.WriteLine(); -"; + var expected = """ + + using System; + + ///$$ + Console.WriteLine(); + + """; VerifyTypingCharacter(code, expected); } @@ -765,19 +917,23 @@ public void TypingCharacter_NotInTopLevel() [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/59081")] public void TypingCharacter_NotInNamespace() { - var code = @" -using System; + var code = """ + + using System; + + //$$ + namespace NS { } -//$$ -namespace NS { } -"; + """; - var expected = @" -using System; + var expected = """ -///$$ -namespace NS { } -"; + using System; + + ///$$ + namespace NS { } + + """; VerifyTypingCharacter(code, expected); } @@ -786,18 +942,22 @@ namespace NS { } public void PressingEnter_InsertComment_Class1() { var code = -@"///$$ -class C -{ -}"; + """ + ///$$ + class C + { + } + """; var expected = -@"/// -/// $$ -/// -class C -{ -}"; + """ + /// + /// $$ + /// + class C + { + } + """; VerifyPressingEnter(code, expected); } @@ -806,17 +966,21 @@ class C public void PressingEnter_InsertComment_Class1_AutoGenerateXmlDocCommentsOff() { var code = -@"///$$ -class C -{ -}"; + """ + ///$$ + class C + { + } + """; var expected = -@"/// -$$ -class C -{ -}"; + """ + /// + $$ + class C + { + } + """; VerifyPressingEnter(code, expected, globalOptions: new OptionsCollection(LanguageNames.CSharp) { @@ -828,17 +992,21 @@ class C public void PressingEnter_InsertComment_Class2() { var code = -@"///$$class C -{ -}"; + """ + ///$$class C + { + } + """; var expected = -@"/// -/// $$ -/// -class C -{ -}"; + """ + /// + /// $$ + /// + class C + { + } + """; VerifyPressingEnter(code, expected); } @@ -847,17 +1015,21 @@ class C public void PressingEnter_InsertComment_Class3() { var code = -@"///$$[Goo] class C -{ -}"; + """ + ///$$[Goo] class C + { + } + """; var expected = -@"/// -/// $$ -/// -[Goo] class C -{ -}"; + """ + /// + /// $$ + /// + [Goo] class C + { + } + """; VerifyPressingEnter(code, expected); } @@ -866,15 +1038,19 @@ [Goo] class C public void PressingEnter_InsertComment_NotAfterWhitespace() { var code = - @"/// $$class C -{ -}"; + """ + /// $$class C + { + } + """; var expected = -@"/// -/// $$class C -{ -}"; + """ + /// + /// $$class C + { + } + """; VerifyPressingEnter(code, expected); } @@ -883,23 +1059,27 @@ public void PressingEnter_InsertComment_NotAfterWhitespace() public void PressingEnter_InsertComment_Method1() { var code = -@"class C -{ - ///$$ - int M(int goo) { return 0; } -}"; - - var expected = -@"class C -{ - /// - /// $$ - /// - /// - /// - /// - int M(int goo) { return 0; } -}"; + """ + class C + { + ///$$ + int M(int goo) { return 0; } + } + """; + + var expected = + """ + class C + { + /// + /// $$ + /// + /// + /// + /// + int M(int goo) { return 0; } + } + """; VerifyPressingEnter(code, expected); } @@ -908,22 +1088,26 @@ public void PressingEnter_InsertComment_Method1() public void PressingEnter_InsertComment_Method2() { var code = -@"class C -{ - ///$$int M(int goo) { return 0; } -}"; + """ + class C + { + ///$$int M(int goo) { return 0; } + } + """; var expected = -@"class C -{ - /// - /// $$ - /// - /// - /// - /// - int M(int goo) { return 0; } -}"; + """ + class C + { + /// + /// $$ + /// + /// + /// + /// + int M(int goo) { return 0; } + } + """; VerifyPressingEnter(code, expected); } @@ -932,23 +1116,27 @@ public void PressingEnter_InsertComment_Method2() public void PressingEnter_NotInMethodBody1() { var code = -@"class C -{ -void Goo() -{ -///$$ -} -}"; - - var expected = -@"class C -{ -void Goo() -{ -/// -$$ -} -}"; + """ + class C + { + void Goo() + { + ///$$ + } + } + """; + + var expected = + """ + class C + { + void Goo() + { + /// + $$ + } + } + """; VerifyPressingEnter(code, expected); } @@ -957,15 +1145,19 @@ void Goo() public void PressingEnter_NotInterleavedInClassName1() { var code = -@"class///$$ C -{ -}"; + """ + class///$$ C + { + } + """; var expected = -@"class/// -$$ C -{ -}"; + """ + class/// + $$ C + { + } + """; VerifyPressingEnter(code, expected); } @@ -974,15 +1166,19 @@ public void PressingEnter_NotInterleavedInClassName1() public void PressingEnter_NotInterleavedInClassName2() { var code = -@"class ///$$C -{ -}"; + """ + class ///$$C + { + } + """; var expected = -@"class /// -$$C -{ -}"; + """ + class /// + $$C + { + } + """; VerifyPressingEnter(code, expected); } @@ -991,15 +1187,19 @@ public void PressingEnter_NotInterleavedInClassName2() public void PressingEnter_NotInterleavedInClassName3() { var code = -@"class /// $$C -{ -}"; + """ + class /// $$C + { + } + """; var expected = -@"class /// -$$C -{ -}"; + """ + class /// + $$C + { + } + """; VerifyPressingEnter(code, expected); } @@ -1009,15 +1209,19 @@ public void PressingEnter_NotInterleavedInClassName3() public void PressingEnter_NotAfterClassName1() { var code = -@"class C ///$$ -{ -}"; + """ + class C ///$$ + { + } + """; var expected = -@"class C /// -$$ -{ -}"; + """ + class C /// + $$ + { + } + """; VerifyPressingEnter(code, expected); } @@ -1026,15 +1230,19 @@ public void PressingEnter_NotAfterClassName1() public void PressingEnter_NotAfterClassName2() { var code = -@"class C /** $$ -{ -}"; + """ + class C /** $$ + { + } + """; var expected = -@"class C /** -$$ -{ -}"; + """ + class C /** + $$ + { + } + """; VerifyPressingEnter(code, expected); } @@ -1043,17 +1251,21 @@ public void PressingEnter_NotAfterClassName2() public void PressingEnter_NotAfterCtorName() { var code = -@"class C -{ -C() ///$$ -}"; + """ + class C + { + C() ///$$ + } + """; var expected = -@"class C -{ -C() /// -$$ -}"; + """ + class C + { + C() /// + $$ + } + """; VerifyPressingEnter(code, expected); } @@ -1062,23 +1274,27 @@ public void PressingEnter_NotAfterCtorName() public void PressingEnter_NotInsideCtor() { var code = -@"class C -{ -C() -{ -///$$ -} -}"; - - var expected = -@"class C -{ -C() -{ -/// -$$ -} -}"; + """ + class C + { + C() + { + ///$$ + } + } + """; + + var expected = + """ + class C + { + C() + { + /// + $$ + } + } + """; VerifyPressingEnter(code, expected); } @@ -1087,31 +1303,35 @@ public void PressingEnter_NotInsideCtor() public void PressingEnter_NotBeforeDocComment() { var code = -@" class c1 - { -$$/// - /// - /// - /// - public async Task goo() - { - var x = 1; - } - }"; - - var expected = -@" class c1 - { - -$$/// - /// - /// - /// - public async Task goo() - { - var x = 1; - } - }"; + """ + class c1 + { + $$/// + /// + /// + /// + public async Task goo() + { + var x = 1; + } + } + """; + + var expected = + """ + class c1 + { + + $$/// + /// + /// + /// + public async Task goo() + { + var x = 1; + } + } + """; VerifyPressingEnter(code, expected); } @@ -1120,19 +1340,23 @@ public async Task goo() public void PressingEnter_InsertSlashes1() { var code = -@"///$$ -/// -class C -{ -}"; + """ + ///$$ + /// + class C + { + } + """; var expected = -@"/// -/// $$ -/// -class C -{ -}"; + """ + /// + /// $$ + /// + class C + { + } + """; VerifyPressingEnter(code, expected); } @@ -1141,21 +1365,25 @@ class C public void PressingEnter_InsertSlashes2() { var code = -@"/// -/// $$ -/// -class C -{ -}"; + """ + /// + /// $$ + /// + class C + { + } + """; var expected = -@"/// -/// -/// $$ -/// -class C -{ -}"; + """ + /// + /// + /// $$ + /// + class C + { + } + """; VerifyPressingEnter(code, expected); } @@ -1164,21 +1392,25 @@ class C public void PressingEnter_InsertSlashes3() { var code = -@" /// - /// $$ - /// - class C - { - }"; + """ + /// + /// $$ + /// + class C + { + } + """; var expected = -@" /// - /// - /// $$ - /// - class C - { - }"; + """ + /// + /// + /// $$ + /// + class C + { + } + """; VerifyPressingEnter(code, expected); } @@ -1187,17 +1419,21 @@ class C public void PressingEnter_InsertSlashes4() { var code = -@"/// $$ -class C -{ -}"; + """ + /// $$ + class C + { + } + """; var expected = -@"/// -/// $$ -class C -{ -}"; + """ + /// + /// $$ + class C + { + } + """; VerifyPressingEnter(code, expected); } @@ -1206,21 +1442,25 @@ class C public void PressingEnter_InsertSlashes5() { var code = -@" /// - /// $$ - /// - class C - { - }"; + """ + /// + /// $$ + /// + class C + { + } + """; var expected = -@" /// - /// - /// $$ - /// - class C - { - }"; + """ + /// + /// + /// $$ + /// + class C + { + } + """; VerifyPressingEnter(code, expected); } @@ -1229,17 +1469,21 @@ class C public void PressingEnter_InsertSlashes6() { var code = -@"/// $$ -class C -{ -}"; + """ + /// $$ + class C + { + } + """; var expected = -@"/// -/// $$ -class C -{ -}"; + """ + /// + /// $$ + class C + { + } + """; VerifyPressingEnter(code, expected); } @@ -1248,17 +1492,21 @@ class C public void PressingEnter_InsertSlashes7() { var code = -@" /// $$ - class C - { - }"; + """ + /// $$ + class C + { + } + """; var expected = -@" /// - /// $$ - class C - { - }"; + """ + /// + /// $$ + class C + { + } + """; VerifyPressingEnter(code, expected); } @@ -1267,16 +1515,20 @@ class C public void PressingEnter_InsertSlashes8() { var code = -@"/// -/// -/// -///$$class C {}"; - var expected = -@"/// -/// -/// -/// -/// $$class C {}"; + """ + /// + /// + /// + ///$$class C {} + """; + var expected = + """ + /// + /// + /// + /// + /// $$class C {} + """; VerifyPressingEnter(code, expected); } @@ -1284,21 +1536,25 @@ public void PressingEnter_InsertSlashes8() public void PressingEnter_InsertSlashes9() { var code = -@"class C -{ - ///$$ - /// - int M(int goo) { return 0; } -}"; + """ + class C + { + ///$$ + /// + int M(int goo) { return 0; } + } + """; var expected = -@"class C -{ - /// - /// $$ - /// - int M(int goo) { return 0; } -}"; + """ + class C + { + /// + /// $$ + /// + int M(int goo) { return 0; } + } + """; VerifyPressingEnter(code, expected); } @@ -1307,16 +1563,20 @@ public void PressingEnter_InsertSlashes9() public void PressingEnter_InsertSlashes10() { var code = -@"/// -/// -/// -///$$Go ahead and add some slashes"; + """ + /// + /// + /// + ///$$Go ahead and add some slashes + """; var expected = -@"/// -/// -/// -/// -/// $$Go ahead and add some slashes"; + """ + /// + /// + /// + /// + /// $$Go ahead and add some slashes + """; VerifyPressingEnter(code, expected); } @@ -1325,29 +1585,33 @@ public void PressingEnter_InsertSlashes10() public void PressingEnter_InsertSlashes11() { var code = -@"class C -{ - /// - /// - /// - /// $$ - void Goo(int i) - { - } -}"; - - var expected = -@"class C -{ - /// - /// - /// - /// - /// $$ - void Goo(int i) - { - } -}"; + """ + class C + { + /// + /// + /// + /// $$ + void Goo(int i) + { + } + } + """; + + var expected = + """ + class C + { + /// + /// + /// + /// + /// $$ + void Goo(int i) + { + } + } + """; VerifyPressingEnter(code, expected); } @@ -1356,19 +1620,23 @@ void Goo(int i) public void PressingEnter_InsertSlashes12_AutoGenerateXmlDocCommentsOff() { var code = -@"///$$ -/// -class C -{ -}"; + """ + ///$$ + /// + class C + { + } + """; var expected = -@"/// -/// $$ -/// -class C -{ -}"; + """ + /// + /// $$ + /// + class C + { + } + """; VerifyPressingEnter(code, expected, globalOptions: new OptionsCollection(LanguageNames.CSharp) { @@ -1380,19 +1648,23 @@ class C public void PressingEnter_DoNotInsertSlashes1() { var code = -@"/// -/// $$ -class C -{ -}"; + """ + /// + /// $$ + class C + { + } + """; var expected = -@"/// -/// -$$ -class C -{ -}"; + """ + /// + /// + $$ + class C + { + } + """; VerifyPressingEnter(code, expected); } @@ -1401,16 +1673,20 @@ class C public void PressingEnter_DoNotInsertSlashes2() { var code = -@"/// + """ + /// -///$$ -class C{}"; + ///$$ + class C{} + """; var expected = -@"/// + """ + /// -/// -$$ -class C{}"; + /// + $$ + class C{} + """; VerifyPressingEnter(code, expected); } @@ -1418,23 +1694,27 @@ class C{}"; public void PressingEnter_ExtraSlashesAfterExteriorTrivia() { var code = -@"class C -{ -C() -{ -//////$$ -} -}"; - - var expected = -@"class C -{ -C() -{ -////// -///$$ -} -}"; + """ + class C + { + C() + { + //////$$ + } + } + """; + + var expected = + """ + class C + { + C() + { + ////// + ///$$ + } + } + """; VerifyPressingEnter(code, expected); } @@ -1443,20 +1723,24 @@ public void PressingEnter_ExtraSlashesAfterExteriorTrivia() public void PressingEnter_PreserveParams() { var code = -@"/// -/// -/// -/// $$ -static void Main(string[] args) -{ }"; - var expected = -@"/// -/// -/// -/// -/// $$ -static void Main(string[] args) -{ }"; + """ + /// + /// + /// + /// $$ + static void Main(string[] args) + { } + """; + var expected = + """ + /// + /// + /// + /// + /// $$ + static void Main(string[] args) + { } + """; VerifyPressingEnter(code, expected); } @@ -1465,27 +1749,31 @@ static void Main(string[] args) public void PressingEnter_InTextBeforeSpace() { const string code = -@"class C -{ - /// - /// hello$$ world - /// - void M() - { - } -}"; + """ + class C + { + /// + /// hello$$ world + /// + void M() + { + } + } + """; const string expected = -@"class C -{ - /// - /// hello - /// $$world - /// - void M() - { - } -}"; + """ + class C + { + /// + /// hello + /// $$world + /// + void M() + { + } + } + """; VerifyPressingEnter(code, expected); } @@ -1494,27 +1782,31 @@ void M() public void PressingEnter_Indentation1() { const string code = -@"class C -{ - /// - /// hello world$$ - /// - void M() - { - } -}"; + """ + class C + { + /// + /// hello world$$ + /// + void M() + { + } + } + """; const string expected = -@"class C -{ - /// - /// hello world - /// $$ - /// - void M() - { - } -}"; + """ + class C + { + /// + /// hello world + /// $$ + /// + void M() + { + } + } + """; VerifyPressingEnter(code, expected); } @@ -1523,27 +1815,31 @@ void M() public void PressingEnter_Indentation2() { const string code = -@"class C -{ - /// - /// hello $$world - /// - void M() - { - } -}"; + """ + class C + { + /// + /// hello $$world + /// + void M() + { + } + } + """; const string expected = -@"class C -{ - /// - /// hello - /// $$world - /// - void M() - { - } -}"; + """ + class C + { + /// + /// hello + /// $$world + /// + void M() + { + } + } + """; VerifyPressingEnter(code, expected); } @@ -1552,27 +1848,31 @@ void M() public void PressingEnter_Indentation3() { const string code = -@"class C -{ - /// - /// hello$$ world - /// - void M() - { - } -}"; + """ + class C + { + /// + /// hello$$ world + /// + void M() + { + } + } + """; const string expected = -@"class C -{ - /// - /// hello - /// $$world - /// - void M() - { - } -}"; + """ + class C + { + /// + /// hello + /// $$world + /// + void M() + { + } + } + """; VerifyPressingEnter(code, expected); } @@ -1581,27 +1881,31 @@ void M() public void PressingEnter_Indentation4() { const string code = -@"class C -{ - /// - /// $$hello world - /// - void M() - { - } -}"; + """ + class C + { + /// + /// $$hello world + /// + void M() + { + } + } + """; const string expected = -@"class C -{ - /// - /// - /// $$hello world - /// - void M() - { - } -}"; + """ + class C + { + /// + /// + /// $$hello world + /// + void M() + { + } + } + """; VerifyPressingEnter(code, expected); } @@ -1610,27 +1914,31 @@ void M() public void PressingEnter_Indentation5_UseTabs() { const string code = -@"class C -{ - /// - /// hello world$$ - /// - void M() - { - } -}"; + """ + class C + { + /// + /// hello world$$ + /// + void M() + { + } + } + """; const string expected = -@"class C -{ - /// - /// hello world - /// $$ - /// - void M() - { - } -}"; + """ + class C + { + /// + /// hello world + /// $$ + /// + void M() + { + } + } + """; VerifyPressingEnter(code, expected, useTabs: true); } @@ -1639,20 +1947,24 @@ void M() public void PressingEnter_Selection1() { var code = -@"/// -/// Hello [|World|]$$! -/// -class C -{ -}"; - var expected = -@"/// -/// Hello -/// $$! -/// -class C -{ -}"; + """ + /// + /// Hello [|World|]$$! + /// + class C + { + } + """; + var expected = + """ + /// + /// Hello + /// $$! + /// + class C + { + } + """; VerifyPressingEnter(code, expected); } @@ -1661,49 +1973,59 @@ class C public void PressingEnter_Selection2() { var code = -@"/// -/// Hello $$[|World|]! -/// -class C -{ -}"; - var expected = -@"/// -/// Hello -/// $$! -/// -class C -{ -}"; + """ + /// + /// Hello $$[|World|]! + /// + class C + { + } + """; + var expected = + """ + /// + /// Hello + /// $$! + /// + class C + { + } + """; VerifyPressingEnter(code, expected); } - [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/27223")] - public void PressingEnter_XmldocInStringLiteral() - { - var code = -@"class C -{ -C() -{ -string s = @"" -/// $$ -void M() {}"" -} -}"; - - var expected = -@"class C -{ -C() -{ -string s = @"" -/// -/// $$ -void M() {}"" -} -}"; + [WpfFact] + [WorkItem("https://github.com/dotnet/roslyn/issues/27223")] + [WorkItem("https://github.com/dotnet/roslyn/issues/49564")] + public void PressingEnter_XmlDocCommentInStringLiteral() + { + var code = + """ + class C + { + C() + { + string s = @" + /// $$ + void M() {}" + } + } + """; + + var expected = + """ + class C + { + C() + { + string s = @" + /// + $$ + void M() {}" + } + } + """; VerifyPressingEnter(code, expected); } @@ -1712,17 +2034,21 @@ void M() {}"" public void Command_Class() { var code = -@"class C -{$$ -}"; + """ + class C + {$$ + } + """; var expected = -@"/// -/// $$ -/// -class C -{ -}"; + """ + /// + /// $$ + /// + class C + { + } + """; VerifyInsertCommentCommand(code, expected); } @@ -1733,10 +2059,12 @@ public void Command_Record() var code = "record R$$;"; var expected = -@"/// -/// $$ -/// -record R;"; + """ + /// + /// $$ + /// + record R; + """; VerifyInsertCommentCommand(code, expected); } @@ -1747,10 +2075,12 @@ public void Command_RecordStruct() var code = "record struct R$$;"; var expected = -@"/// -/// $$ -/// -record struct R;"; + """ + /// + /// $$ + /// + record struct R; + """; VerifyInsertCommentCommand(code, expected); } @@ -1761,12 +2091,14 @@ public void Command_RecordWithPositionalParameters() var code = "record R$$(string S, int I);"; var expected = -@"/// -/// $$ -/// -/// -/// -record R(string S, int I);"; + """ + /// + /// $$ + /// + /// + /// + record R(string S, int I); + """; VerifyInsertCommentCommand(code, expected); } @@ -1777,12 +2109,14 @@ public void Command_ClassParameters() var code = "class R$$(string S, int I);"; var expected = -@"/// -/// $$ -/// -/// -/// -class R(string S, int I);"; + """ + /// + /// $$ + /// + /// + /// + class R(string S, int I); + """; VerifyInsertCommentCommand(code, expected); } @@ -1793,12 +2127,14 @@ public void Command_RecordStructWithPositionalParameters() var code = "record struct R$$(string S, int I);"; var expected = -@"/// -/// $$ -/// -/// -/// -record struct R(string S, int I);"; + """ + /// + /// $$ + /// + /// + /// + record struct R(string S, int I); + """; VerifyInsertCommentCommand(code, expected); } @@ -1809,12 +2145,14 @@ public void Command_StructParameters() var code = "struct R$$(string S, int I);"; var expected = -@"/// -/// $$ -/// -/// -/// -struct R(string S, int I);"; + """ + /// + /// $$ + /// + /// + /// + struct R(string S, int I); + """; VerifyInsertCommentCommand(code, expected); } @@ -1823,17 +2161,21 @@ struct R(string S, int I);"; public void Command_Class_AutoGenerateXmlDocCommentsOff() { var code = -@"class C -{$$ -}"; + """ + class C + {$$ + } + """; var expected = -@"/// -/// $$ -/// -class C -{ -}"; + """ + /// + /// $$ + /// + class C + { + } + """; VerifyInsertCommentCommand(code, expected, globalOptions: new OptionsCollection(LanguageNames.CSharp) { @@ -1845,14 +2187,18 @@ class C public void Command_BeforeClass1() { var code = -@"$$ -class C { }"; + """ + $$ + class C { } + """; var expected = -@" -/// -/// $$ -/// -class C { }"; + """ + + /// + /// $$ + /// + class C { } + """; VerifyInsertCommentCommand(code, expected); } @@ -1861,16 +2207,20 @@ class C { }"; public void Command_BeforeClass2() { var code = -@"class B { } -$$ -class C { }"; + """ + class B { } + $$ + class C { } + """; var expected = -@"class B { } + """ + class B { } -/// -/// $$ -/// -class C { }"; + /// + /// $$ + /// + class C { } + """; VerifyInsertCommentCommand(code, expected); } @@ -1879,20 +2229,24 @@ class C { }"; public void Command_BeforeClass3() { var code = -@"class B -{ - $$ - class C { } -}"; - var expected = -@"class B -{ - - /// - /// $$ - /// - class C { } -}"; + """ + class B + { + $$ + class C { } + } + """; + var expected = + """ + class B + { + + /// + /// $$ + /// + class C { } + } + """; VerifyInsertCommentCommand(code, expected); } @@ -1901,14 +2255,18 @@ class C { } public void Command_Class_NotIfMultilineDocCommentExists() { var code = -@"/** -*/ -class C { $$ }"; + """ + /** + */ + class C { $$ } + """; var expected = -@"/** -*/ -class C { $$ }"; + """ + /** + */ + class C { $$ } + """; VerifyInsertCommentCommand(code, expected); } @@ -1916,22 +2274,26 @@ class C { $$ }"; public void Command_Method() { var code = -@"class C -{ - int M(int goo) { $$return 0; } -}"; + """ + class C + { + int M(int goo) { $$return 0; } + } + """; var expected = -@"class C -{ - /// - /// $$ - /// - /// - /// - /// - int M(int goo) { return 0; } -}"; + """ + class C + { + /// + /// $$ + /// + /// + /// + /// + int M(int goo) { return 0; } + } + """; VerifyInsertCommentCommand(code, expected); } @@ -1940,16 +2302,20 @@ public void Command_Method() public void Command_Class_NotIfCommentExists() { var code = -@"/// -class C -{$$ -}"; + """ + /// + class C + {$$ + } + """; var expected = -@"/// -class C -{$$ -}"; + """ + /// + class C + {$$ + } + """; VerifyInsertCommentCommand(code, expected); } @@ -1958,18 +2324,22 @@ class C public void Command_Method_NotIfCommentExists() { var code = -@"class C -{ - /// - int M(int goo) { $$return 0; } -}"; + """ + class C + { + /// + int M(int goo) { $$return 0; } + } + """; var expected = -@"class C -{ - /// - int M(int goo) { $$return 0; } -}"; + """ + class C + { + /// + int M(int goo) { $$return 0; } + } + """; VerifyInsertCommentCommand(code, expected); } @@ -1980,10 +2350,12 @@ public void Command_FirstClassOnLine() var code = @"$$class C { } class D { }"; var expected = -@"/// -/// $$ -/// -class C { } class D { }"; + """ + /// + /// $$ + /// + class C { } class D { } + """; VerifyInsertCommentCommand(code, expected); } @@ -2002,19 +2374,23 @@ public void Command_NotOnSecondClassOnLine() public void Command_FirstMethodOnLine() { var code = -@"class C -{ - protected abstract void $$Goo(); protected abstract void Bar(); -}"; + """ + class C + { + protected abstract void $$Goo(); protected abstract void Bar(); + } + """; var expected = -@"class C -{ - /// - /// $$ - /// - protected abstract void Goo(); protected abstract void Bar(); -}"; + """ + class C + { + /// + /// $$ + /// + protected abstract void Goo(); protected abstract void Bar(); + } + """; VerifyInsertCommentCommand(code, expected); } @@ -2023,16 +2399,20 @@ public void Command_FirstMethodOnLine() public void Command_NotOnSecondMethodOnLine() { var code = -@"class C -{ - protected abstract void Goo(); protected abstract void $$Bar(); -}"; + """ + class C + { + protected abstract void Goo(); protected abstract void $$Bar(); + } + """; var expected = -@"class C -{ - protected abstract void Goo(); protected abstract void $$Bar(); -}"; + """ + class C + { + protected abstract void Goo(); protected abstract void $$Bar(); + } + """; VerifyInsertCommentCommand(code, expected); } @@ -2041,28 +2421,32 @@ public void Command_NotOnSecondMethodOnLine() public void TestUseTab() { var code = -@"using System; + """ + using System; -public class Class1 -{ - //$$ - public Class1() - { - } -}"; + public class Class1 + { + //$$ + public Class1() + { + } + } + """; var expected = -@"using System; + """ + using System; -public class Class1 -{ - /// - /// $$ - /// - public Class1() - { - } -}"; + public class Class1 + { + /// + /// $$ + /// + public Class1() + { + } + } + """; VerifyTypingCharacter(code, expected, useTabs: true); } @@ -2071,27 +2455,31 @@ public Class1() public void TestOpenLineAbove1() { const string code = -@"class C -{ - /// - /// stuff$$ - /// - void M() - { - } -}"; - - var expected = -@"class C -{ - /// - /// $$ - /// stuff - /// - void M() - { - } -}"; + """ + class C + { + /// + /// stuff$$ + /// + void M() + { + } + } + """; + + var expected = + """ + class C + { + /// + /// $$ + /// stuff + /// + void M() + { + } + } + """; VerifyOpenLineAbove(code, expected); } @@ -2100,27 +2488,31 @@ void M() public void TestOpenLineAbove2() { const string code = -@"class C -{ - /// - /// $$stuff - /// - void M() - { - } -}"; + """ + class C + { + /// + /// $$stuff + /// + void M() + { + } + } + """; const string expected = -@"class C -{ - /// - /// $$ - /// stuff - /// - void M() - { - } -}"; + """ + class C + { + /// + /// $$ + /// stuff + /// + void M() + { + } + } + """; VerifyOpenLineAbove(code, expected); } @@ -2129,29 +2521,33 @@ void M() public void TestOpenLineAbove3() { const string code = -@"class C -{ - /// $$ - /// stuff - /// - void M() - { - } -}"; + """ + class C + { + /// $$ + /// stuff + /// + void M() + { + } + } + """; // Note that the caret position specified below does not look correct because // it is in virtual space in this case. const string expected = -@"class C -{ -$$ - /// - /// stuff - /// - void M() - { - } -}"; + """ + class C + { + $$ + /// + /// stuff + /// + void M() + { + } + } + """; VerifyOpenLineAbove(code, expected); } @@ -2160,27 +2556,31 @@ void M() public void TestOpenLineAbove4_Tabs() { const string code = -@"class C -{ - /// - /// $$stuff - /// - void M() - { - } -}"; + """ + class C + { + /// + /// $$stuff + /// + void M() + { + } + } + """; const string expected = -@"class C -{ - /// - /// $$ - /// stuff - /// - void M() - { - } -}"; + """ + class C + { + /// + /// $$ + /// stuff + /// + void M() + { + } + } + """; VerifyOpenLineAbove(code, expected, useTabs: true); } @@ -2189,27 +2589,31 @@ void M() public void TestOpenLineBelow1() { const string code = -@"class C -{ - /// - /// stuff$$ - /// - void M() - { - } -}"; + """ + class C + { + /// + /// stuff$$ + /// + void M() + { + } + } + """; const string expected = -@"class C -{ - /// - /// stuff - /// $$ - /// - void M() - { - } -}"; + """ + class C + { + /// + /// stuff + /// $$ + /// + void M() + { + } + } + """; VerifyOpenLineBelow(code, expected); } @@ -2218,27 +2622,31 @@ void M() public void TestOpenLineBelow2() { const string code = -@"class C -{ - /// - /// $$stuff - /// - void M() - { - } -}"; + """ + class C + { + /// + /// $$stuff + /// + void M() + { + } + } + """; const string expected = -@"class C -{ - /// - /// stuff - /// $$ - /// - void M() - { - } -}"; + """ + class C + { + /// + /// stuff + /// $$ + /// + void M() + { + } + } + """; VerifyOpenLineBelow(code, expected); } @@ -2247,17 +2655,21 @@ void M() public void TestOpenLineBelow3() { const string code = -@"/// -/// stuff -/// $$ -"; + """ + /// + /// stuff + /// $$ + + """; const string expected = -@"/// -/// stuff -/// -/// $$ -"; + """ + /// + /// stuff + /// + /// $$ + + """; VerifyOpenLineBelow(code, expected); } @@ -2266,27 +2678,31 @@ public void TestOpenLineBelow3() public void TestOpenLineBelow4_Tabs() { const string code = -@"class C -{ - /// - /// $$stuff - /// - void M() - { - } -}"; + """ + class C + { + /// + /// $$stuff + /// + void M() + { + } + } + """; const string expected = -@"class C -{ - /// - /// stuff - /// $$ - /// - void M() - { - } -}"; + """ + class C + { + /// + /// stuff + /// $$ + /// + void M() + { + } + } + """; VerifyOpenLineBelow(code, expected, useTabs: true); } @@ -2295,17 +2711,21 @@ void M() public void VerifyEnterWithTrimNewLineEditorConfigOption() { const string code = -@"/// -/// $$ -/// -class C { }"; + """ + /// + /// $$ + /// + class C { } + """; const string expected = -@"/// -/// -/// $$ -/// -class C { }"; + """ + /// + /// + /// $$ + /// + class C { } + """; VerifyPressingEnter(code, expected, useTabs: true, trimTrailingWhiteSpace: true); } @@ -2314,18 +2734,22 @@ class C { }"; public void TypingCharacter_Class_WithComment() { var code = -@"//$$ This is my class and it does great things. -class C -{ -}"; + """ + //$$ This is my class and it does great things. + class C + { + } + """; var expected = -@"/// -/// $$This is my class and it does great things. -/// -class C -{ -}"; + """ + /// + /// $$This is my class and it does great things. + /// + class C + { + } + """; VerifyTypingCharacter(code, expected); } @@ -2334,18 +2758,22 @@ class C public void TypingCharacter_Class_WithComment_NoSpace() { var code = -@"//$$This is my class and it does great things. -class C -{ -}"; + """ + //$$This is my class and it does great things. + class C + { + } + """; var expected = -@"/// -/// $$This is my class and it does great things. -/// -class C -{ -}"; + """ + /// + /// $$This is my class and it does great things. + /// + class C + { + } + """; VerifyTypingCharacter(code, expected); } diff --git a/src/EditorFeatures/TestUtilities/DocumentationComments/AbstractDocumentationCommentTests.cs b/src/EditorFeatures/TestUtilities/DocumentationComments/AbstractDocumentationCommentTests.cs index cd63b1933cdea..25d59be39ec3c 100644 --- a/src/EditorFeatures/TestUtilities/DocumentationComments/AbstractDocumentationCommentTests.cs +++ b/src/EditorFeatures/TestUtilities/DocumentationComments/AbstractDocumentationCommentTests.cs @@ -162,7 +162,7 @@ private void Verify( MarkupTestFile.GetPosition(expectedMarkup, out var expectedCode, out int _); var actual = view.TextSnapshot.GetText(); - Assert.Equal(expectedCode, actual); + AssertEx.Equal(expectedCode, actual); var endCaretPosition = view.Caret.Position.BufferPosition.Position; var actualWithCaret = actual.Insert(endCaretPosition, "$$"); From 9504a7e14f3fc7997bbb9e71bec1bb61f38ef9d2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 11 Nov 2024 12:53:43 -0800 Subject: [PATCH 257/508] ERvert --- src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs index 39ae59106d4e9..9c288cc54b153 100644 --- a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs +++ b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs @@ -121,9 +121,8 @@ private void PauseWork() // progress to synchronize the solution. lock (_gate) { - // We only pause expensive work (like updating the full solution checksum). Cheaper work (like sending over - // text edits and active doc changes can proceed). _synchronizeWorkspaceQueue.CancelExistingWork(); + _synchronizeActiveDocumentQueue.CancelExistingWork(); _isPaused = true; } } @@ -133,6 +132,7 @@ private void ResumeWork() lock (_gate) { _isPaused = false; + _synchronizeActiveDocumentQueue.AddWork(); _synchronizeWorkspaceQueue.AddWork(); } } From 66a0c7289a925e5311038755498ca4d704b49be2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 11 Nov 2024 13:39:40 -0800 Subject: [PATCH 258/508] Fix formatting of simplify-linq-expression --- ...rpSimplifyLinqExpressionCodeFixProvider.cs | 5 +- .../Core/Analyzers/Analyzers.projitems | 4 +- .../Core/CodeFixes/CodeFixes.projitems | 2 +- ...ctSimplifyLinqExpressionCodeFixProvider.cs | 62 ++++++++++++++++ ...SimplifyLinqExpressionCodeFixProvider`3.cs | 70 ------------------- ...icSimplifyLinqExpressionCodeFixProvider.vb | 2 - 6 files changed, 67 insertions(+), 78 deletions(-) create mode 100644 src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/AbstractSimplifyLinqExpressionCodeFixProvider.cs delete mode 100644 src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/AbstractSimplifyLinqExpressionCodeFixProvider`3.cs diff --git a/src/Analyzers/CSharp/CodeFixes/SimplifyLinqExpression/CSharpSimplifyLinqExpressionCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/SimplifyLinqExpression/CSharpSimplifyLinqExpressionCodeFixProvider.cs index ea9061d1df6eb..b02bc05901a65 100644 --- a/src/Analyzers/CSharp/CodeFixes/SimplifyLinqExpression/CSharpSimplifyLinqExpressionCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/SimplifyLinqExpression/CSharpSimplifyLinqExpressionCodeFixProvider.cs @@ -15,7 +15,4 @@ namespace Microsoft.CodeAnalysis.CSharp.SimplifyLinqExpression; [ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.SimplifyLinqExpression), Shared] [method: ImportingConstructor] [method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] -internal sealed class CSharpSimplifyLinqExpressionCodeFixProvider() : AbstractSimplifyLinqExpressionCodeFixProvider -{ - protected override ISyntaxFacts SyntaxFacts => CSharpSyntaxFacts.Instance; -} +internal sealed class CSharpSimplifyLinqExpressionCodeFixProvider() : AbstractSimplifyLinqExpressionCodeFixProvider; diff --git a/src/Analyzers/Core/Analyzers/Analyzers.projitems b/src/Analyzers/Core/Analyzers/Analyzers.projitems index ea81bd2ba5653..45e0c4005a393 100644 --- a/src/Analyzers/Core/Analyzers/Analyzers.projitems +++ b/src/Analyzers/Core/Analyzers/Analyzers.projitems @@ -10,7 +10,9 @@ - + + Designer + diff --git a/src/Analyzers/Core/CodeFixes/CodeFixes.projitems b/src/Analyzers/Core/CodeFixes/CodeFixes.projitems index 4bd44bf11c75d..b81658d320787 100644 --- a/src/Analyzers/Core/CodeFixes/CodeFixes.projitems +++ b/src/Analyzers/Core/CodeFixes/CodeFixes.projitems @@ -129,7 +129,7 @@ - + diff --git a/src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/AbstractSimplifyLinqExpressionCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/AbstractSimplifyLinqExpressionCodeFixProvider.cs new file mode 100644 index 0000000000000..ba6b25c251017 --- /dev/null +++ b/src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/AbstractSimplifyLinqExpressionCodeFixProvider.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.Shared.Extensions; + +namespace Microsoft.CodeAnalysis.SimplifyLinqExpression; + +internal abstract class AbstractSimplifyLinqExpressionCodeFixProvider : SyntaxEditorBasedCodeFixProvider + where TExpressionSyntax : SyntaxNode + where TInvocationExpressionSyntax : TExpressionSyntax + where TSimpleNameSyntax : TExpressionSyntax +{ + public sealed override ImmutableArray FixableDiagnosticIds + => [IDEDiagnosticIds.SimplifyLinqExpressionDiagnosticId]; + + public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) + { + RegisterCodeFix(context, AnalyzersResources.Simplify_LINQ_expression, nameof(AnalyzersResources.Simplify_LINQ_expression)); + return Task.CompletedTask; + } + + protected override Task FixAllAsync( + Document document, + ImmutableArray diagnostics, + SyntaxEditor editor, + CancellationToken cancellationToken) + { + var syntaxFacts = document.GetRequiredLanguageService(); + var root = editor.OriginalRoot; + + foreach (var diagnostic in diagnostics.OrderByDescending(diagnostics => diagnostics.Location.SourceSpan.Start)) + { + var invocation = (TInvocationExpressionSyntax)root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); + + editor.ReplaceNode(invocation, (current, generator) => + { + var invocation = (TInvocationExpressionSyntax)current; + + var memberAccess = syntaxFacts.GetExpressionOfInvocationExpression(current); + var name = (TSimpleNameSyntax)syntaxFacts.GetNameOfMemberAccessExpression(memberAccess); + var whereExpression = (TInvocationExpressionSyntax)syntaxFacts.GetExpressionOfMemberAccessExpression(memberAccess)!; + var arguments = syntaxFacts.GetArgumentsOfInvocationExpression(whereExpression); + var expression = (TExpressionSyntax)syntaxFacts.GetExpressionOfMemberAccessExpression(syntaxFacts.GetExpressionOfInvocationExpression(whereExpression))!; + + return generator.InvocationExpression( + generator.MemberAccessExpression(expression, name), + arguments).WithTriviaFrom(current); + }); + } + + return Task.CompletedTask; + } +} diff --git a/src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/AbstractSimplifyLinqExpressionCodeFixProvider`3.cs b/src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/AbstractSimplifyLinqExpressionCodeFixProvider`3.cs deleted file mode 100644 index fd404a25cbadf..0000000000000 --- a/src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/AbstractSimplifyLinqExpressionCodeFixProvider`3.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Immutable; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Editing; -using Microsoft.CodeAnalysis.LanguageService; - -namespace Microsoft.CodeAnalysis.SimplifyLinqExpression; - -internal abstract class AbstractSimplifyLinqExpressionCodeFixProvider : SyntaxEditorBasedCodeFixProvider - where TExpressionSyntax : SyntaxNode - where TInvocationExpressionSyntax : TExpressionSyntax - where TSimpleNameSyntax : TExpressionSyntax -{ - protected abstract ISyntaxFacts SyntaxFacts { get; } - - public sealed override ImmutableArray FixableDiagnosticIds - => [IDEDiagnosticIds.SimplifyLinqExpressionDiagnosticId]; - - public override Task RegisterCodeFixesAsync(CodeFixContext context) - { - RegisterCodeFix(context, AnalyzersResources.Simplify_LINQ_expression, nameof(AnalyzersResources.Simplify_LINQ_expression)); - return Task.CompletedTask; - } - - protected override Task FixAllAsync(Document document, - ImmutableArray diagnostics, - SyntaxEditor editor, - CancellationToken cancellationToken) - { - var root = editor.OriginalRoot; - var expressionsToReWrite = diagnostics.Select(d => GetInvocation(root, d)).OrderByDescending(i => i.SpanStart); - foreach (var original in expressionsToReWrite) - { - editor.ReplaceNode(original, (current, generator) => - { - var invocation = (TInvocationExpressionSyntax)current; - var (expression, name, arguments) = FindNodes(invocation); - return generator.InvocationExpression( - generator.MemberAccessExpression(expression, name), - arguments); - }); - } - - return Task.CompletedTask; - - static TInvocationExpressionSyntax GetInvocation(SyntaxNode root, Diagnostic diagnostic) - { - return (TInvocationExpressionSyntax)root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); - } - - (TExpressionSyntax Expression, TSimpleNameSyntax Name, SeparatedSyntaxList Arguments) FindNodes(TInvocationExpressionSyntax current) - { - var memberAccess = SyntaxFacts.GetExpressionOfInvocationExpression(current); - var name = (TSimpleNameSyntax)SyntaxFacts.GetNameOfMemberAccessExpression(memberAccess); - var whereExpression = (TInvocationExpressionSyntax)SyntaxFacts.GetExpressionOfMemberAccessExpression(memberAccess)!; - var arguments = SyntaxFacts.GetArgumentsOfInvocationExpression(whereExpression); - var expression = (TExpressionSyntax)SyntaxFacts.GetExpressionOfMemberAccessExpression(SyntaxFacts.GetExpressionOfInvocationExpression(whereExpression))!; - return (expression, name, arguments); - } - } -} diff --git a/src/Analyzers/VisualBasic/CodeFixes/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionCodeFixProvider.vb b/src/Analyzers/VisualBasic/CodeFixes/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionCodeFixProvider.vb index b2317a224cf4f..161980ac69ebd 100644 --- a/src/Analyzers/VisualBasic/CodeFixes/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionCodeFixProvider.vb +++ b/src/Analyzers/VisualBasic/CodeFixes/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionCodeFixProvider.vb @@ -20,7 +20,5 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SimplifyLinqExpression Public Sub New() End Sub - - Protected Overrides ReadOnly Property SyntaxFacts As ISyntaxFacts = VisualBasicSyntaxFacts.Instance End Class End Namespace From af475f1f8352e847efbb2c9c0cbe833a60665818 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 11 Nov 2024 13:45:32 -0800 Subject: [PATCH 259/508] Share fixer --- .../CodeFixes/CSharpCodeFixes.projitems | 1 - ...rpSimplifyLinqExpressionCodeFixProvider.cs | 18 -------------- ...ctSimplifyLinqExpressionCodeFixProvider.cs | 22 ++++++++--------- ...icSimplifyLinqExpressionCodeFixProvider.vb | 24 ------------------- .../CodeFixes/VisualBasicCodeFixes.projitems | 1 - 5 files changed, 11 insertions(+), 55 deletions(-) delete mode 100644 src/Analyzers/CSharp/CodeFixes/SimplifyLinqExpression/CSharpSimplifyLinqExpressionCodeFixProvider.cs delete mode 100644 src/Analyzers/VisualBasic/CodeFixes/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionCodeFixProvider.vb diff --git a/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems b/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems index 8eccdc1a5e084..0dcf3b255450c 100644 --- a/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems +++ b/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems @@ -137,7 +137,6 @@ - diff --git a/src/Analyzers/CSharp/CodeFixes/SimplifyLinqExpression/CSharpSimplifyLinqExpressionCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/SimplifyLinqExpression/CSharpSimplifyLinqExpressionCodeFixProvider.cs deleted file mode 100644 index b02bc05901a65..0000000000000 --- a/src/Analyzers/CSharp/CodeFixes/SimplifyLinqExpression/CSharpSimplifyLinqExpressionCodeFixProvider.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Composition; -using System.Diagnostics.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp.LanguageService; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.LanguageService; -using Microsoft.CodeAnalysis.SimplifyLinqExpression; - -namespace Microsoft.CodeAnalysis.CSharp.SimplifyLinqExpression; - -[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.SimplifyLinqExpression), Shared] -[method: ImportingConstructor] -[method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] -internal sealed class CSharpSimplifyLinqExpressionCodeFixProvider() : AbstractSimplifyLinqExpressionCodeFixProvider; diff --git a/src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/AbstractSimplifyLinqExpressionCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/AbstractSimplifyLinqExpressionCodeFixProvider.cs index ba6b25c251017..4de5e98dc5057 100644 --- a/src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/AbstractSimplifyLinqExpressionCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/AbstractSimplifyLinqExpressionCodeFixProvider.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; +using System.Composition; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -14,10 +16,10 @@ namespace Microsoft.CodeAnalysis.SimplifyLinqExpression; -internal abstract class AbstractSimplifyLinqExpressionCodeFixProvider : SyntaxEditorBasedCodeFixProvider - where TExpressionSyntax : SyntaxNode - where TInvocationExpressionSyntax : TExpressionSyntax - where TSimpleNameSyntax : TExpressionSyntax +[ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic, Name = PredefinedCodeFixProviderNames.SimplifyLinqExpression), Shared] +[method: ImportingConstructor] +[method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] +internal sealed class SimplifyLinqExpressionCodeFixProvider() : SyntaxEditorBasedCodeFixProvider { public sealed override ImmutableArray FixableDiagnosticIds => [IDEDiagnosticIds.SimplifyLinqExpressionDiagnosticId]; @@ -39,21 +41,19 @@ protected override Task FixAllAsync( foreach (var diagnostic in diagnostics.OrderByDescending(diagnostics => diagnostics.Location.SourceSpan.Start)) { - var invocation = (TInvocationExpressionSyntax)root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); + var invocation = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); editor.ReplaceNode(invocation, (current, generator) => { - var invocation = (TInvocationExpressionSyntax)current; - var memberAccess = syntaxFacts.GetExpressionOfInvocationExpression(current); - var name = (TSimpleNameSyntax)syntaxFacts.GetNameOfMemberAccessExpression(memberAccess); - var whereExpression = (TInvocationExpressionSyntax)syntaxFacts.GetExpressionOfMemberAccessExpression(memberAccess)!; + var name = syntaxFacts.GetNameOfMemberAccessExpression(memberAccess); + var whereExpression = syntaxFacts.GetExpressionOfMemberAccessExpression(memberAccess)!; var arguments = syntaxFacts.GetArgumentsOfInvocationExpression(whereExpression); - var expression = (TExpressionSyntax)syntaxFacts.GetExpressionOfMemberAccessExpression(syntaxFacts.GetExpressionOfInvocationExpression(whereExpression))!; + var expression = syntaxFacts.GetExpressionOfMemberAccessExpression(syntaxFacts.GetExpressionOfInvocationExpression(whereExpression))!; return generator.InvocationExpression( generator.MemberAccessExpression(expression, name), - arguments).WithTriviaFrom(current); + arguments).WithLeadingTrivia(current.GetLeadingTrivia()); }); } diff --git a/src/Analyzers/VisualBasic/CodeFixes/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionCodeFixProvider.vb b/src/Analyzers/VisualBasic/CodeFixes/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionCodeFixProvider.vb deleted file mode 100644 index 161980ac69ebd..0000000000000 --- a/src/Analyzers/VisualBasic/CodeFixes/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionCodeFixProvider.vb +++ /dev/null @@ -1,24 +0,0 @@ -' Licensed to the .NET Foundation under one or more agreements. -' The .NET Foundation licenses this file to you under the MIT license. -' See the LICENSE file in the project root for more information. - -Imports System.Composition -Imports System.Diagnostics.CodeAnalysis -Imports Microsoft.CodeAnalysis.CodeFixes -Imports Microsoft.CodeAnalysis.LanguageService -Imports Microsoft.CodeAnalysis.SimplifyLinqExpression -Imports Microsoft.CodeAnalysis.VisualBasic.LanguageService -Imports Microsoft.CodeAnalysis.VisualBasic.Syntax - -Namespace Microsoft.CodeAnalysis.VisualBasic.SimplifyLinqExpression - - Friend Class VisualBasicSimplifyLinqExpressionCodeFixProvider - Inherits AbstractSimplifyLinqExpressionCodeFixProvider(Of - InvocationExpressionSyntax, SimpleNameSyntax, ExpressionSyntax) - - - - Public Sub New() - End Sub - End Class -End Namespace diff --git a/src/Analyzers/VisualBasic/CodeFixes/VisualBasicCodeFixes.projitems b/src/Analyzers/VisualBasic/CodeFixes/VisualBasicCodeFixes.projitems index a25a0e527ed83..dcdf21dec4791 100644 --- a/src/Analyzers/VisualBasic/CodeFixes/VisualBasicCodeFixes.projitems +++ b/src/Analyzers/VisualBasic/CodeFixes/VisualBasicCodeFixes.projitems @@ -63,7 +63,6 @@ - From eff1394a84616c321478de1c96122c8e4c32611b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 11 Nov 2024 13:51:57 -0800 Subject: [PATCH 260/508] Update tests --- ...CSharpSimplifyLinqExpressionFixAllTests.cs | 3 +- .../CSharpSimplifyLinqExpressionTests.cs | 41 ++++++++++++++++++- .../Core/CodeFixes/CodeFixes.projitems | 2 +- ... SimplifyLinqExpressionCodeFixProvider.cs} | 0 ...lBasicSimplifyLinqExpressionFixAllTests.vb | 7 ++-- .../VisualBasicSimplifyLinqExpressionTests.vb | 23 ++++++----- .../BuildActionTelemetryTable/Program.cs | 2 +- 7 files changed, 59 insertions(+), 19 deletions(-) rename src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/{AbstractSimplifyLinqExpressionCodeFixProvider.cs => SimplifyLinqExpressionCodeFixProvider.cs} (100%) diff --git a/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqExpressionFixAllTests.cs b/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqExpressionFixAllTests.cs index 774562e8c2c98..4cd70149a36e7 100644 --- a/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqExpressionFixAllTests.cs +++ b/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqExpressionFixAllTests.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.SimplifyLinqExpression; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.SimplifyLinqExpression; using Microsoft.CodeAnalysis.Test.Utilities; using Xunit; @@ -12,7 +13,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Analyzers.UnitTests.SimplifyLinqExpressi using VerifyCS = CSharpCodeFixVerifier< CSharpSimplifyLinqExpressionDiagnosticAnalyzer, - CSharpSimplifyLinqExpressionCodeFixProvider>; + SimplifyLinqExpressionCodeFixProvider>; [Trait(Traits.Feature, Traits.Features.CodeActionsInlineDeclaration)] public partial class CSharpSimplifyLinqExpressionTests diff --git a/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqExpressionTests.cs b/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqExpressionTests.cs index 6e5d611754ff4..fcf08f2b894de 100644 --- a/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqExpressionTests.cs +++ b/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqExpressionTests.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.SimplifyLinqExpression; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.SimplifyLinqExpression; using Microsoft.CodeAnalysis.Test.Utilities; using Xunit; @@ -12,10 +13,10 @@ namespace Microsoft.CodeAnalysis.CSharp.Analyzers.UnitTests.SimplifyLinqExpressi using VerifyCS = CSharpCodeFixVerifier< CSharpSimplifyLinqExpressionDiagnosticAnalyzer, - CSharpSimplifyLinqExpressionCodeFixProvider>; + SimplifyLinqExpressionCodeFixProvider>; [Trait(Traits.Feature, Traits.Features.CodeActionsSimplifyLinqExpression)] -public partial class CSharpSimplifyLinqExpressionTests +public sealed partial class CSharpSimplifyLinqExpressionTests { [Theory, CombinatorialData] public static async Task TestAllowedMethodTypes( @@ -525,4 +526,40 @@ void Main() """; await VerifyCS.VerifyAnalyzerAsync(source); } + + [Fact] + public static async Task TestTrivia1() + { + await new VerifyCS.Test + { + TestCode = $$""" + using System; + using System.Linq; + using System.Collections.Generic; + + class C + { + static void Main(string[] args) + { + var v = args.Skip(1) + [|.Where(a => a.Length == 1).Count();|] + } + } + """, + FixedCode = $$""" + using System; + using System.Linq; + using System.Collections.Generic; + + class C + { + static void Main(string[] args) + { + var v = args.Skip(1) + .Count(a => a.Length == 1); + } + } + """ + }.RunAsync(); + } } diff --git a/src/Analyzers/Core/CodeFixes/CodeFixes.projitems b/src/Analyzers/Core/CodeFixes/CodeFixes.projitems index b81658d320787..a2c57739a682d 100644 --- a/src/Analyzers/Core/CodeFixes/CodeFixes.projitems +++ b/src/Analyzers/Core/CodeFixes/CodeFixes.projitems @@ -129,7 +129,7 @@ - + diff --git a/src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/AbstractSimplifyLinqExpressionCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/SimplifyLinqExpressionCodeFixProvider.cs similarity index 100% rename from src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/AbstractSimplifyLinqExpressionCodeFixProvider.cs rename to src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/SimplifyLinqExpressionCodeFixProvider.cs diff --git a/src/Analyzers/VisualBasic/Tests/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionFixAllTests.vb b/src/Analyzers/VisualBasic/Tests/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionFixAllTests.vb index 4288e2004cb24..ca5f9856e7799 100644 --- a/src/Analyzers/VisualBasic/Tests/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionFixAllTests.vb +++ b/src/Analyzers/VisualBasic/Tests/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionFixAllTests.vb @@ -3,6 +3,7 @@ ' See the LICENSE file in the project root for more information. Imports Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions +Imports Microsoft.CodeAnalysis.SimplifyLinqExpression Namespace Microsoft.CodeAnalysis.VisualBasic.SimplifyLinqExpression @@ -41,7 +42,7 @@ Module T Dim test5 = test.FirstOrDefault(Function(x) x.Equals(""!"")) End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) End Function @@ -78,7 +79,7 @@ Module T Dim test5 = Enumerable.FirstOrDefault(test, Function(x) x.Equals(""!"")) End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) End Function @@ -115,7 +116,7 @@ Module T Dim test5 = test.FirstOrDefault(Function(x) x.FirstOrDefault(Function(s) s.Equals(""!"")).Equals(""!"")) End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) End Function End Class End Namespace diff --git a/src/Analyzers/VisualBasic/Tests/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionTests.vb b/src/Analyzers/VisualBasic/Tests/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionTests.vb index 816e9b43d9e25..c4fc3b266cf4a 100644 --- a/src/Analyzers/VisualBasic/Tests/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionTests.vb +++ b/src/Analyzers/VisualBasic/Tests/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionTests.vb @@ -3,6 +3,7 @@ ' See the LICENSE file in the project root for more information. Imports Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions +Imports Microsoft.CodeAnalysis.SimplifyLinqExpression Namespace Microsoft.CodeAnalysis.VisualBasic.SimplifyLinqExpression @@ -40,7 +41,7 @@ Module T End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) End Function @@ -65,7 +66,7 @@ Module T End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyAnalyzerAsync(testCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyAnalyzerAsync(testCode) End Function @@ -100,7 +101,7 @@ Module T Dim test = (From x In data).{methodName}(Function(x) x = 1) End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) End Function @@ -125,7 +126,7 @@ Module T End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyAnalyzerAsync(testCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyAnalyzerAsync(testCode) End Function @@ -166,7 +167,7 @@ Module T End Function) End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) End Function @@ -191,7 +192,7 @@ Module T Dim output = testvar2.Where(Function(x) x = 4).{methodName}() End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyAnalyzerAsync(testCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyAnalyzerAsync(testCode) End Function @@ -236,7 +237,7 @@ Module T Dim test1 = test.{firstMethod}(Function(x) x.{secondMethod}(Function(c) c.Equals(""!"")).Equals(""!"")) End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) End Function @@ -272,7 +273,7 @@ Module T End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) End Function @@ -296,7 +297,7 @@ Module T Dim output = testvar1.Where(Function(x) x = 4).{methodName}(Function(x) x <> 1) End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyAnalyzerAsync(testCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyAnalyzerAsync(testCode) End Function @@ -312,7 +313,7 @@ Module T Dim output = testvar1.Where(Function(x) x = 4).Count() End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyAnalyzerAsync(testCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyAnalyzerAsync(testCode) End Function @@ -341,7 +342,7 @@ Module T Dim result = queryableData.Where(Expression.Lambda(Of Func(Of String, Boolean))(predicateBody, pe)).First() End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyAnalyzerAsync(testCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyAnalyzerAsync(testCode) End Function End Class End Namespace diff --git a/src/Tools/BuildActionTelemetryTable/Program.cs b/src/Tools/BuildActionTelemetryTable/Program.cs index d03b7331e2d4e..e0878467c011b 100644 --- a/src/Tools/BuildActionTelemetryTable/Program.cs +++ b/src/Tools/BuildActionTelemetryTable/Program.cs @@ -402,7 +402,7 @@ public class Program { "Microsoft.CodeAnalysis.VisualBasic.ReplaceConditionalWithStatements.VisualBasicReplaceConditionalWithStatementsCodeRefactoringProvider", "Replace Conditional With Statements (Refactoring)" }, { "Microsoft.CodeAnalysis.VisualBasic.ReplaceDocCommentTextWithTag.VisualBasicReplaceDocCommentTextWithTagCodeRefactoringProvider", "Replace Doc Comment Text With Tag (Refactoring)" }, { "Microsoft.CodeAnalysis.VisualBasic.SimplifyInterpolation.VisualBasicSimplifyInterpolationCodeFixProvider", "Simplify Interpolation" }, - { "Microsoft.CodeAnalysis.VisualBasic.SimplifyLinqExpression.VisualBasicSimplifyLinqExpressionCodeFixProvider", "Simplify Linq Expression" }, + { "Microsoft.CodeAnalysis.VisualBasic.SimplifyLinqExpression.SimplifyLinqExpressionCodeFixProvider", "Simplify Linq Expression" }, { "Microsoft.CodeAnalysis.VisualBasic.SimplifyObjectCreation.VisualBasicSimplifyObjectCreationCodeFixProvider", "Simplify Object Creation" }, { "Microsoft.CodeAnalysis.VisualBasic.SimplifyThisOrMe.VisualBasicSimplifyThisOrMeCodeFixProvider", "Simplify This Or Me" }, { "Microsoft.CodeAnalysis.VisualBasic.SimplifyTypeNames.SimplifyTypeNamesCodeFixProvider", "Simplify Type Names" }, From ad7f251742d094d44ef56813495bc47b630a66af Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 11 Nov 2024 14:09:39 -0800 Subject: [PATCH 261/508] Add test --- .../CSharpSimplifyLinqExpressionTests.cs | 8 +++--- .../SimplifyLinqExpressionCodeFixProvider.cs | 26 +++++++++++++------ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqExpressionTests.cs b/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqExpressionTests.cs index fcf08f2b894de..765f89af739a2 100644 --- a/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqExpressionTests.cs +++ b/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqExpressionTests.cs @@ -532,7 +532,7 @@ public static async Task TestTrivia1() { await new VerifyCS.Test { - TestCode = $$""" + TestCode = """ using System; using System.Linq; using System.Collections.Generic; @@ -541,12 +541,12 @@ class C { static void Main(string[] args) { - var v = args.Skip(1) - [|.Where(a => a.Length == 1).Count();|] + var v = [|args.Skip(1) + .Where(a => a.Length == 1).Count()|]; } } """, - FixedCode = $$""" + FixedCode = """ using System; using System.Linq; using System.Collections.Generic; diff --git a/src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/SimplifyLinqExpressionCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/SimplifyLinqExpressionCodeFixProvider.cs index 4de5e98dc5057..d75e75c4921c7 100644 --- a/src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/SimplifyLinqExpressionCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/SimplifyLinqExpressionCodeFixProvider.cs @@ -45,15 +45,25 @@ protected override Task FixAllAsync( editor.ReplaceNode(invocation, (current, generator) => { + // 'current' is the original full expression. like x.Where(...).Count(); + + // 'x.Where(...).Count' in the above expression var memberAccess = syntaxFacts.GetExpressionOfInvocationExpression(current); - var name = syntaxFacts.GetNameOfMemberAccessExpression(memberAccess); - var whereExpression = syntaxFacts.GetExpressionOfMemberAccessExpression(memberAccess)!; - var arguments = syntaxFacts.GetArgumentsOfInvocationExpression(whereExpression); - var expression = syntaxFacts.GetExpressionOfMemberAccessExpression(syntaxFacts.GetExpressionOfInvocationExpression(whereExpression))!; - - return generator.InvocationExpression( - generator.MemberAccessExpression(expression, name), - arguments).WithLeadingTrivia(current.GetLeadingTrivia()); + + // 'Count' in the above expression + var outerName = syntaxFacts.GetNameOfMemberAccessExpression(memberAccess); + + // 'x.Where(...)' in the above expression. + var innerInvocationExpression = syntaxFacts.GetExpressionOfMemberAccessExpression(memberAccess)!; + + // 'x.Where' in the above expression. + var innerMemberAccessExpression = syntaxFacts.GetExpressionOfInvocationExpression(innerInvocationExpression); + + // 'Where' in the above expression. + var innerName = syntaxFacts.GetNameOfMemberAccessExpression(innerMemberAccessExpression); + + // trim down to the 'x.Where(...)', except with 'Where' replaced with 'Count'. + return innerInvocationExpression.ReplaceNode(innerName, outerName.WithTriviaFrom(innerName)).WithTrailingTrivia(current.GetTrailingTrivia()); }); } From fcaaaf6d5750c3c45a34bc28facb4c23bbe1dea4 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 11 Nov 2024 14:10:29 -0800 Subject: [PATCH 262/508] Work item --- .../CSharpSimplifyLinqExpressionTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqExpressionTests.cs b/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqExpressionTests.cs index 765f89af739a2..7d340c5d7f1ce 100644 --- a/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqExpressionTests.cs +++ b/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqExpressionTests.cs @@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.SimplifyLinqExpression; using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.Analyzers.UnitTests.SimplifyLinqExpression; @@ -527,7 +528,7 @@ void Main() await VerifyCS.VerifyAnalyzerAsync(source); } - [Fact] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52283")] public static async Task TestTrivia1() { await new VerifyCS.Test From 32f7283ada0791f7a2da03fa8e8910531202bffc Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 11 Nov 2024 14:30:04 -0800 Subject: [PATCH 263/508] Adjust semantic model for method group conversion (#75719) --- .../Compilation/CSharpSemanticModel.cs | 13 +- .../Test/Symbol/Symbols/ConversionTests.cs | 341 ++++++++++++++++++ .../Test/Semantic/Semantics/Conversions.vb | 34 ++ 3 files changed, 385 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index c3405df8f8be7..4ab3ceaa48867 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -4294,11 +4294,18 @@ private OneOrMany GetMethodGroupSemanticSymbols( // we want to get the symbol that overload resolution chose for M, not the whole method group M. var conversion = (BoundConversion)boundNodeForSyntacticParent; - var method = conversion.SymbolOpt; - if ((object)method != null) + MethodSymbol method = null; + if (conversion.ConversionKind == ConversionKind.MethodGroup) + { + method = conversion.SymbolOpt; + } + else if (conversion.Operand is BoundConversion { ConversionKind: ConversionKind.MethodGroup } nestedMethodGroupConversion) { - Debug.Assert(conversion.ConversionKind == ConversionKind.MethodGroup); + method = nestedMethodGroupConversion.SymbolOpt; + } + if ((object)method != null) + { if (conversion.IsExtensionMethod) { method = ReducedExtensionMethodSymbol.Create(method); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ConversionTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ConversionTests.cs index c461c0a0129c9..ee425b79b53d3 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/ConversionTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ConversionTests.cs @@ -13,6 +13,7 @@ using Roslyn.Test.Utilities; using Xunit; using Basic.Reference.Assemblies; +using Microsoft.CodeAnalysis.Test.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols { @@ -425,6 +426,346 @@ static void Main() Assert.True(conversion.IsNumeric); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36377")] + public void GetSymbolInfo_ExplicitCastOnMethodGroup() + { + var src = """ +public class C +{ + public static void M() + { + C x = (C)C.Test; + } + + public static int Test() => 1; + + public static explicit operator C(System.Func intDelegate) + { + return new C(); + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Test"); + Assert.Equal("System.Int32 C.Test()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75833")] + public void GetSymbolInfo_ImplicitUserDefinedConversionOnMethodGroup_InLocalDeclaration() + { + var src = """ +public class C +{ + public static void M() + { + C x = C.Test; + } + + public static int Test() => 1; + + public static implicit operator C(System.Func intDelegate) + { + return new C(); + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,17): error CS0428: Cannot convert method group 'Test' to non-delegate type 'C'. Did you intend to invoke the method? + // C x = C.Test; + Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "Test").WithArguments("Test", "C").WithLocation(5, 17)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Test"); + Assert.Equal("C C.op_Implicit(System.Func intDelegate)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); // Unexpected: Should be null + var conversion = model.GetConversion(memberAccess); + Assert.Equal(ConversionKind.ExplicitUserDefined, conversion.Kind); // Unexpected: Should be NoConversion or possibly Identity for error case + Assert.Equal(ConversionKind.MethodGroup, conversion.UserDefinedFromConversion.Kind); + Assert.Equal(ConversionKind.Identity, conversion.UserDefinedToConversion.Kind); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75833")] + public void GetSymbolInfo_ImplicitUserDefinedConversionOnMethodGroup_WithToConversion_InLocalDeclaration() + { + var src = """ +public struct C +{ + public static void M() + { + C? x = C.Test; + } + + public static int Test() => 1; + + public static implicit operator C(System.Func intDelegate) + { + return new C(); + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,18): error CS0428: Cannot convert method group 'Test' to non-delegate type 'C?'. Did you intend to invoke the method? + // C? x = C.Test; + Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "Test").WithArguments("Test", "C?").WithLocation(5, 18)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Test"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + var conversion = model.GetConversion(memberAccess); + Assert.Equal(ConversionKind.ExplicitUserDefined, conversion.Kind); // Unexpected: Should be NoConversion or possibly Identity for error case + Assert.Equal(ConversionKind.MethodGroup, conversion.UserDefinedFromConversion.Kind); + Assert.Equal(ConversionKind.Identity, conversion.UserDefinedToConversion.Kind); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75833")] + public void GetSymbolInfo_ImplicitUserDefinedConversionOnMethodGroup_InAssignemnt() + { + var src = """ +public class C +{ + public static void M() + { + C x; + x = C.Test; + } + + public static int Test() => 1; + + public static implicit operator C(System.Func intDelegate) + { + return new C(); + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (6,15): error CS0428: Cannot convert method group 'Test' to non-delegate type 'C'. Did you intend to invoke the method? + // x = C.Test; + Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "Test").WithArguments("Test", "C").WithLocation(6, 15)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Test"); + Assert.Equal("C C.op_Implicit(System.Func intDelegate)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); // Unexpected: Should be null + var conversion = model.GetConversion(memberAccess); + Assert.Equal(ConversionKind.ExplicitUserDefined, conversion.Kind); // Unexpected: Should be NoConversion or possibly Identity for error case + Assert.Equal(ConversionKind.MethodGroup, conversion.UserDefinedFromConversion.Kind); + Assert.Equal(ConversionKind.Identity, conversion.UserDefinedToConversion.Kind); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75833")] + public void GetSymbolInfo_ImplicitUserDefinedConversionOnMethodGroup_WithToConversion_InAssignment() + { + var src = """ +public struct C +{ + public static void M() + { + C? x; + x = C.Test; + } + + public static int Test() => 1; + + public static implicit operator C(System.Func intDelegate) + { + return new C(); + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (6,15): error CS0428: Cannot convert method group 'Test' to non-delegate type 'C?'. Did you intend to invoke the method? + // x = C.Test; + Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "Test").WithArguments("Test", "C?").WithLocation(6, 15)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Test"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); // Unexpected: Should be null + var conversion = model.GetConversion(memberAccess); + Assert.Equal(ConversionKind.ExplicitUserDefined, conversion.Kind); // Unexpected: Should be NoConversion or possibly Identity for error case + Assert.Equal(ConversionKind.MethodGroup, conversion.UserDefinedFromConversion.Kind); + Assert.Equal(ConversionKind.Identity, conversion.UserDefinedToConversion.Kind); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75833")] + public void GetSymbolInfo_ImplicitUserDefinedConversionOnMethodGroup_InInvocationArgument() + { + var src = """ +public class C +{ + public static void M() + { + M2(C.Test); + } + + public static void M2(C c) { } + + public static int Test() => 1; + + public static implicit operator C(System.Func intDelegate) => throw null; +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,12): error CS1503: Argument 1: cannot convert from 'method group' to 'C' + // M2(C.Test); + Diagnostic(ErrorCode.ERR_BadArgType, "C.Test").WithArguments("1", "method group", "C").WithLocation(5, 12)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Test"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + var conversion = model.GetConversion(memberAccess); + Assert.Equal(ConversionKind.Identity, conversion.Kind); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75833")] + public void GetSymbolInfo_ImplicitUserDefinedConversionOnMethodGroup_WithToConversion_InInvocationArgument() + { + var src = """ +public struct C +{ + public static void M() + { + M2(C.Test); + } + + public static void M2(C? c) { } + + public static int Test() => 1; + + public static implicit operator C(System.Func intDelegate) + { + return new C(); + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,12): error CS1503: Argument 1: cannot convert from 'method group' to 'C?' + // M2(C.Test); + Diagnostic(ErrorCode.ERR_BadArgType, "C.Test").WithArguments("1", "method group", "C?").WithLocation(5, 12)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Test"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + var conversion = model.GetConversion(memberAccess); + Assert.Equal(ConversionKind.Identity, conversion.Kind); + } + + [Fact] + public void GetSymbolInfo_MethodGroupConversionInLocalDeclaration() + { + var src = """ +public class C +{ + public static void M() + { + System.Func x = C.Test; + } + + public static int Test() => 1; +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Test"); + Assert.Equal("System.Int32 C.Test()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + var conversion = model.GetConversion(memberAccess); + Assert.Equal(ConversionKind.MethodGroup, conversion.Kind); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36377")] + public void GetSymbolInfo_TwoExplicitCastsOnMethodGroup() + { + var src = """ +public sealed class C +{ + public static void M() + { + D x = (D)(C)C.Test; + } + + public static int Test() => 1; + + public static explicit operator C(System.Func intDelegate) => throw null; +} +public sealed class D +{ + public static explicit operator D(C c) => throw null; +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Test"); + Assert.Equal("System.Int32 C.Test()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36377")] + public void GetSymbolInfo_NoConversion() + { + var src = """ +public sealed class C +{ + public static void M() + { + int x = C.Test; + } + + public static int Test() => 1; +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,19): error CS0428: Cannot convert method group 'Test' to non-delegate type 'int'. Did you intend to invoke the method? + // int x = C.Test; + Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "Test").WithArguments("Test", "int").WithLocation(5, 19)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Test"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36377")] + public void GetSymbolInfo_MethodGroupConversion() + { + var src = """ +public sealed class C +{ + public static void M() + { + System.Func x = C.Test; + } + + public static int Test() => 1; +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Test"); + Assert.Equal("System.Int32 C.Test()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + #region "Diagnostics" [Fact] public void VarianceRelationFail() diff --git a/src/Compilers/VisualBasic/Test/Semantic/Semantics/Conversions.vb b/src/Compilers/VisualBasic/Test/Semantic/Semantics/Conversions.vb index fcf63a3f1fe0b..89998c076ec36 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Semantics/Conversions.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Semantics/Conversions.vb @@ -5154,5 +5154,39 @@ True CompileAndVerify(compilation, expectedOutput:=expectedOutput).VerifyDiagnostics() End Sub + + Public Sub GetSymbolInfo_ExplicitCastOnMethodGroup() + Dim compilation = CreateCompilation( + + + ) + + compilation.AssertTheseEmitDiagnostics( +BC30581: 'AddressOf' expression cannot be converted to 'C' because 'C' is not a delegate type. + Dim x As C = DirectCast(AddressOf C.Test, C) + ~~~~~~~~~~~~~~~~ +) + + Dim tree = compilation.SyntaxTrees.Single() + Dim model = compilation.GetSemanticModel(tree) + Dim syntax = tree.GetRoot().DescendantNodes().OfType(Of UnaryExpressionSyntax)().Single() + Assert.Null(model.GetSymbolInfo(syntax).Symbol) + Assert.Null(model.GetSymbolInfo(syntax.Operand).Symbol) + End Sub + End Class End Namespace From 16101e435e468e63e89c4881dce062adc371dc21 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 11 Nov 2024 16:36:51 -0800 Subject: [PATCH 264/508] Always sync text changes and active doc info to remote workspace --- .../Core/Remote/SolutionChecksumUpdater.cs | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs index 9c288cc54b153..049d23aa50dac 100644 --- a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs +++ b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs @@ -48,7 +48,7 @@ internal sealed class SolutionChecksumUpdater private readonly AsyncBatchingWorkQueue _synchronizeActiveDocumentQueue; private readonly object _gate = new(); - private bool _isPaused; + private bool _isSynchronizeWorkspacePaused; public SolutionChecksumUpdater( Workspace workspace, @@ -91,13 +91,13 @@ public SolutionChecksumUpdater( } // Enqueue the work to sync the initial solution. - ResumeWork(); + ResumeSynchronizingPrimaryWorkspace(); } public void Shutdown() { // Try to stop any work that is in progress. - PauseWork(); + PauseSynchronizingPrimaryWorkspace(); _documentTrackingService.ActiveDocumentChanged -= OnActiveDocumentChanged; _workspace.WorkspaceChanged -= OnWorkspaceChanged; @@ -110,43 +110,33 @@ public void Shutdown() } private void OnGlobalOperationStarted(object? sender, EventArgs e) - => PauseWork(); + => PauseSynchronizingPrimaryWorkspace(); private void OnGlobalOperationStopped(object? sender, EventArgs e) - => ResumeWork(); + => ResumeSynchronizingPrimaryWorkspace(); - private void PauseWork() + private void PauseSynchronizingPrimaryWorkspace() { // An expensive global operation started (like a build). Pause ourselves and cancel any outstanding work in // progress to synchronize the solution. lock (_gate) { _synchronizeWorkspaceQueue.CancelExistingWork(); - _synchronizeActiveDocumentQueue.CancelExistingWork(); - _isPaused = true; + _isSynchronizeWorkspacePaused = true; } } - private void ResumeWork() + private void ResumeSynchronizingPrimaryWorkspace() { lock (_gate) { - _isPaused = false; - _synchronizeActiveDocumentQueue.AddWork(); + _isSynchronizeWorkspacePaused = false; _synchronizeWorkspaceQueue.AddWork(); } } private void OnWorkspaceChanged(object? sender, WorkspaceChangeEventArgs e) { - // Check if we're currently paused. If so ignore this notification. We don't want to any work in response - // to whatever the workspace is doing. - lock (_gate) - { - if (_isPaused) - return; - } - if (e.Kind == WorkspaceChangeKind.DocumentChanged) { var oldDocument = e.OldSolution.GetDocument(e.DocumentId); @@ -155,6 +145,14 @@ private void OnWorkspaceChanged(object? sender, WorkspaceChangeEventArgs e) _textChangeQueue.AddWork((oldDocument, newDocument)); } + // Check if we're currently paused. If so ignore this notification. We don't want to any work in response + // to whatever the workspace is doing. + lock (_gate) + { + if (_isSynchronizeWorkspacePaused) + return; + } + _synchronizeWorkspaceQueue.AddWork(); } From deebeee4e07da4998182736f43cfb600627865b1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 11 Nov 2024 18:32:46 -0800 Subject: [PATCH 265/508] Fix --- src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs index 049d23aa50dac..1b1fd30f9813a 100644 --- a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs +++ b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs @@ -132,6 +132,7 @@ private void ResumeSynchronizingPrimaryWorkspace() { _isSynchronizeWorkspacePaused = false; _synchronizeWorkspaceQueue.AddWork(); + _synchronizeActiveDocumentQueue.AddWork(); } } From 32a0084e5e623d0d7e524b1384c611a7149cbc8b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 11 Nov 2024 18:33:22 -0800 Subject: [PATCH 266/508] Fix --- src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs index 1b1fd30f9813a..52dbc0765c987 100644 --- a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs +++ b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs @@ -131,8 +131,8 @@ private void ResumeSynchronizingPrimaryWorkspace() lock (_gate) { _isSynchronizeWorkspacePaused = false; - _synchronizeWorkspaceQueue.AddWork(); _synchronizeActiveDocumentQueue.AddWork(); + _synchronizeWorkspaceQueue.AddWork(); } } From b22d0e330045bf6016178134cdfc72be2ab73fbc Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 11 Nov 2024 18:35:26 -0800 Subject: [PATCH 267/508] inline --- src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs index 52dbc0765c987..057bbb6c73925 100644 --- a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs +++ b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs @@ -90,8 +90,9 @@ public SolutionChecksumUpdater( _globalOperationService.Stopped += OnGlobalOperationStopped; } - // Enqueue the work to sync the initial solution. - ResumeSynchronizingPrimaryWorkspace(); + // Enqueue the work to sync the initial data over. + _synchronizeActiveDocumentQueue.AddWork(); + _synchronizeWorkspaceQueue.AddWork(); } public void Shutdown() @@ -131,7 +132,6 @@ private void ResumeSynchronizingPrimaryWorkspace() lock (_gate) { _isSynchronizeWorkspacePaused = false; - _synchronizeActiveDocumentQueue.AddWork(); _synchronizeWorkspaceQueue.AddWork(); } } From 62fc7462461596e543fc2bf6249915b15108c9f3 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Tue, 12 Nov 2024 10:50:41 +0100 Subject: [PATCH 268/508] Move HasHome out of Binder (#75851) * Move HasHome out of Binder * Fixup references * Add a comment --- .../Portable/Binder/Binder.ValueChecks.cs | 246 ----------------- .../Portable/CodeGen/CodeGenerator_HasHome.cs | 260 ++++++++++++++++++ .../CSharp/Portable/CodeGen/EmitAddress.cs | 4 +- .../Portable/CodeGen/EmitArrayInitializer.cs | 6 +- .../LocalRewriter/LocalRewriter_Call.cs | 13 +- .../LoweredDynamicOperationFactory.cs | 6 +- .../Lowering/SyntheticBoundNodeFactory.cs | 4 +- 7 files changed, 275 insertions(+), 264 deletions(-) create mode 100644 src/Compilers/CSharp/Portable/CodeGen/CodeGenerator_HasHome.cs diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index d69e82a701800..a69dfe3c04d33 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -5662,250 +5662,4 @@ void getParts(BoundInterpolatedString interpolatedString) } } } - - internal partial class Binder - { - internal enum AddressKind - { - // reference may be written to - Writeable, - - // reference itself will not be written to, but may be used for call, callvirt. - // for all purposes it is the same as Writeable, except when fetching an address of an array element - // where it results in a ".readonly" prefix to deal with array covariance. - Constrained, - - // reference itself will not be written to, nor it will be used to modify fields. - ReadOnly, - - // same as ReadOnly, but we are not supposed to get a reference to a clone - // regardless of compat settings. - ReadOnlyStrict, - } - - internal static bool IsAnyReadOnly(AddressKind addressKind) => addressKind >= AddressKind.ReadOnly; - - /// - /// Checks if expression directly or indirectly represents a value with its own home. In - /// such cases it is possible to get a reference without loading into a temporary. - /// - internal static bool HasHome( - BoundExpression expression, - AddressKind addressKind, - Symbol containingSymbol, - bool peVerifyCompatEnabled, - HashSet stackLocalsOpt) - { - Debug.Assert(containingSymbol is object); - - switch (expression.Kind) - { - case BoundKind.ArrayAccess: - if (addressKind == AddressKind.ReadOnly && !expression.Type.IsValueType && peVerifyCompatEnabled) - { - // due to array covariance getting a reference may throw ArrayTypeMismatch when element is not a struct, - // passing "readonly." prefix would prevent that, but it is unverifiable, so will make a copy in compat case - return false; - } - - return true; - - case BoundKind.PointerIndirectionOperator: - case BoundKind.RefValueOperator: - return true; - - case BoundKind.ThisReference: - var type = expression.Type; - if (type.IsReferenceType) - { - Debug.Assert(IsAnyReadOnly(addressKind), "`this` is readonly in classes"); - return true; - } - - if (!IsAnyReadOnly(addressKind) && containingSymbol is MethodSymbol { ContainingSymbol: NamedTypeSymbol, IsEffectivelyReadOnly: true }) - { - return false; - } - - return true; - - case BoundKind.ThrowExpression: - // vacuously this is true, we can take address of throw without temps - return true; - - case BoundKind.Parameter: - return IsAnyReadOnly(addressKind) || - ((BoundParameter)expression).ParameterSymbol.RefKind is not (RefKind.In or RefKind.RefReadOnlyParameter); - - case BoundKind.Local: - // locals have home unless they are byval stack locals or ref-readonly - // locals in a mutating call - var local = ((BoundLocal)expression).LocalSymbol; - return !((CodeGenerator.IsStackLocal(local, stackLocalsOpt) && local.RefKind == RefKind.None) || - (!IsAnyReadOnly(addressKind) && local.RefKind == RefKind.RefReadOnly)); - - case BoundKind.Call: - var methodRefKind = ((BoundCall)expression).Method.RefKind; - return methodRefKind == RefKind.Ref || - (IsAnyReadOnly(addressKind) && methodRefKind == RefKind.RefReadOnly); - - case BoundKind.Dup: - //NB: Dup represents locals that do not need IL slot - var dupRefKind = ((BoundDup)expression).RefKind; - return dupRefKind == RefKind.Ref || - (IsAnyReadOnly(addressKind) && dupRefKind == RefKind.RefReadOnly); - - case BoundKind.FieldAccess: - return FieldAccessHasHome((BoundFieldAccess)expression, addressKind, containingSymbol, peVerifyCompatEnabled, stackLocalsOpt); - - case BoundKind.Sequence: - return HasHome(((BoundSequence)expression).Value, addressKind, containingSymbol, peVerifyCompatEnabled, stackLocalsOpt); - - case BoundKind.AssignmentOperator: - var assignment = (BoundAssignmentOperator)expression; - if (!assignment.IsRef) - { - return false; - } - var lhsRefKind = assignment.Left.GetRefKind(); - return lhsRefKind == RefKind.Ref || - (IsAnyReadOnly(addressKind) && lhsRefKind is RefKind.RefReadOnly or RefKind.RefReadOnlyParameter); - - case BoundKind.ComplexConditionalReceiver: - Debug.Assert(HasHome( - ((BoundComplexConditionalReceiver)expression).ValueTypeReceiver, - addressKind, - containingSymbol, - peVerifyCompatEnabled, - stackLocalsOpt)); - Debug.Assert(HasHome( - ((BoundComplexConditionalReceiver)expression).ReferenceTypeReceiver, - addressKind, - containingSymbol, - peVerifyCompatEnabled, - stackLocalsOpt)); - goto case BoundKind.ConditionalReceiver; - - case BoundKind.ConditionalReceiver: - //ConditionalReceiver is a noop from Emit point of view. - it represents something that has already been pushed. - //We should never need a temp for it. - return true; - - case BoundKind.ConditionalOperator: - var conditional = (BoundConditionalOperator)expression; - - // only ref conditional may be referenced as a variable - if (!conditional.IsRef) - { - return false; - } - - // branch that has no home will need a temporary - // if both have no home, just say whole expression has no home - // so we could just use one temp for the whole thing - return HasHome(conditional.Consequence, addressKind, containingSymbol, peVerifyCompatEnabled, stackLocalsOpt) - && HasHome(conditional.Alternative, addressKind, containingSymbol, peVerifyCompatEnabled, stackLocalsOpt); - - default: - return false; - } - } - - /// - /// Special HasHome for fields. - /// A field has a readable home unless the field is a constant. - /// A ref readonly field doesn't have a writable home. - /// Other fields have a writable home unless the field is a readonly value - /// and is used outside of a constructor or init method. - /// - private static bool FieldAccessHasHome( - BoundFieldAccess fieldAccess, - AddressKind addressKind, - Symbol containingSymbol, - bool peVerifyCompatEnabled, - HashSet stackLocalsOpt) - { - Debug.Assert(containingSymbol is object); - - FieldSymbol field = fieldAccess.FieldSymbol; - - // const fields are literal values with no homes. (ex: decimal.Zero) - if (field.IsConst) - { - return false; - } - - if (field.RefKind is RefKind.Ref) - { - return true; - } - - // in readonly situations where ref to a copy is not allowed, consider fields as addressable - if (addressKind == AddressKind.ReadOnlyStrict) - { - return true; - } - - // ReadOnly references can always be taken unless we are in peverify compat mode - if (addressKind == AddressKind.ReadOnly && !peVerifyCompatEnabled) - { - return true; - } - - // Some field accesses must be values; values do not have homes. - if (fieldAccess.IsByValue) - { - return false; - } - - if (field.RefKind == RefKind.RefReadOnly) - { - return false; - } - - Debug.Assert(field.RefKind == RefKind.None); - - if (!field.IsReadOnly) - { - // in a case if we have a writeable struct field with a receiver that only has a readable home we would need to pass it via a temp. - // it would be advantageous to make a temp for the field, not for the outer struct, since the field is smaller and we can get to is by fetching references. - // NOTE: this would not be profitable if we have to satisfy verifier, since for verifiability - // we would not be able to dig for the inner field using references and the outer struct will have to be copied to a temp anyways. - if (!peVerifyCompatEnabled) - { - Debug.Assert(!IsAnyReadOnly(addressKind)); - - var receiver = fieldAccess.ReceiverOpt; - if (receiver?.Type.IsValueType == true) - { - // Check receiver: - // has writeable home -> return true - the whole chain has writeable home (also a more common case) - // has readable home -> return false - we need to copy the field - // otherwise -> return true - the copy will be made at higher level so the leaf field can have writeable home - - return HasHome(receiver, addressKind, containingSymbol, peVerifyCompatEnabled, stackLocalsOpt) - || !HasHome(receiver, AddressKind.ReadOnly, containingSymbol, peVerifyCompatEnabled, stackLocalsOpt); - } - } - - return true; - } - - // while readonly fields have home it is not valid to refer to it when not constructing. - if (!TypeSymbol.Equals(field.ContainingType, containingSymbol.ContainingSymbol as NamedTypeSymbol, TypeCompareKind.AllIgnoreOptions)) - { - return false; - } - - if (field.IsStatic) - { - return containingSymbol is MethodSymbol { MethodKind: MethodKind.StaticConstructor } or FieldSymbol { IsStatic: true }; - } - else - { - return (containingSymbol is MethodSymbol { MethodKind: MethodKind.Constructor } or FieldSymbol { IsStatic: false } or MethodSymbol { IsInitOnly: true }) && - fieldAccess.ReceiverOpt.Kind == BoundKind.ThisReference; - } - } - } } diff --git a/src/Compilers/CSharp/Portable/CodeGen/CodeGenerator_HasHome.cs b/src/Compilers/CSharp/Portable/CodeGen/CodeGenerator_HasHome.cs new file mode 100644 index 0000000000000..d54002c481560 --- /dev/null +++ b/src/Compilers/CSharp/Portable/CodeGen/CodeGenerator_HasHome.cs @@ -0,0 +1,260 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable disable + +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.CodeAnalysis.CSharp.Symbols; + +namespace Microsoft.CodeAnalysis.CSharp.CodeGen; + +internal partial class CodeGenerator +{ + internal enum AddressKind + { + // reference may be written to + Writeable, + + // reference itself will not be written to, but may be used for call, callvirt. + // for all purposes it is the same as Writeable, except when fetching an address of an array element + // where it results in a ".readonly" prefix to deal with array covariance. + Constrained, + + // reference itself will not be written to, nor it will be used to modify fields. + ReadOnly, + + // same as ReadOnly, but we are not supposed to get a reference to a clone + // regardless of compat settings. + ReadOnlyStrict, + } + + internal static bool IsAnyReadOnly(AddressKind addressKind) => addressKind >= AddressKind.ReadOnly; + + /// + /// Checks if expression directly or indirectly represents a value with its own home. In + /// such cases it is possible to get a reference without loading into a temporary. + /// + /// + /// This should be a lowered node. This method does NOT expect nodes from initial binding. + /// + internal static bool HasHome( + BoundExpression expression, + AddressKind addressKind, + Symbol containingSymbol, + bool peVerifyCompatEnabled, + HashSet stackLocalsOpt) + { + Debug.Assert(containingSymbol is object); + + switch (expression.Kind) + { + case BoundKind.ArrayAccess: + if (addressKind == AddressKind.ReadOnly && !expression.Type.IsValueType && peVerifyCompatEnabled) + { + // due to array covariance getting a reference may throw ArrayTypeMismatch when element is not a struct, + // passing "readonly." prefix would prevent that, but it is unverifiable, so will make a copy in compat case + return false; + } + + return true; + + case BoundKind.PointerIndirectionOperator: + case BoundKind.RefValueOperator: + return true; + + case BoundKind.ThisReference: + var type = expression.Type; + if (type.IsReferenceType) + { + Debug.Assert(IsAnyReadOnly(addressKind), "`this` is readonly in classes"); + return true; + } + + if (!IsAnyReadOnly(addressKind) && containingSymbol is MethodSymbol { ContainingSymbol: NamedTypeSymbol, IsEffectivelyReadOnly: true }) + { + return false; + } + + return true; + + case BoundKind.ThrowExpression: + // vacuously this is true, we can take address of throw without temps + return true; + + case BoundKind.Parameter: + return IsAnyReadOnly(addressKind) || + ((BoundParameter)expression).ParameterSymbol.RefKind is not (RefKind.In or RefKind.RefReadOnlyParameter); + + case BoundKind.Local: + // locals have home unless they are byval stack locals or ref-readonly + // locals in a mutating call + var local = ((BoundLocal)expression).LocalSymbol; + return !((CodeGenerator.IsStackLocal(local, stackLocalsOpt) && local.RefKind == RefKind.None) || + (!IsAnyReadOnly(addressKind) && local.RefKind == RefKind.RefReadOnly)); + + case BoundKind.Call: + var methodRefKind = ((BoundCall)expression).Method.RefKind; + return methodRefKind == RefKind.Ref || + (IsAnyReadOnly(addressKind) && methodRefKind == RefKind.RefReadOnly); + + case BoundKind.Dup: + //NB: Dup represents locals that do not need IL slot + var dupRefKind = ((BoundDup)expression).RefKind; + return dupRefKind == RefKind.Ref || + (IsAnyReadOnly(addressKind) && dupRefKind == RefKind.RefReadOnly); + + case BoundKind.FieldAccess: + return FieldAccessHasHome((BoundFieldAccess)expression, addressKind, containingSymbol, peVerifyCompatEnabled, stackLocalsOpt); + + case BoundKind.Sequence: + return HasHome(((BoundSequence)expression).Value, addressKind, containingSymbol, peVerifyCompatEnabled, stackLocalsOpt); + + case BoundKind.AssignmentOperator: + var assignment = (BoundAssignmentOperator)expression; + if (!assignment.IsRef) + { + return false; + } + var lhsRefKind = assignment.Left.GetRefKind(); + return lhsRefKind == RefKind.Ref || + (IsAnyReadOnly(addressKind) && lhsRefKind is RefKind.RefReadOnly or RefKind.RefReadOnlyParameter); + + case BoundKind.ComplexConditionalReceiver: + Debug.Assert(HasHome( + ((BoundComplexConditionalReceiver)expression).ValueTypeReceiver, + addressKind, + containingSymbol, + peVerifyCompatEnabled, + stackLocalsOpt)); + Debug.Assert(HasHome( + ((BoundComplexConditionalReceiver)expression).ReferenceTypeReceiver, + addressKind, + containingSymbol, + peVerifyCompatEnabled, + stackLocalsOpt)); + goto case BoundKind.ConditionalReceiver; + + case BoundKind.ConditionalReceiver: + //ConditionalReceiver is a noop from Emit point of view. - it represents something that has already been pushed. + //We should never need a temp for it. + return true; + + case BoundKind.ConditionalOperator: + var conditional = (BoundConditionalOperator)expression; + + // only ref conditional may be referenced as a variable + if (!conditional.IsRef) + { + return false; + } + + // branch that has no home will need a temporary + // if both have no home, just say whole expression has no home + // so we could just use one temp for the whole thing + return HasHome(conditional.Consequence, addressKind, containingSymbol, peVerifyCompatEnabled, stackLocalsOpt) + && HasHome(conditional.Alternative, addressKind, containingSymbol, peVerifyCompatEnabled, stackLocalsOpt); + + default: + return false; + } + } + + /// + /// Special HasHome for fields. + /// A field has a readable home unless the field is a constant. + /// A ref readonly field doesn't have a writable home. + /// Other fields have a writable home unless the field is a readonly value + /// and is used outside of a constructor or init method. + /// + private static bool FieldAccessHasHome( + BoundFieldAccess fieldAccess, + AddressKind addressKind, + Symbol containingSymbol, + bool peVerifyCompatEnabled, + HashSet stackLocalsOpt) + { + Debug.Assert(containingSymbol is object); + + FieldSymbol field = fieldAccess.FieldSymbol; + + // const fields are literal values with no homes. (ex: decimal.Zero) + if (field.IsConst) + { + return false; + } + + if (field.RefKind is RefKind.Ref) + { + return true; + } + + // in readonly situations where ref to a copy is not allowed, consider fields as addressable + if (addressKind == AddressKind.ReadOnlyStrict) + { + return true; + } + + // ReadOnly references can always be taken unless we are in peverify compat mode + if (addressKind == AddressKind.ReadOnly && !peVerifyCompatEnabled) + { + return true; + } + + // Some field accesses must be values; values do not have homes. + if (fieldAccess.IsByValue) + { + return false; + } + + if (field.RefKind == RefKind.RefReadOnly) + { + return false; + } + + Debug.Assert(field.RefKind == RefKind.None); + + if (!field.IsReadOnly) + { + // in a case if we have a writeable struct field with a receiver that only has a readable home we would need to pass it via a temp. + // it would be advantageous to make a temp for the field, not for the outer struct, since the field is smaller and we can get to is by fetching references. + // NOTE: this would not be profitable if we have to satisfy verifier, since for verifiability + // we would not be able to dig for the inner field using references and the outer struct will have to be copied to a temp anyways. + if (!peVerifyCompatEnabled) + { + Debug.Assert(!IsAnyReadOnly(addressKind)); + + var receiver = fieldAccess.ReceiverOpt; + if (receiver?.Type.IsValueType == true) + { + // Check receiver: + // has writeable home -> return true - the whole chain has writeable home (also a more common case) + // has readable home -> return false - we need to copy the field + // otherwise -> return true - the copy will be made at higher level so the leaf field can have writeable home + + return HasHome(receiver, addressKind, containingSymbol, peVerifyCompatEnabled, stackLocalsOpt) + || !HasHome(receiver, AddressKind.ReadOnly, containingSymbol, peVerifyCompatEnabled, stackLocalsOpt); + } + } + + return true; + } + + // while readonly fields have home it is not valid to refer to it when not constructing. + if (!TypeSymbol.Equals(field.ContainingType, containingSymbol.ContainingSymbol as NamedTypeSymbol, TypeCompareKind.AllIgnoreOptions)) + { + return false; + } + + if (field.IsStatic) + { + return containingSymbol is MethodSymbol { MethodKind: MethodKind.StaticConstructor } or FieldSymbol { IsStatic: true }; + } + else + { + return (containingSymbol is MethodSymbol { MethodKind: MethodKind.Constructor } or FieldSymbol { IsStatic: false } or MethodSymbol { IsInitOnly: true }) && + fieldAccess.ReceiverOpt.Kind == BoundKind.ThisReference; + } + } +} diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs index 3cc5453d326c9..f6358956bf7d4 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs @@ -5,14 +5,12 @@ #nullable disable using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Reflection.Metadata; using Microsoft.CodeAnalysis.CodeGen; using Microsoft.CodeAnalysis.CSharp.Symbols; using Roslyn.Utilities; -using static Microsoft.CodeAnalysis.CSharp.Binder; namespace Microsoft.CodeAnalysis.CSharp.CodeGen { @@ -466,7 +464,7 @@ private void EmitStaticFieldAddress(FieldSymbol field, SyntaxNode syntaxNode) /// such cases it is possible to get a reference without loading into a temporary. /// private bool HasHome(BoundExpression expression, AddressKind addressKind) - => Binder.HasHome(expression, addressKind, _method, IsPeVerifyCompatEnabled(), _stackLocals); + => HasHome(expression, addressKind, _method, IsPeVerifyCompatEnabled(), _stackLocals); private LocalDefinition EmitParameterAddress(BoundParameter parameter, AddressKind addressKind) { diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitArrayInitializer.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitArrayInitializer.cs index 0635d237c63a1..e35d326e09bb6 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitArrayInitializer.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitArrayInitializer.cs @@ -550,7 +550,7 @@ private bool TryEmitOptimizedReadonlySpanCreation(NamedTypeSymbol spanType, Boun if (inPlaceTarget is not null) { - EmitAddress(inPlaceTarget, Binder.AddressKind.Writeable); + EmitAddress(inPlaceTarget, AddressKind.Writeable); } // Map a field to the block (that makes it addressable). @@ -702,7 +702,7 @@ bool tryEmitAsCachedArrayOfConstants(BoundArrayCreation arrayCreation, ArrayType if (inPlaceTarget is not null) { - EmitAddress(inPlaceTarget, Binder.AddressKind.Writeable); + EmitAddress(inPlaceTarget, AddressKind.Writeable); } ImmutableArray constants = initializers.SelectAsArray(static init => init.ConstantValueOpt!); @@ -758,7 +758,7 @@ void emitEmptyReadonlySpan(NamedTypeSymbol spanType, BoundExpression wrappedExpr // If this is in-place initialization, call the default ctor. if (inPlaceTarget is not null) { - EmitAddress(inPlaceTarget, Binder.AddressKind.Writeable); + EmitAddress(inPlaceTarget, AddressKind.Writeable); _builder.EmitOpCode(ILOpCode.Initobj); EmitSymbolToken(spanType, wrappedExpression.Syntax); if (used) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs index 82fe7099eb900..021a5509ae2d7 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs @@ -3,14 +3,13 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; +using Microsoft.CodeAnalysis.CSharp.CodeGen; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Operations; using Roslyn.Utilities; -using Microsoft.CodeAnalysis.CSharp.CodeGen; namespace Microsoft.CodeAnalysis.CSharp { @@ -286,9 +285,9 @@ private void InterceptCallAndAdjustArguments( // Instance call receivers can be implicitly captured to temps in the emit layer, but not static call arguments // Therefore we may need to explicitly store the receiver to temp here. if (thisRefKind != RefKind.None - && !Binder.HasHome( + && !CodeGenerator.HasHome( receiverOpt, - thisRefKind == RefKind.Ref ? Binder.AddressKind.Writeable : Binder.AddressKind.ReadOnlyStrict, + thisRefKind == RefKind.Ref ? CodeGenerator.AddressKind.Writeable : CodeGenerator.AddressKind.ReadOnlyStrict, _factory.CurrentFunction, peVerifyCompatEnabled: false, stackLocalsOpt: null)) @@ -702,8 +701,8 @@ private ImmutableArray VisitArgumentsAndCaptureReceiverIfNeeded refKind = rewrittenReceiver.GetRefKind(); if (refKind == RefKind.None && - Binder.HasHome(rewrittenReceiver, - Binder.AddressKind.Constrained, + CodeGenerator.HasHome(rewrittenReceiver, + CodeGenerator.AddressKind.Constrained, _factory.CurrentFunction, peVerifyCompatEnabled: false, stackLocalsOpt: null)) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LoweredDynamicOperationFactory.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LoweredDynamicOperationFactory.cs index 0a264ac61f155..d2543a5cbf121 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LoweredDynamicOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LoweredDynamicOperationFactory.cs @@ -5,9 +5,9 @@ using System; using System.Collections.Immutable; using System.Diagnostics; +using Microsoft.CodeAnalysis.CSharp.CodeGen; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.PooledObjects; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp { @@ -547,8 +547,8 @@ internal RefKind GetReceiverRefKind(BoundExpression loweredReceiver) return RefKind.None; } - var hasHome = Binder.HasHome(loweredReceiver, - Binder.AddressKind.Writeable, + var hasHome = CodeGenerator.HasHome(loweredReceiver, + CodeGenerator.AddressKind.Writeable, _factory.CurrentFunction, peVerifyCompatEnabled: false, stackLocalsOpt: null); diff --git a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs index 3df1fe92edc03..637a3a8da0e85 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs @@ -1652,8 +1652,8 @@ public BoundLocal StoreToTemp( break; case RefKind.In: - if (!Binder.HasHome(argument, - Binder.AddressKind.ReadOnly, + if (!CodeGenerator.HasHome(argument, + CodeGenerator.AddressKind.ReadOnly, containingMethod, Compilation.IsPeVerifyCompatEnabled, stackLocalsOpt: null)) From 5780d35660dfc4b9b56d536375962179e76a19fe Mon Sep 17 00:00:00 2001 From: Jared Parsons Date: Tue, 12 Nov 2024 05:41:24 -0800 Subject: [PATCH 269/508] Update structured logger (#75865) Upgrade to latest structured logger binaries and see if that addresses the failure to read binary logs. --- eng/Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/Directory.Packages.props b/eng/Directory.Packages.props index 9395a5fb4c942..d75a411948dc8 100644 --- a/eng/Directory.Packages.props +++ b/eng/Directory.Packages.props @@ -319,7 +319,7 @@ Infra --> - + From d3176f53a9420585956260ca2124070b39e4aa4d Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Tue, 12 Nov 2024 16:07:26 +0100 Subject: [PATCH 270/508] Make VBCSCompiler pipe name insensitive to casing of the path (#75852) * Make VBCSCompiler pipe name insensitive to casing of the path * Improve comment wording Co-authored-by: Jared Parsons --------- Co-authored-by: Jared Parsons --- .../Server/VBCSCompilerTests/BuildClientTests.cs | 8 ++++++++ src/Compilers/Shared/BuildServerConnection.cs | 3 +++ 2 files changed, 11 insertions(+) diff --git a/src/Compilers/Server/VBCSCompilerTests/BuildClientTests.cs b/src/Compilers/Server/VBCSCompilerTests/BuildClientTests.cs index bca3ced3dcea0..53c327be7866a 100644 --- a/src/Compilers/Server/VBCSCompilerTests/BuildClientTests.cs +++ b/src/Compilers/Server/VBCSCompilerTests/BuildClientTests.cs @@ -344,6 +344,14 @@ public void GetPipeNameForPathOptLength() // We only have ~50 total bytes to work with on mac, so the base path must be small Assert.Equal(43, name.Length); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75714")] + public void GetPipeNameForPath_Casing() + { + var path1 = string.Format(@"q:{0}the{0}path", Path.DirectorySeparatorChar); + var path2 = string.Format(@"Q:{0}The{0}Path", Path.DirectorySeparatorChar); + Assert.Equal(BuildServerConnection.GetPipeName(path1), BuildServerConnection.GetPipeName(path2)); + } } } } diff --git a/src/Compilers/Shared/BuildServerConnection.cs b/src/Compilers/Shared/BuildServerConnection.cs index 2803cd46929ce..c8596ba8b7dc1 100644 --- a/src/Compilers/Shared/BuildServerConnection.cs +++ b/src/Compilers/Shared/BuildServerConnection.cs @@ -564,6 +564,9 @@ internal static string GetPipeName( // of this method. clientDirectory = clientDirectory.TrimEnd(Path.DirectorySeparatorChar); + // Similarly, we don't want multiple servers if the provided launch path differs in casing. + clientDirectory = clientDirectory.ToLowerInvariant(); + var pipeNameInput = $"{userName}.{isAdmin}.{clientDirectory}"; using (var sha = SHA256.Create()) { From 83480ecc7ef423e62cd4277450355c6897d7ec46 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 09:26:49 -0800 Subject: [PATCH 271/508] Only wrap when multiple --- ...pSymbolDisplayService.SymbolDescriptionBuilder.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Features/CSharp/Portable/LanguageServices/CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs b/src/Features/CSharp/Portable/LanguageServices/CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs index f46c54a95087f..469af59d9185f 100644 --- a/src/Features/CSharp/Portable/LanguageServices/CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs +++ b/src/Features/CSharp/Portable/LanguageServices/CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs @@ -120,7 +120,9 @@ protected override ImmutableArray ToMinimalDisplayParts(ISymb if (typeParameter.Length == 0) return displayParts; - // For readability, we add every 'where' on its own line. + // For readability, we add every 'where' on its own line if we have two or more constraints to wrap. + var wrappedConstraints = 0; + using var _ = ArrayBuilder.GetInstance(displayParts.Length, out var builder); for (var i = 0; i < displayParts.Length; i++) { @@ -129,17 +131,21 @@ protected override ImmutableArray ToMinimalDisplayParts(ISymb var part = displayParts[i]; if (i + 2 < displayParts.Length && part.Kind == SymbolDisplayPartKind.Keyword && - part.ToString() == "where" && displayParts[i + 1].Kind == SymbolDisplayPartKind.Space && - displayParts[i + 2].Kind == SymbolDisplayPartKind.TypeParameterName) + displayParts[i + 2].Kind == SymbolDisplayPartKind.TypeParameterName && + part.ToString() == "where") { builder.AddRange(LineBreak()); builder.AddRange(Space(4)); + wrappedConstraints++; } builder.Add(part); } + if (wrappedConstraints < 2) + return displayParts; + return builder.ToImmutableAndClear(); } From 6a24d0c4d62ca8f881bc671a1282e30f679ffc2e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 10:20:50 -0800 Subject: [PATCH 272/508] Wrap types --- ...DisplayService.SymbolDescriptionBuilder.cs | 32 +++++++---- ...ervice.AbstractSymbolDescriptionBuilder.cs | 55 +++++++++---------- 2 files changed, 47 insertions(+), 40 deletions(-) diff --git a/src/Features/CSharp/Portable/LanguageServices/CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs b/src/Features/CSharp/Portable/LanguageServices/CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs index 469af59d9185f..a5420ae37bf1b 100644 --- a/src/Features/CSharp/Portable/LanguageServices/CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs +++ b/src/Features/CSharp/Portable/LanguageServices/CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs @@ -115,32 +115,40 @@ protected override Task> GetInitializerSourceP protected override ImmutableArray ToMinimalDisplayParts(ISymbol symbol, SemanticModel semanticModel, int position, SymbolDisplayFormat format) { var displayParts = CodeAnalysis.CSharp.SymbolDisplay.ToMinimalDisplayParts(symbol, semanticModel, position, format); + return WrapConstraints(symbol, displayParts); + } + protected override ImmutableArray WrapConstraints(ISymbol symbol, ImmutableArray displayParts) + { var typeParameter = symbol.GetTypeParameters(); if (typeParameter.Length == 0) return displayParts; // For readability, we add every 'where' on its own line if we have two or more constraints to wrap. var wrappedConstraints = 0; - using var _ = ArrayBuilder.GetInstance(displayParts.Length, out var builder); - for (var i = 0; i < displayParts.Length; i++) + + var displayPartsSpans = displayParts.AsSpan(); + while (displayPartsSpans is [var firstSpan, ..]) { - // Look for `wheretype_parameter_name` and add a line break before it. - - var part = displayParts[i]; - if (i + 2 < displayParts.Length && - part.Kind == SymbolDisplayPartKind.Keyword && - displayParts[i + 1].Kind == SymbolDisplayPartKind.Space && - displayParts[i + 2].Kind == SymbolDisplayPartKind.TypeParameterName && - part.ToString() == "where") + // Look for `where T :` and add a line break before it. + if (displayPartsSpans is [ + { Kind: SymbolDisplayPartKind.Keyword } keyword, + { Kind: SymbolDisplayPartKind.Space }, + { Kind: SymbolDisplayPartKind.TypeParameterName }, + { Kind: SymbolDisplayPartKind.Space }, + { Kind: SymbolDisplayPartKind.Punctuation } punctuation, + ..] && + keyword.ToString() == "where" && + punctuation.ToString() == ":") { builder.AddRange(LineBreak()); builder.AddRange(Space(4)); wrappedConstraints++; } - builder.Add(part); + builder.Add(firstSpan); + displayPartsSpans = displayPartsSpans[1..]; } if (wrappedConstraints < 2) @@ -221,7 +229,7 @@ private async Task> GetInitializerSourcePartsA private async Task> GetInitializerSourcePartsAsync( EqualsValueClauseSyntax? equalsValue) { - if (equalsValue != null && equalsValue.Value != null) + if (equalsValue?.Value != null) { var semanticModel = GetSemanticModel(equalsValue.SyntaxTree); if (semanticModel != null) diff --git a/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs b/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs index f55d35d7ec1df..ab28e31bbbd4d 100644 --- a/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs +++ b/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs @@ -146,6 +146,9 @@ protected SemanticModel GetSemanticModel(SyntaxTree tree) protected Compilation Compilation => _semanticModel.Compilation; + protected virtual ImmutableArray WrapConstraints(ISymbol symbol, ImmutableArray displayParts) + => displayParts; + private async Task AddPartsAsync(ImmutableArray symbols) { var firstSymbol = symbols[0]; @@ -458,13 +461,32 @@ private void AddDescriptionForNamedType(INamedTypeSymbol symbol) AddAwaitablePrefix(); } - AddSymbolDescription(symbol); + if (symbol.TypeKind == TypeKind.Delegate) + { + var style = s_descriptionStyle.WithMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + + // Under the covers anonymous delegates are represented with generic types. However, we don't want + // to see the unbound form of that generic. We want to see the fully instantiated signature. + var displayParts = symbol.IsAnonymousDelegateType() + ? symbol.ToDisplayParts(style) + : symbol.OriginalDefinition.ToDisplayParts(style); + + AddToGroup(SymbolDescriptionGroups.MainDescription, WrapConstraints(symbol, displayParts)); + } + else + { + AddToGroup(SymbolDescriptionGroups.MainDescription, + WrapConstraints(symbol.OriginalDefinition, symbol.OriginalDefinition.ToDisplayParts(s_descriptionStyle))); + } + + if (symbol.NullableAnnotation == NullableAnnotation.Annotated) + AddToGroup(SymbolDescriptionGroups.MainDescription, new SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, null, "?")); if (!symbol.IsUnboundGenericType && !TypeArgumentsAndParametersAreSame(symbol) && !symbol.IsAnonymousDelegateType()) { - var allTypeParameters = symbol.GetAllTypeParameters().ToList(); + var allTypeParameters = symbol.GetAllTypeParameters(); var allTypeArguments = symbol.GetAllTypeArguments().ToList(); AddTypeParameterMapPart(allTypeParameters, allTypeArguments); @@ -478,28 +500,6 @@ private void AddDescriptionForNamedType(INamedTypeSymbol symbol) } } - private void AddSymbolDescription(INamedTypeSymbol symbol) - { - if (symbol.TypeKind == TypeKind.Delegate) - { - var style = s_descriptionStyle.WithMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.UseSpecialTypes); - - // Under the covers anonymous delegates are represented with generic types. However, we don't want - // to see the unbound form of that generic. We want to see the fully instantiated signature. - AddToGroup(SymbolDescriptionGroups.MainDescription, symbol.IsAnonymousDelegateType() - ? symbol.ToDisplayParts(style) - : symbol.OriginalDefinition.ToDisplayParts(style)); - } - else - { - AddToGroup(SymbolDescriptionGroups.MainDescription, - symbol.OriginalDefinition.ToDisplayParts(s_descriptionStyle)); - } - - if (symbol.NullableAnnotation == NullableAnnotation.Annotated) - AddToGroup(SymbolDescriptionGroups.MainDescription, new SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, null, "?")); - } - private static bool TypeArgumentsAndParametersAreSame(INamedTypeSymbol symbol) { var typeArguments = symbol.GetAllTypeArguments().ToList(); @@ -725,12 +725,12 @@ private static int GetOverloadCount(ImmutableArray symbolGroup) } protected void AddTypeParameterMapPart( - List typeParameters, + ImmutableArray typeParameters, List typeArguments) { var parts = new List(); - var count = typeParameters.Count; + var count = typeParameters.Length; for (var i = 0; i < count; i++) { parts.AddRange(TypeParameterName(typeParameters[i].Name)); @@ -746,8 +746,7 @@ protected void AddTypeParameterMapPart( } } - AddToGroup(SymbolDescriptionGroups.TypeParameterMap, - parts); + AddToGroup(SymbolDescriptionGroups.TypeParameterMap, parts); } protected void AddToGroup(SymbolDescriptionGroups group, params SymbolDisplayPart[] partsArray) From 002f3bfa1b73c9cc2bba3cb9fdfddadae49e0962 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 10:33:47 -0800 Subject: [PATCH 273/508] Add tests --- .../QuickInfo/SemanticQuickInfoSourceTests.cs | 78 ++++++++++++++++--- ...DisplayService.SymbolDescriptionBuilder.cs | 10 ++- ...ervice.AbstractSymbolDescriptionBuilder.cs | 3 +- 3 files changed, 76 insertions(+), 15 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs index f65586109000d..49b0c3dd041d4 100644 --- a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs +++ b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Security; using System.Threading; @@ -23,15 +24,21 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.QuickInfo; [Trait(Traits.Feature, Traits.Features.QuickInfo)] -public class SemanticQuickInfoSourceTests : AbstractSemanticQuickInfoSourceTests +public sealed class SemanticQuickInfoSourceTests : AbstractSemanticQuickInfoSourceTests { - private static async Task TestWithOptionsAsync(CSharpParseOptions options, string markup, params Action[] expectedResults) + private static async Task TestWithOptionsAsync( + CSharpParseOptions options, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, + params Action[] expectedResults) { using var workspace = EditorTestWorkspace.CreateCSharp(markup, options); await TestWithOptionsAsync(workspace, expectedResults); } - private static async Task TestWithOptionsAsync(CSharpCompilationOptions options, string markup, params Action[] expectedResults) + private static async Task TestWithOptionsAsync( + CSharpCompilationOptions options, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, + params Action[] expectedResults) { using var workspace = EditorTestWorkspace.CreateCSharp(markup, compilationOptions: options); await TestWithOptionsAsync(workspace, expectedResults); @@ -83,7 +90,9 @@ private static async Task TestWithOptionsAsync(Document document, QuickInfoServi } } - private static async Task VerifyWithMscorlib45Async(string markup, params Action[] expectedResults) + private static async Task VerifyWithMscorlib45Async( + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, + params Action[] expectedResults) { var xmlString = string.Format(""" @@ -98,7 +107,9 @@ private static async Task VerifyWithMscorlib45Async(string markup, params Action await VerifyWithMarkupAsync(xmlString, expectedResults); } - private static async Task VerifyWithNet8Async(string markup, params Action[] expectedResults) + private static async Task VerifyWithNet8Async( + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, + params Action[] expectedResults) { var xmlString = string.Format(""" @@ -141,13 +152,17 @@ private static async Task VerifyWithMarkupAsync(string xmlString, Action[] expectedResults) + protected override async Task TestAsync( + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, + params Action[] expectedResults) { await TestWithOptionsAsync(Options.Regular, markup, expectedResults); await TestWithOptionsAsync(Options.Script, markup, expectedResults); } - private async Task TestWithUsingsAsync(string markup, params Action[] expectedResults) + private async Task TestWithUsingsAsync( + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, + params Action[] expectedResults) { var markupWithUsings = @"using System; @@ -158,25 +173,33 @@ private async Task TestWithUsingsAsync(string markup, params Action[] expectedResults) + private Task TestInClassAsync( + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, + params Action[] expectedResults) { var markupInClass = "class C { " + markup + " }"; return TestWithUsingsAsync(markupInClass, expectedResults); } - private Task TestInMethodAsync(string markup, params Action[] expectedResults) + private Task TestInMethodAsync( + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, + params Action[] expectedResults) { var markupInMethod = "class C { void M() { " + markup + " } }"; return TestWithUsingsAsync(markupInMethod, expectedResults); } - private Task TestInMethodAsync(string markup, string extraSource, params Action[] expectedResults) + private Task TestInMethodAsync( + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string extraSource, + params Action[] expectedResults) { var markupInMethod = "class C { void M() { " + markup + " } }" + extraSource; return TestWithUsingsAsync(markupInMethod, expectedResults); } - private static async Task TestWithReferenceAsync(string sourceCode, + private static async Task TestWithReferenceAsync( + string sourceCode, string referencedCode, string sourceLanguage, string referencedLanguage, @@ -293,7 +316,7 @@ private static async Task VerifyWithReferenceWorkerAsync(string xmlString, param } } - protected async Task TestInvalidTypeInClassAsync(string code) + private async Task TestInvalidTypeInClassAsync(string code) { var codeInClass = "class C { " + code + " }"; await TestAsync(codeInClass); @@ -6882,6 +6905,37 @@ class X MainDescription("void X.M() where T : notnull")); } + [Fact] + public async Task MultipleConstraints_Type() + { + await TestAsync( +@" +class $$X where T : notnull where U : notnull +{ +}", + MainDescription(""" + class X + where T : notnull + where U : notnull + """)); + } + + [Fact] + public async Task MultipleConstraints_Method() + { + await TestAsync( +@" +class X +{ + void $$M() where T : notnull where U : notnull { } +}", + MainDescription(""" + void X.M() + where T : notnull + where U : notnull + """)); + } + [Fact] public async Task NotNullConstraint_Delegate() { diff --git a/src/Features/CSharp/Portable/LanguageServices/CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs b/src/Features/CSharp/Portable/LanguageServices/CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs index a5420ae37bf1b..73b60a1c2c4a9 100644 --- a/src/Features/CSharp/Portable/LanguageServices/CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs +++ b/src/Features/CSharp/Portable/LanguageServices/CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs @@ -131,8 +131,9 @@ protected override ImmutableArray WrapConstraints(ISymbol sym var displayPartsSpans = displayParts.AsSpan(); while (displayPartsSpans is [var firstSpan, ..]) { - // Look for `where T :` and add a line break before it. + // Look for ` where T :` and add a line break before it. if (displayPartsSpans is [ + { Kind: SymbolDisplayPartKind.Space }, { Kind: SymbolDisplayPartKind.Keyword } keyword, { Kind: SymbolDisplayPartKind.Space }, { Kind: SymbolDisplayPartKind.TypeParameterName }, @@ -142,12 +143,17 @@ protected override ImmutableArray WrapConstraints(ISymbol sym keyword.ToString() == "where" && punctuation.ToString() == ":") { + // Intentionally do not this initial space. We want to replace it with a newline and 4 spaces instead. + builder.AddRange(LineBreak()); builder.AddRange(Space(4)); wrappedConstraints++; } + else + { + builder.Add(firstSpan); + } - builder.Add(firstSpan); displayPartsSpans = displayPartsSpans[1..]; } diff --git a/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs b/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs index ab28e31bbbd4d..0d409a29b664b 100644 --- a/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs +++ b/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.DocumentationComments; using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; @@ -728,7 +729,7 @@ protected void AddTypeParameterMapPart( ImmutableArray typeParameters, List typeArguments) { - var parts = new List(); + using var _ = ArrayBuilder.GetInstance(out var parts); var count = typeParameters.Length; for (var i = 0; i < count; i++) From fa02ecd61112df0acc9e383af271db5e34f8f2a5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 10:38:24 -0800 Subject: [PATCH 274/508] Use raw strings for tests --- .../QuickInfo/SemanticQuickInfoSourceTests.cs | 9552 ++++++++++------- 1 file changed, 5378 insertions(+), 4174 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs index 49b0c3dd041d4..8b0dc9a91d517 100644 --- a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs +++ b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs @@ -165,10 +165,12 @@ private async Task TestWithUsingsAsync( params Action[] expectedResults) { var markupWithUsings = -@"using System; -using System.Collections.Generic; -using System.Linq; -" + markup; + """ + using System; + using System.Collections.Generic; + using System.Linq; + + """ + markup; await TestAsync(markupWithUsings, expectedResults); } @@ -222,19 +224,20 @@ private static async Task TestWithMetadataReferenceHelperAsync( string referencedLanguage, params Action[] expectedResults) { - var xmlString = string.Format(@" - - - -{1} - - - -{3} - - - -", sourceLanguage, SecurityElement.Escape(sourceCode), + var xmlString = string.Format(""" + + + + {1} + + + + {3} + + + + + """, sourceLanguage, SecurityElement.Escape(sourceCode), referencedLanguage, SecurityElement.Escape(referencedCode)); await VerifyWithReferenceWorkerAsync(xmlString, expectedResults); @@ -247,21 +250,22 @@ private static async Task TestWithProjectReferenceHelperAsync( string referencedLanguage, params Action[] expectedResults) { - var xmlString = string.Format(@" - - - ReferencedProject - -{1} - - - - -{3} - - - -", sourceLanguage, SecurityElement.Escape(sourceCode), + var xmlString = string.Format(""" + + + ReferencedProject + + {1} + + + + + {3} + + + + + """, sourceLanguage, SecurityElement.Escape(sourceCode), referencedLanguage, SecurityElement.Escape(referencedCode)); await VerifyWithReferenceWorkerAsync(xmlString, expectedResults); @@ -273,17 +277,18 @@ private static async Task TestInSameProjectHelperAsync( string sourceLanguage, params Action[] expectedResults) { - var xmlString = string.Format(@" - - - -{1} - - -{2} - - -", sourceLanguage, SecurityElement.Escape(sourceCode), SecurityElement.Escape(referencedCode)); + var xmlString = string.Format(""" + + + + {1} + + + {2} + + + + """, sourceLanguage, SecurityElement.Escape(sourceCode), SecurityElement.Escape(referencedCode)); await VerifyWithReferenceWorkerAsync(xmlString, expectedResults); } @@ -326,7 +331,7 @@ private async Task TestInvalidTypeInClassAsync(string code) public async Task TestNamespaceInUsingDirective() { await TestAsync( -@"using $$System;", + @"using $$System;", MainDescription("namespace System")); } @@ -334,7 +339,7 @@ await TestAsync( public async Task TestNamespaceInUsingDirective2() { await TestAsync( -@"using System.Coll$$ections.Generic;", + @"using System.Coll$$ections.Generic;", MainDescription("namespace System.Collections")); } @@ -342,7 +347,7 @@ await TestAsync( public async Task TestNamespaceInUsingDirective3() { await TestAsync( -@"using System.L$$inq;", + @"using System.L$$inq;", MainDescription("namespace System.Linq")); } @@ -350,7 +355,7 @@ await TestAsync( public async Task TestNamespaceInUsingDirectiveWithAlias() { await TestAsync( -@"using Goo = Sys$$tem.Console;", + @"using Goo = Sys$$tem.Console;", MainDescription("namespace System")); } @@ -358,7 +363,7 @@ await TestAsync( public async Task TestTypeInUsingDirectiveWithAlias() { await TestAsync( -@"using Goo = System.Con$$sole;", + @"using Goo = System.Con$$sole;", MainDescription("class System.Console")); } @@ -366,9 +371,11 @@ await TestAsync( public async Task TestDocumentationInUsingDirectiveWithAlias() { var markup = -@"using I$$ = IGoo; -///summary for interface IGoo -interface IGoo { }"; + """ + using I$$ = IGoo; + ///summary for interface IGoo + interface IGoo { } + """; await TestAsync(markup, MainDescription("interface IGoo"), @@ -379,10 +386,12 @@ await TestAsync(markup, public async Task TestDocumentationInUsingDirectiveWithAlias2() { var markup = -@"using I = IGoo; -///summary for interface IGoo -interface IGoo { } -class C : I$$ { }"; + """ + using I = IGoo; + ///summary for interface IGoo + interface IGoo { } + class C : I$$ { } + """; await TestAsync(markup, MainDescription("interface IGoo"), @@ -393,13 +402,15 @@ await TestAsync(markup, public async Task TestDocumentationInUsingDirectiveWithAlias3() { var markup = -@"using I = IGoo; -///summary for interface IGoo -interface IGoo -{ - void Goo(); -} -class C : I$$ { }"; + """ + using I = IGoo; + ///summary for interface IGoo + interface IGoo + { + void Goo(); + } + class C : I$$ { } + """; await TestAsync(markup, MainDescription("interface IGoo"), @@ -410,9 +421,11 @@ await TestAsync(markup, public async Task TestThis() { var markup = -@" -///summary for Class C -class C { string M() { return thi$$s.ToString(); } }"; + """ + + ///summary for Class C + class C { string M() { return thi$$s.ToString(); } } + """; await TestWithUsingsAsync(markup, MainDescription("class C"), @@ -423,9 +436,11 @@ await TestWithUsingsAsync(markup, public async Task TestClassWithDocComment() { var markup = -@" -///Hello! -class C { void M() { $$C obj; } }"; + """ + + ///Hello! + class C { void M() { $$C obj; } } + """; await TestAsync(markup, MainDescription("class C"), @@ -439,60 +454,70 @@ public async Task TestSingleLineDocComments() // SingleLine doc comment with leading whitespace await TestAsync( -@"///Hello! -class C -{ - void M() - { - $$C obj; - } -}", + """ + ///Hello! + class C + { + void M() + { + $$C obj; + } + } + """, MainDescription("class C"), Documentation("Hello!")); // SingleLine doc comment with space before opening tag await TestAsync( -@"/// Hello! -class C -{ - void M() - { - $$C obj; - } -}", + """ + /// Hello! + class C + { + void M() + { + $$C obj; + } + } + """, MainDescription("class C"), Documentation("Hello!")); // SingleLine doc comment with space before opening tag and leading whitespace await TestAsync( -@"/// Hello! -class C -{ - void M() - { - $$C obj; - } -}", + """ + /// Hello! + class C + { + void M() + { + $$C obj; + } + } + """, MainDescription("class C"), Documentation("Hello!")); // SingleLine doc comment with leading whitespace and blank line await TestAsync( -@"///Hello! -/// + """ + ///Hello! + /// -class C -{ - void M() - { - $$C obj; - } -}", + class C + { + void M() + { + $$C obj; + } + } + """, MainDescription("class C"), Documentation("Hello!")); // SingleLine doc comment with '\r' line separators - await TestAsync("///Hello!\r///\rclass C { void M() { $$C obj; } }", + await TestAsync(""" + ///Hello! /// class C { void M() { $$C obj; } } + """, MainDescription("class C"), Documentation("Hello!")); } @@ -504,97 +529,111 @@ public async Task TestMultiLineDocComments() // Multiline doc comment with leading whitespace await TestAsync( -@"/**Hello!*/ -class C -{ - void M() - { - $$C obj; - } -}", + """ + /**Hello!*/ + class C + { + void M() + { + $$C obj; + } + } + """, MainDescription("class C"), Documentation("Hello!")); // Multiline doc comment with space before opening tag await TestAsync( -@"/** Hello! - **/ -class C -{ - void M() - { - $$C obj; - } -}", + """ + /** Hello! + **/ + class C + { + void M() + { + $$C obj; + } + } + """, MainDescription("class C"), Documentation("Hello!")); // Multiline doc comment with space before opening tag and leading whitespace await TestAsync( -@"/** - ** Hello! - **/ -class C -{ - void M() - { - $$C obj; - } -}", + """ + /** + ** Hello! + **/ + class C + { + void M() + { + $$C obj; + } + } + """, MainDescription("class C"), Documentation("Hello!")); // Multiline doc comment with no per-line prefix await TestAsync( -@"/** - - Hello! - -*/ -class C -{ - void M() - { - $$C obj; - } -}", + """ + /** + + Hello! + + */ + class C + { + void M() + { + $$C obj; + } + } + """, MainDescription("class C"), Documentation("Hello!")); // Multiline doc comment with inconsistent per-line prefix await TestAsync( -@"/** - ** - Hello! - ** - **/ -class C -{ - void M() - { - $$C obj; - } -}", + """ + /** + ** + Hello! + ** + **/ + class C + { + void M() + { + $$C obj; + } + } + """, MainDescription("class C"), Documentation("Hello!")); // Multiline doc comment with closing comment on final line await TestAsync( -@"/** -Hello! -*/ -class C -{ - void M() - { - $$C obj; - } -}", + """ + /** + Hello! + */ + class C + { + void M() + { + $$C obj; + } + } + """, MainDescription("class C"), Documentation("Hello!")); // Multiline doc comment with '\r' line separators - await TestAsync("/**\r* \r* Hello!\r* \r*/\rclass C { void M() { $$C obj; } }", + await TestAsync(""" + /** * * Hello! * */ class C { void M() { $$C obj; } } + """, MainDescription("class C"), Documentation("Hello!")); } @@ -603,9 +642,11 @@ void M() public async Task TestMethodWithDocComment() { var markup = -@" -///Hello! -void M() { M$$() }"; + """ + + ///Hello! + void M() { M$$() } + """; await TestInClassAsync(markup, MainDescription("void C.M()"), @@ -616,7 +657,7 @@ await TestInClassAsync(markup, public async Task TestInt32() { await TestInClassAsync( -@"$$Int32 i;", + @"$$Int32 i;", MainDescription("struct System.Int32")); } @@ -624,7 +665,7 @@ await TestInClassAsync( public async Task TestBuiltInInt() { await TestInClassAsync( -@"$$int i;", + @"$$int i;", MainDescription("struct System.Int32")); } @@ -632,7 +673,7 @@ await TestInClassAsync( public async Task TestString() { await TestInClassAsync( -@"$$String s;", + @"$$String s;", MainDescription("class System.String")); } @@ -640,7 +681,7 @@ await TestInClassAsync( public async Task TestBuiltInString() { await TestInClassAsync( -@"$$string s;", + @"$$string s;", MainDescription("class System.String")); } @@ -648,7 +689,7 @@ await TestInClassAsync( public async Task TestBuiltInStringAtEndOfToken() { await TestInClassAsync( -@"string$$ s;", + @"string$$ s;", MainDescription("class System.String")); } @@ -656,7 +697,7 @@ await TestInClassAsync( public async Task TestBoolean() { await TestInClassAsync( -@"$$Boolean b;", + @"$$Boolean b;", MainDescription("struct System.Boolean")); } @@ -664,7 +705,7 @@ await TestInClassAsync( public async Task TestBuiltInBool() { await TestInClassAsync( -@"$$bool b;", + @"$$bool b;", MainDescription("struct System.Boolean")); } @@ -672,7 +713,7 @@ await TestInClassAsync( public async Task TestSingle() { await TestInClassAsync( -@"$$Single s;", + @"$$Single s;", MainDescription("struct System.Single")); } @@ -680,7 +721,7 @@ await TestInClassAsync( public async Task TestBuiltInFloat() { await TestInClassAsync( -@"$$float f;", + @"$$float f;", MainDescription("struct System.Single")); } @@ -688,63 +729,75 @@ await TestInClassAsync( public async Task TestVoidIsInvalid() { await TestInvalidTypeInClassAsync( -@"$$void M() -{ -}"); + """ + $$void M() + { + } + """); } [Fact] public async Task TestInvalidPointer1_931958() { await TestInvalidTypeInClassAsync( -@"$$T* i;"); + @"$$T* i;"); } [Fact] public async Task TestInvalidPointer2_931958() { await TestInvalidTypeInClassAsync( -@"T$$* i;"); + @"T$$* i;"); } [Fact] public async Task TestInvalidPointer3_931958() { await TestInvalidTypeInClassAsync( -@"T*$$ i;"); + @"T*$$ i;"); } [Fact] public async Task TestListOfString() { await TestInClassAsync( -@"$$List l;", + @"$$List l;", MainDescription("class System.Collections.Generic.List"), - TypeParameterMap($"\r\nT {FeaturesResources.is_} string")); + TypeParameterMap($""" + + T {FeaturesResources.is_} string + """)); } [Fact] public async Task TestListOfSomethingFromSource() { var markup = -@" -///Generic List -public class GenericList { Generic$$List t; }"; + """ + + ///Generic List + public class GenericList { Generic$$List t; } + """; await TestAsync(markup, MainDescription("class GenericList"), Documentation("Generic List"), - TypeParameterMap($"\r\nT {FeaturesResources.is_} int")); + TypeParameterMap($""" + + T {FeaturesResources.is_} int + """)); } [Fact] public async Task TestListOfT() { await TestInMethodAsync( -@"class C -{ - $$List l; -}", + """ + class C + { + $$List l; + } + """, MainDescription("class System.Collections.Generic.List")); } @@ -752,10 +805,13 @@ await TestInMethodAsync( public async Task TestDictionaryOfIntAndString() { await TestInClassAsync( -@"$$Dictionary d;", + @"$$Dictionary d;", MainDescription("class System.Collections.Generic.Dictionary"), TypeParameterMap( - Lines($"\r\nTKey {FeaturesResources.is_} int", + Lines($""" + + TKey {FeaturesResources.is_} int + """, $"TValue {FeaturesResources.is_} string"))); } @@ -763,13 +819,18 @@ await TestInClassAsync( public async Task TestDictionaryOfTAndU() { await TestInMethodAsync( -@"class C -{ - $$Dictionary d; -}", + """ + class C + { + $$Dictionary d; + } + """, MainDescription("class System.Collections.Generic.Dictionary"), TypeParameterMap( - Lines($"\r\nTKey {FeaturesResources.is_} T", + Lines($""" + + TKey {FeaturesResources.is_} T + """, $"TValue {FeaturesResources.is_} U"))); } @@ -777,19 +838,24 @@ await TestInMethodAsync( public async Task TestIEnumerableOfInt() { await TestInClassAsync( -@"$$IEnumerable M() -{ - yield break; -}", + """ + $$IEnumerable M() + { + yield break; + } + """, MainDescription("interface System.Collections.Generic.IEnumerable"), - TypeParameterMap($"\r\nT {FeaturesResources.is_} int")); + TypeParameterMap($""" + + T {FeaturesResources.is_} int + """)); } [Fact] public async Task TestEventHandler() { await TestInClassAsync( -@"event $$EventHandler e;", + @"event $$EventHandler e;", MainDescription("delegate void System.EventHandler(object sender, System.EventArgs e)")); } @@ -797,10 +863,12 @@ await TestInClassAsync( public async Task TestTypeParameter() { await TestAsync( -@"class C -{ - $$T t; -}", + """ + class C + { + $$T t; + } + """, MainDescription($"T {FeaturesResources.in_} C")); } @@ -808,10 +876,12 @@ await TestAsync( public async Task TestTypeParameterWithDocComment() { var markup = -@" -///Hello! -///T is Type Parameter -class C { $$T t; }"; + """ + + ///Hello! + ///T is Type Parameter + class C { $$T t; } + """; await TestAsync(markup, MainDescription($"T {FeaturesResources.in_} C"), @@ -822,10 +892,12 @@ await TestAsync(markup, public async Task TestTypeParameter1_Bug931949() { await TestAsync( -@"class T1 -{ - $$T11 t; -}", + """ + class T1 + { + $$T11 t; + } + """, MainDescription($"T11 {FeaturesResources.in_} T1")); } @@ -833,10 +905,12 @@ await TestAsync( public async Task TestTypeParameter2_Bug931949() { await TestAsync( -@"class T1 -{ - T$$11 t; -}", + """ + class T1 + { + T$$11 t; + } + """, MainDescription($"T11 {FeaturesResources.in_} T1")); } @@ -844,10 +918,12 @@ await TestAsync( public async Task TestTypeParameter3_Bug931949() { await TestAsync( -@"class T1 -{ - T1$$1 t; -}", + """ + class T1 + { + T1$$1 t; + } + """, MainDescription($"T11 {FeaturesResources.in_} T1")); } @@ -855,10 +931,12 @@ await TestAsync( public async Task TestTypeParameter4_Bug931949() { await TestAsync( -@"class T1 -{ - T11$$ t; -}", + """ + class T1 + { + T11$$ t; + } + """, MainDescription($"T11 {FeaturesResources.in_} T1")); } @@ -867,20 +945,25 @@ public async Task TestNullableOfInt() { await TestInClassAsync(@"$$Nullable i; }", MainDescription("struct System.Nullable where T : struct"), - TypeParameterMap($"\r\nT {FeaturesResources.is_} int")); + TypeParameterMap($""" + + T {FeaturesResources.is_} int + """)); } [Fact] public async Task TestGenericTypeDeclaredOnMethod1_Bug1946() { await TestAsync( -@"class C -{ - static void Meth1($$T1 i) where T1 : struct - { - T1 i; - } -}", + """ + class C + { + static void Meth1($$T1 i) where T1 : struct + { + T1 i; + } + } + """, MainDescription($"T1 {FeaturesResources.in_} C.Meth1 where T1 : struct")); } @@ -888,13 +971,15 @@ static void Meth1($$T1 i) where T1 : struct public async Task TestGenericTypeDeclaredOnMethod2_Bug1946() { await TestAsync( -@"class C -{ - static void Meth1(T1 i) where $$T1 : struct - { - T1 i; - } -}", + """ + class C + { + static void Meth1(T1 i) where $$T1 : struct + { + T1 i; + } + } + """, MainDescription($"T1 {FeaturesResources.in_} C.Meth1 where T1 : struct")); } @@ -902,13 +987,15 @@ static void Meth1(T1 i) where $$T1 : struct public async Task TestGenericTypeDeclaredOnMethod3_Bug1946() { await TestAsync( -@"class C -{ - static void Meth1(T1 i) where T1 : struct - { - $$T1 i; - } -}", + """ + class C + { + static void Meth1(T1 i) where T1 : struct + { + $$T1 i; + } + } + """, MainDescription($"T1 {FeaturesResources.in_} C.Meth1 where T1 : struct")); } @@ -916,9 +1003,11 @@ static void Meth1(T1 i) where T1 : struct public async Task TestGenericTypeParameterConstraint_Class() { await TestAsync( -@"class C where $$T : class -{ -}", + """ + class C where $$T : class + { + } + """, MainDescription($"T {FeaturesResources.in_} C where T : class")); } @@ -926,19 +1015,23 @@ await TestAsync( public async Task TestGenericTypeParameterConstraint_Struct() { await TestAsync( -@"struct S where $$T : class -{ -}", - MainDescription($"T {FeaturesResources.in_} S where T : class")); - } - + """ + struct S where $$T : class + { + } + """, + MainDescription($"T {FeaturesResources.in_} S where T : class")); + } + [Fact] public async Task TestGenericTypeParameterConstraint_Interface() { await TestAsync( -@"interface I where $$T : class -{ -}", + """ + interface I where $$T : class + { + } + """, MainDescription($"T {FeaturesResources.in_} I where T : class")); } @@ -946,7 +1039,7 @@ await TestAsync( public async Task TestGenericTypeParameterConstraint_Delegate() { await TestAsync( -@"delegate void D() where $$T : class;", + @"delegate void D() where $$T : class;", MainDescription($"T {FeaturesResources.in_} D where T : class")); } @@ -968,13 +1061,15 @@ await TestAsync(@"class C where $$T : System.Collections.Generic.IEnumerable< public async Task TestMethodReferenceInSameMethod() { await TestAsync( -@"class C -{ - void M() - { - M$$(); - } -}", + """ + class C + { + void M() + { + M$$(); + } + } + """, MainDescription("void C.M()")); } @@ -982,9 +1077,11 @@ void M() public async Task TestMethodReferenceInSameMethodWithDocComment() { var markup = -@" -///Hello World -void M() { M$$(); }"; + """ + + ///Hello World + void M() { M$$(); } + """; await TestInClassAsync(markup, MainDescription("void C.M()"), @@ -995,12 +1092,14 @@ await TestInClassAsync(markup, public async Task TestFieldInMethodBuiltIn() { var markup = -@"int field; + """ + int field; -void M() -{ - field$$ -}"; + void M() + { + field$$ + } + """; await TestInClassAsync(markup, MainDescription($"({FeaturesResources.field}) int C.field")); @@ -1010,12 +1109,14 @@ await TestInClassAsync(markup, public async Task TestFieldInMethodBuiltIn2() { await TestInClassAsync( -@"int field; + """ + int field; -void M() -{ - int f = field$$; -}", + void M() + { + int f = field$$; + } + """, MainDescription($"({FeaturesResources.field}) int C.field")); } @@ -1023,21 +1124,25 @@ void M() public async Task TestFieldInMethodBuiltInWithFieldInitializer() { await TestInClassAsync( -@"int field = 1; + """ + int field = 1; -void M() -{ - int f = field $$; -}"); + void M() + { + int f = field $$; + } + """); } [Fact] public async Task TestOperatorBuiltIn() { await TestInMethodAsync( -@"int x; + """ + int x; -x = x$$+1;", + x = x$$+1; + """, MainDescription("int int.operator +(int left, int right)")); } @@ -1045,9 +1150,11 @@ await TestInMethodAsync( public async Task TestOperatorBuiltIn1() { await TestInMethodAsync( -@"int x; + """ + int x; -x = x$$ + 1;", + x = x$$ + 1; + """, MainDescription($"({FeaturesResources.local_variable}) int x")); } @@ -1055,9 +1162,11 @@ await TestInMethodAsync( public async Task TestOperatorBuiltIn2() { await TestInMethodAsync( -@"int x; + """ + int x; -x = x+$$x;", + x = x+$$x; + """, MainDescription($"({FeaturesResources.local_variable}) int x")); } @@ -1065,9 +1174,11 @@ await TestInMethodAsync( public async Task TestOperatorBuiltIn3() { await TestInMethodAsync( -@"int x; + """ + int x; -x = x +$$ x;", + x = x +$$ x; + """, MainDescription("int int.operator +(int left, int right)")); } @@ -1075,9 +1186,11 @@ await TestInMethodAsync( public async Task TestOperatorBuiltIn4() { await TestInMethodAsync( -@"int x; + """ + int x; -x = x + $$x;", + x = x + $$x; + """, MainDescription($"({FeaturesResources.local_variable}) int x")); } @@ -1085,9 +1198,11 @@ await TestInMethodAsync( public async Task TestOperatorBuiltIn5() { await TestInMethodAsync( -@"int x; + """ + int x; -x = unchecked (x$$+1);", + x = unchecked (x$$+1); + """, MainDescription("int int.operator +(int left, int right)")); } @@ -1095,9 +1210,11 @@ await TestInMethodAsync( public async Task TestOperatorBuiltIn6() { await TestInMethodAsync( -@"int x; + """ + int x; -x = checked (x$$+1);", + x = checked (x$$+1); + """, MainDescription("int int.operator checked +(int left, int right)")); } @@ -1105,9 +1222,11 @@ await TestInMethodAsync( public async Task TestOperatorBuiltIn7() { await TestInMethodAsync( -@"int x; + """ + int x; -x = unchecked (x +$$ x);", + x = unchecked (x +$$ x); + """, MainDescription("int int.operator +(int left, int right)")); } @@ -1115,9 +1234,11 @@ await TestInMethodAsync( public async Task TestOperatorBuiltIn8() { await TestInMethodAsync( -@"int x; + """ + int x; -x = checked (x +$$ x);", + x = checked (x +$$ x); + """, MainDescription("int int.operator checked +(int left, int right)")); } @@ -1125,9 +1246,11 @@ await TestInMethodAsync( public async Task TestOperatorBuiltIn9() { await TestInMethodAsync( -@"int x; + """ + int x; -x = $$-x;", + x = $$-x; + """, MainDescription("int int.operator -(int value)")); } @@ -1135,9 +1258,11 @@ await TestInMethodAsync( public async Task TestOperatorBuiltIn10() { await TestInMethodAsync( -@"int x; + """ + int x; -x = unchecked ($$-x);", + x = unchecked ($$-x); + """, MainDescription("int int.operator -(int value)")); } @@ -1145,9 +1270,11 @@ await TestInMethodAsync( public async Task TestOperatorBuiltIn11() { await TestInMethodAsync( -@"int x; + """ + int x; -x = checked ($$-x);", + x = checked ($$-x); + """, MainDescription("int int.operator checked -(int value)")); } @@ -1155,9 +1282,11 @@ await TestInMethodAsync( public async Task TestOperatorBuiltIn12() { await TestInMethodAsync( -@"int x; + """ + int x; -x = x >>>$$ x;", + x = x >>>$$ x; + """, MainDescription("int int.operator >>>(int left, int right)")); } @@ -1165,9 +1294,11 @@ await TestInMethodAsync( public async Task TestOperatorBuiltIn13() { await TestInMethodAsync( -@"int x; + """ + int x; -x >>>=$$ x;", + x >>>=$$ x; + """, MainDescription("int int.operator >>>(int left, int right)")); } @@ -1175,10 +1306,12 @@ await TestInMethodAsync( public async Task TestOperatorCustomTypeBuiltIn_01() { var markup = -@"class C -{ - static void M() { C c; c = c +$$ c; } -}"; + """ + class C + { + static void M() { C c; c = c +$$ c; } + } + """; await TestAsync(markup); } @@ -1187,10 +1320,12 @@ public async Task TestOperatorCustomTypeBuiltIn_01() public async Task TestOperatorCustomTypeBuiltIn_02() { var markup = -@"class C -{ - static void M() { C c; c = c >>>$$ c; } -}"; + """ + class C + { + static void M() { C c; c = c >>>$$ c; } + } + """; await TestAsync(markup); } @@ -1199,11 +1334,13 @@ public async Task TestOperatorCustomTypeBuiltIn_02() public async Task TestOperatorCustomTypeOverload_01() { var markup = -@"class C -{ - static void M() { C c; c = c +$$ c; } - static C operator+(C a, C b) { return a; } -}"; + """ + class C + { + static void M() { C c; c = c +$$ c; } + static C operator+(C a, C b) { return a; } + } + """; await TestAsync(markup, MainDescription("C C.operator +(C a, C b)")); @@ -1213,11 +1350,13 @@ await TestAsync(markup, public async Task TestOperatorCustomTypeOverload_02() { var markup = -@"class C -{ - static void M() { C c; c = unchecked (c +$$ c); } - static C operator+(C a, C b) { return a; } -}"; + """ + class C + { + static void M() { C c; c = unchecked (c +$$ c); } + static C operator+(C a, C b) { return a; } + } + """; await TestAsync(markup, MainDescription("C C.operator +(C a, C b)")); @@ -1227,12 +1366,14 @@ await TestAsync(markup, public async Task TestOperatorCustomTypeOverload_03() { var markup = -@"class C -{ - static void M() { C c; c = unchecked (c +$$ c); } - static C operator+(C a, C b) { return a; } - static C operator checked +(C a, C b) { return a; } -}"; + """ + class C + { + static void M() { C c; c = unchecked (c +$$ c); } + static C operator+(C a, C b) { return a; } + static C operator checked +(C a, C b) { return a; } + } + """; await TestAsync(markup, MainDescription("C C.operator +(C a, C b)")); @@ -1242,12 +1383,14 @@ await TestAsync(markup, public async Task TestOperatorCustomTypeOverload_04() { var markup = -@"class C -{ - static void M() { C c; c = checked (c +$$ c); } - static C operator+(C a, C b) { return a; } - static C operator checked +(C a, C b) { return a; } -}"; + """ + class C + { + static void M() { C c; c = checked (c +$$ c); } + static C operator+(C a, C b) { return a; } + static C operator checked +(C a, C b) { return a; } + } + """; await TestAsync(markup, MainDescription("C C.operator checked +(C a, C b)")); @@ -1257,11 +1400,13 @@ await TestAsync(markup, public async Task TestOperatorCustomTypeOverload_05() { var markup = -@"class C -{ - static void M() { C c; c = $$-c; } - static C operator-(C a) { return a; } -}"; + """ + class C + { + static void M() { C c; c = $$-c; } + static C operator-(C a) { return a; } + } + """; await TestAsync(markup, MainDescription("C C.operator -(C a)")); @@ -1271,11 +1416,13 @@ await TestAsync(markup, public async Task TestOperatorCustomTypeOverload_06() { var markup = -@"class C -{ - static void M() { C c; c = unchecked ($$-c); } - static C operator-(C a) { return a; } -}"; + """ + class C + { + static void M() { C c; c = unchecked ($$-c); } + static C operator-(C a) { return a; } + } + """; await TestAsync(markup, MainDescription("C C.operator -(C a)")); @@ -1285,12 +1432,14 @@ await TestAsync(markup, public async Task TestOperatorCustomTypeOverload_07() { var markup = -@"class C -{ - static void M() { C c; c = unchecked ($$-c); } - static C operator-(C a) { return a; } - static C operator checked -(C a) { return a; } -}"; + """ + class C + { + static void M() { C c; c = unchecked ($$-c); } + static C operator-(C a) { return a; } + static C operator checked -(C a) { return a; } + } + """; await TestAsync(markup, MainDescription("C C.operator -(C a)")); @@ -1300,12 +1449,14 @@ await TestAsync(markup, public async Task TestOperatorCustomTypeOverload_08() { var markup = -@"class C -{ - static void M() { C c; c = checked ($$-c); } - static C operator-(C a) { return a; } - static C operator checked -(C a) { return a; } -}"; + """ + class C + { + static void M() { C c; c = checked ($$-c); } + static C operator-(C a) { return a; } + static C operator checked -(C a) { return a; } + } + """; await TestAsync(markup, MainDescription("C C.operator checked -(C a)")); @@ -1315,11 +1466,13 @@ await TestAsync(markup, public async Task TestOperatorCustomTypeOverload_09() { var markup = -@"class C -{ - static void M() { C c; c = c >>>$$ c; } - static C operator>>>(C a, C b) { return a; } -}"; + """ + class C + { + static void M() { C c; c = c >>>$$ c; } + static C operator>>>(C a, C b) { return a; } + } + """; await TestAsync(markup, MainDescription("C C.operator >>>(C a, C b)")); @@ -1329,11 +1482,13 @@ await TestAsync(markup, public async Task TestOperatorCustomTypeOverload_10() { var markup = -@"class C -{ - static void M() { C c; c >>>=$$ c; } - static C operator>>>(C a, C b) { return a; } -}"; + """ + class C + { + static void M() { C c; c >>>=$$ c; } + static C operator>>>(C a, C b) { return a; } + } + """; await TestAsync(markup, MainDescription("C C.operator >>>(C a, C b)")); @@ -1343,12 +1498,14 @@ await TestAsync(markup, public async Task TestFieldInMethodMinimal() { var markup = -@"DateTime field; + """ + DateTime field; -void M() -{ - field$$ -}"; + void M() + { + field$$ + } + """; await TestInClassAsync(markup, MainDescription($"({FeaturesResources.field}) DateTime C.field")); @@ -1358,12 +1515,14 @@ await TestInClassAsync(markup, public async Task TestFieldInMethodQualified() { var markup = -@"System.IO.FileInfo file; + """ + System.IO.FileInfo file; -void M() -{ - file$$ -}"; + void M() + { + file$$ + } + """; await TestInClassAsync(markup, MainDescription($"({FeaturesResources.field}) System.IO.FileInfo C.file")); @@ -1373,9 +1532,11 @@ await TestInClassAsync(markup, public async Task TestMemberOfStructFromSource() { var markup = -@"struct MyStruct { -public static int SomeField; } -static class Test { int a = MyStruct.Some$$Field; }"; + """ + struct MyStruct { + public static int SomeField; } + static class Test { int a = MyStruct.Some$$Field; } + """; await TestAsync(markup, MainDescription($"({FeaturesResources.field}) static int MyStruct.SomeField")); @@ -1385,10 +1546,12 @@ await TestAsync(markup, public async Task TestMemberOfStructFromSourceWithDocComment() { var markup = -@"struct MyStruct { -///My Field -public static int SomeField; } -static class Test { int a = MyStruct.Some$$Field; }"; + """ + struct MyStruct { + ///My Field + public static int SomeField; } + static class Test { int a = MyStruct.Some$$Field; } + """; await TestAsync(markup, MainDescription($"({FeaturesResources.field}) static int MyStruct.SomeField"), @@ -1399,9 +1562,11 @@ await TestAsync(markup, public async Task TestMemberOfStructInsideMethodFromSource() { var markup = -@"struct MyStruct { -public static int SomeField; } -static class Test { static void Method() { int a = MyStruct.Some$$Field; } }"; + """ + struct MyStruct { + public static int SomeField; } + static class Test { static void Method() { int a = MyStruct.Some$$Field; } } + """; await TestAsync(markup, MainDescription($"({FeaturesResources.field}) static int MyStruct.SomeField")); @@ -1411,10 +1576,12 @@ await TestAsync(markup, public async Task TestMemberOfStructInsideMethodFromSourceWithDocComment() { var markup = -@"struct MyStruct { -///My Field -public static int SomeField; } -static class Test { static void Method() { int a = MyStruct.Some$$Field; } }"; + """ + struct MyStruct { + ///My Field + public static int SomeField; } + static class Test { static void Method() { int a = MyStruct.Some$$Field; } } + """; await TestAsync(markup, MainDescription($"({FeaturesResources.field}) static int MyStruct.SomeField"), @@ -1425,17 +1592,19 @@ await TestAsync(markup, public async Task TestPartialMethodDocComment_01() { var markup = -@"partial class MyClass -{ - ///My Method Definition - public partial void MyMethod(); + """ + partial class MyClass + { + ///My Method Definition + public partial void MyMethod(); - ///My Method Implementation - public partial void MyMethod() - { - } -} -static class Test { static void Method() { MyClass.My$$Method(); } }"; + ///My Method Implementation + public partial void MyMethod() + { + } + } + static class Test { static void Method() { MyClass.My$$Method(); } } + """; await TestAsync(markup, MainDescription($"void MyClass.MyMethod()"), @@ -1446,16 +1615,18 @@ await TestAsync(markup, public async Task TestPartialMethodDocComment_02() { var markup = -@"partial class MyClass -{ - ///My Method Definition - public partial void MyMethod(); + """ + partial class MyClass + { + ///My Method Definition + public partial void MyMethod(); - public partial void MyMethod() - { - } -} -static class Test { static void Method() { MyClass.My$$Method(); } }"; + public partial void MyMethod() + { + } + } + static class Test { static void Method() { MyClass.My$$Method(); } } + """; await TestAsync(markup, MainDescription($"void MyClass.MyMethod()"), @@ -1466,16 +1637,18 @@ await TestAsync(markup, public async Task TestPartialMethodDocComment_03() { var markup = -@"partial class MyClass -{ - public partial void MyMethod(); + """ + partial class MyClass + { + public partial void MyMethod(); - ///My Method Implementation - public partial void MyMethod() - { - } -} -static class Test { static void Method() { MyClass.My$$Method(); } }"; + ///My Method Implementation + public partial void MyMethod() + { + } + } + static class Test { static void Method() { MyClass.My$$Method(); } } + """; await TestAsync(markup, MainDescription($"void MyClass.MyMethod()"), @@ -1486,12 +1659,14 @@ await TestAsync(markup, public async Task TestPartialMethodDocComment_04() { var markup = -@"partial class MyClass -{ - ///My Method Definition - public partial void MyMethod(); -} -static class Test { static void Method() { MyClass.My$$Method(); } }"; + """ + partial class MyClass + { + ///My Method Definition + public partial void MyMethod(); + } + static class Test { static void Method() { MyClass.My$$Method(); } } + """; await TestAsync(markup, MainDescription($"void MyClass.MyMethod()"), @@ -1502,12 +1677,14 @@ await TestAsync(markup, public async Task TestPartialMethodDocComment_05() { var markup = -@"partial class MyClass -{ - ///My Method Implementation - public partial void MyMethod() { } -} -static class Test { static void Method() { MyClass.My$$Method(); } }"; + """ + partial class MyClass + { + ///My Method Implementation + public partial void MyMethod() { } + } + static class Test { static void Method() { MyClass.My$$Method(); } } + """; await TestAsync(markup, MainDescription($"void MyClass.MyMethod()"), @@ -1518,14 +1695,16 @@ await TestAsync(markup, public async Task TestPartialMethodDocComment_06() { var markup = -@"partial class MyClass -{ - ///My Method Definition - partial void MyMethod(); + """ + partial class MyClass + { + ///My Method Definition + partial void MyMethod(); - partial void MyMethod() { } -} -static class Test { static void Method() { MyClass.My$$Method(); } }"; + partial void MyMethod() { } + } + static class Test { static void Method() { MyClass.My$$Method(); } } + """; await TestAsync(markup, MainDescription($"void MyClass.MyMethod()"), @@ -1544,12 +1723,14 @@ public async Task TestMetadataFieldQualified1() { // NOTE: we qualify the field type, but not the type that contains the field in Dev10 var markup = -@"class C { - void M() - { - DateTime dt = System.DateTime.MaxValue$$ - } -}"; + """ + class C { + void M() + { + DateTime dt = System.DateTime.MaxValue$$ + } + } + """; await TestAsync(markup, MainDescription($"({FeaturesResources.field}) static readonly System.DateTime System.DateTime.MaxValue")); } @@ -1558,13 +1739,15 @@ await TestAsync(markup, public async Task TestMetadataFieldQualified2() { await TestAsync( -@"class C -{ - void M() - { - DateTime dt = System.DateTime.MaxValue$$ - } -}", + """ + class C + { + void M() + { + DateTime dt = System.DateTime.MaxValue$$ + } + } + """, MainDescription($"({FeaturesResources.field}) static readonly System.DateTime System.DateTime.MaxValue")); } @@ -1572,15 +1755,17 @@ void M() public async Task TestMetadataFieldQualified3() { await TestAsync( -@"using System; + """ + using System; -class C -{ - void M() - { - DateTime dt = System.DateTime.MaxValue$$ - } -}", + class C + { + void M() + { + DateTime dt = System.DateTime.MaxValue$$ + } + } + """, MainDescription($"({FeaturesResources.field}) static readonly DateTime DateTime.MaxValue")); } @@ -1588,34 +1773,38 @@ void M() public async Task ConstructedGenericField() { await TestAsync( -@"class C -{ - public T Field; -} - -class D -{ - void M() - { - new C().Fi$$eld.ToString(); - } -}", - MainDescription($"({FeaturesResources.field}) int C.Field")); - } + """ + class C + { + public T Field; + } - [Fact] - public async Task UnconstructedGenericField() + class D + { + void M() + { + new C().Fi$$eld.ToString(); + } + } + """, + MainDescription($"({FeaturesResources.field}) int C.Field")); + } + + [Fact] + public async Task UnconstructedGenericField() { await TestAsync( -@"class C -{ - public T Field; + """ + class C + { + public T Field; - void M() - { - Fi$$eld.ToString(); - } -}", + void M() + { + Fi$$eld.ToString(); + } + } + """, MainDescription($"({FeaturesResources.field}) T C.Field")); } @@ -1661,185 +1850,209 @@ await TestInMethodAsync(@"string f = default$$", [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/756226")] public async Task TestAwaitKeywordOnGenericTaskReturningAsync() { - var markup = @"using System.Threading.Tasks; -class C -{ - public async Task Calc() - { - aw$$ait Calc(); - return 5; - } -}"; + var markup = """ + using System.Threading.Tasks; + class C + { + public async Task Calc() + { + aw$$ait Calc(); + return 5; + } + } + """; await TestAsync(markup, MainDescription(string.Format(FeaturesResources.Awaited_task_returns_0, "struct System.Int32"))); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/756226")] public async Task TestAwaitKeywordInDeclarationStatement() { - var markup = @"using System.Threading.Tasks; -class C -{ - public async Task Calc() - { - var x = $$await Calc(); - return 5; - } -}"; + var markup = """ + using System.Threading.Tasks; + class C + { + public async Task Calc() + { + var x = $$await Calc(); + return 5; + } + } + """; await TestAsync(markup, MainDescription(string.Format(FeaturesResources.Awaited_task_returns_0, "struct System.Int32"))); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/756226")] public async Task TestAwaitKeywordOnTaskReturningAsync() { - var markup = @"using System.Threading.Tasks; -class C -{ - public async void Calc() - { - aw$$ait Task.Delay(100); - } -}"; + var markup = """ + using System.Threading.Tasks; + class C + { + public async void Calc() + { + aw$$ait Task.Delay(100); + } + } + """; await TestAsync(markup, MainDescription(FeaturesResources.Awaited_task_returns_no_value)); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/756226"), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/756337")] public async Task TestNestedAwaitKeywords1() { - var markup = @"using System; -using System.Threading.Tasks; -class AsyncExample2 -{ - async Task> AsyncMethod() - { - return NewMethod(); - } + var markup = """ + using System; + using System.Threading.Tasks; + class AsyncExample2 + { + async Task> AsyncMethod() + { + return NewMethod(); + } - private static Task NewMethod() - { - int hours = 24; - return hours; - } + private static Task NewMethod() + { + int hours = 24; + return hours; + } - async Task UseAsync() - { - Func> lambda = async () => - { - return await await AsyncMethod(); - }; + async Task UseAsync() + { + Func> lambda = async () => + { + return await await AsyncMethod(); + }; - int result = await await AsyncMethod(); - Task> resultTask = AsyncMethod(); - result = await awa$$it resultTask; - result = await lambda(); - } -}"; + int result = await await AsyncMethod(); + Task> resultTask = AsyncMethod(); + result = await awa$$it resultTask; + result = await lambda(); + } + } + """; await TestAsync(markup, MainDescription(string.Format(FeaturesResources.Awaited_task_returns_0, $"({CSharpFeaturesResources.awaitable}) class System.Threading.Tasks.Task")), - TypeParameterMap($"\r\nTResult {FeaturesResources.is_} int")); + TypeParameterMap($""" + + TResult {FeaturesResources.is_} int + """)); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/756226")] public async Task TestNestedAwaitKeywords2() { - var markup = @"using System; -using System.Threading.Tasks; -class AsyncExample2 -{ - async Task> AsyncMethod() - { - return NewMethod(); - } + var markup = """ + using System; + using System.Threading.Tasks; + class AsyncExample2 + { + async Task> AsyncMethod() + { + return NewMethod(); + } - private static Task NewMethod() - { - int hours = 24; - return hours; - } + private static Task NewMethod() + { + int hours = 24; + return hours; + } - async Task UseAsync() - { - Func> lambda = async () => - { - return await await AsyncMethod(); - }; + async Task UseAsync() + { + Func> lambda = async () => + { + return await await AsyncMethod(); + }; - int result = await await AsyncMethod(); - Task> resultTask = AsyncMethod(); - result = awa$$it await resultTask; - result = await lambda(); - } -}"; + int result = await await AsyncMethod(); + Task> resultTask = AsyncMethod(); + result = awa$$it await resultTask; + result = await lambda(); + } + } + """; await TestAsync(markup, MainDescription(string.Format(FeaturesResources.Awaited_task_returns_0, "struct System.Int32"))); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/756226"), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/756337")] public async Task TestAwaitablePrefixOnCustomAwaiter() { - var markup = @"using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using Z = $$C; + var markup = """ + using System; + using System.Runtime.CompilerServices; + using System.Threading.Tasks; + using Z = $$C; -class C -{ - public MyAwaiter GetAwaiter() { throw new NotImplementedException(); } -} + class C + { + public MyAwaiter GetAwaiter() { throw new NotImplementedException(); } + } -class MyAwaiter : INotifyCompletion -{ - public void OnCompleted(Action continuation) - { - throw new NotImplementedException(); - } + class MyAwaiter : INotifyCompletion + { + public void OnCompleted(Action continuation) + { + throw new NotImplementedException(); + } - public bool IsCompleted { get { throw new NotImplementedException(); } } - public void GetResult() { } -}"; + public bool IsCompleted { get { throw new NotImplementedException(); } } + public void GetResult() { } + } + """; await TestAsync(markup, MainDescription($"({CSharpFeaturesResources.awaitable}) class C")); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/756226"), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/756337")] public async Task TestTaskType() { - var markup = @"using System.Threading.Tasks; -class C -{ - public void Calc() - { - Task$$ v1; - } -}"; + var markup = """ + using System.Threading.Tasks; + class C + { + public void Calc() + { + Task$$ v1; + } + } + """; await TestAsync(markup, MainDescription($"({CSharpFeaturesResources.awaitable}) class System.Threading.Tasks.Task")); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/756226"), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/756337")] public async Task TestTaskOfTType() { - var markup = @"using System; -using System.Threading.Tasks; -class C -{ - public void Calc() - { - Task$$ v1; - } -}"; + var markup = """ + using System; + using System.Threading.Tasks; + class C + { + public void Calc() + { + Task$$ v1; + } + } + """; await TestAsync(markup, MainDescription($"({CSharpFeaturesResources.awaitable}) class System.Threading.Tasks.Task"), - TypeParameterMap($"\r\nTResult {FeaturesResources.is_} int")); + TypeParameterMap($""" + + TResult {FeaturesResources.is_} int + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7100")] public async Task TestDynamicIsntAwaitable() { - var markup = @" -class C -{ - dynamic D() { return null; } - void M() - { - D$$(); - } -} -"; + var markup = """ + + class C + { + dynamic D() { return null; } + void M() + { + D$$(); + } + } + + """; await TestAsync(markup, MainDescription("dynamic C.D()")); } @@ -1856,7 +2069,10 @@ public async Task TestStringLiteralUtf8_01() await TestInMethodAsync(@"var f = ""Goo""u8$$", TestSources.Span, MainDescription("readonly ref struct System.ReadOnlySpan"), - TypeParameterMap($"\r\nT {FeaturesResources.is_} byte")); + TypeParameterMap($""" + + T {FeaturesResources.is_} byte + """)); } [Fact] @@ -1865,7 +2081,10 @@ public async Task TestStringLiteralUtf8_02() await TestInMethodAsync(@"var f = ""Goo""U8$$", TestSources.Span, MainDescription("readonly ref struct System.ReadOnlySpan"), - TypeParameterMap($"\r\nT {FeaturesResources.is_} byte")); + TypeParameterMap($""" + + T {FeaturesResources.is_} byte + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/1280")] @@ -1881,7 +2100,10 @@ public async Task TestVerbatimStringLiteralUtf8_01() await TestInMethodAsync(@"string f = @""cat""u8$$", TestSources.Span, MainDescription("readonly ref struct System.ReadOnlySpan"), - TypeParameterMap($"\r\nT {FeaturesResources.is_} byte")); + TypeParameterMap($""" + + T {FeaturesResources.is_} byte + """)); } [Fact] @@ -1890,7 +2112,10 @@ public async Task TestVerbatimStringLiteralUtf8_02() await TestInMethodAsync(@"string f = @""cat""U8$$", TestSources.Span, MainDescription("readonly ref struct System.ReadOnlySpan"), - TypeParameterMap($"\r\nT {FeaturesResources.is_} byte")); + TypeParameterMap($""" + + T {FeaturesResources.is_} byte + """)); } [Fact] @@ -1906,7 +2131,10 @@ public async Task TestRawStringLiteralUtf8_01() await TestInMethodAsync(@"string f = """"""Goo""""""u8$$", TestSources.Span, MainDescription("readonly ref struct System.ReadOnlySpan"), - TypeParameterMap($"\r\nT {FeaturesResources.is_} byte")); + TypeParameterMap($""" + + T {FeaturesResources.is_} byte + """)); } [Fact] @@ -1915,56 +2143,83 @@ public async Task TestRawStringLiteralUtf8_02() await TestInMethodAsync(@"string f = """"""Goo""""""U8$$", TestSources.Span, MainDescription("readonly ref struct System.ReadOnlySpan"), - TypeParameterMap($"\r\nT {FeaturesResources.is_} byte")); + TypeParameterMap($""" + + T {FeaturesResources.is_} byte + """)); } [Fact] public async Task TestRawStringLiteralMultiline() { - await TestInMethodAsync(@"string f = """""" - Goo - """"""$$", + await TestInMethodAsync("""" + string f = """ + Goo + """$$ + """", MainDescription("class System.String")); } [Fact] public async Task TestRawStringLiteralMultilineUtf8_01() { - await TestInMethodAsync(@"string f = """""" - Goo - """"""u8$$", + await TestInMethodAsync("""" + string f = """ + Goo + """u8$$ + """", TestSources.Span, MainDescription("readonly ref struct System.ReadOnlySpan"), - TypeParameterMap($"\r\nT {FeaturesResources.is_} byte")); + TypeParameterMap($""" + + T {FeaturesResources.is_} byte + """)); } [Fact] public async Task TestRawStringLiteralMultilineUtf8_02() { - await TestInMethodAsync(@"string f = """""" - Goo - """"""U8$$", + await TestInMethodAsync("""" + string f = """ + Goo + """U8$$ + """", TestSources.Span, MainDescription("readonly ref struct System.ReadOnlySpan"), - TypeParameterMap($"\r\nT {FeaturesResources.is_} byte")); + TypeParameterMap($""" + + T {FeaturesResources.is_} byte + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/1280")] public async Task TestInterpolatedStringLiteral() { await TestInMethodAsync(@"string f = $""cat""$$", MainDescription("class System.String")); - await TestInMethodAsync(@"string f = $""c$$at""", MainDescription("class System.String")); - await TestInMethodAsync(@"string f = $""$$cat""", MainDescription("class System.String")); - await TestInMethodAsync(@"string f = $""cat {1$$ + 2} dog""", MainDescription("struct System.Int32")); + await TestInMethodAsync(""" + string f = $"c$$at" + """, MainDescription("class System.String")); + await TestInMethodAsync(""" + string f = $"$$cat" + """, MainDescription("class System.String")); + await TestInMethodAsync(""" + string f = $"cat {1$$ + 2} dog" + """, MainDescription("struct System.Int32")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/1280")] public async Task TestVerbatimInterpolatedStringLiteral() { await TestInMethodAsync(@"string f = $@""cat""$$", MainDescription("class System.String")); - await TestInMethodAsync(@"string f = $@""c$$at""", MainDescription("class System.String")); - await TestInMethodAsync(@"string f = $@""$$cat""", MainDescription("class System.String")); - await TestInMethodAsync(@"string f = $@""cat {1$$ + 2} dog""", MainDescription("struct System.Int32")); + await TestInMethodAsync(""" + string f = $@"c$$at" + """, MainDescription("class System.String")); + await TestInMethodAsync(""" + string f = $@"$$cat" + """, MainDescription("class System.String")); + await TestInMethodAsync(""" + string f = $@"cat {1$$ + 2} dog" + """, MainDescription("struct System.Int32")); } [Fact] @@ -1978,7 +2233,7 @@ await TestInMethodAsync(@"string f = 'x'$$", public async Task DynamicKeyword() { await TestInMethodAsync( -@"dyn$$amic dyn;", + @"dyn$$amic dyn;", MainDescription("dynamic"), Documentation(FeaturesResources.Represents_an_object_whose_operations_will_be_resolved_at_runtime)); } @@ -1987,12 +2242,14 @@ await TestInMethodAsync( public async Task DynamicField() { await TestInClassAsync( -@"dynamic dyn; + """ + dynamic dyn; -void M() -{ - d$$yn.Goo(); -}", + void M() + { + d$$yn.Goo(); + } + """, MainDescription($"({FeaturesResources.field}) dynamic C.dyn")); } @@ -2000,12 +2257,14 @@ void M() public async Task LocalProperty_Minimal() { await TestInClassAsync( -@"DateTime Prop { get; set; } + """ + DateTime Prop { get; set; } -void M() -{ - P$$rop.ToString(); -}", + void M() + { + P$$rop.ToString(); + } + """, MainDescription("DateTime C.Prop { get; set; }")); } @@ -2013,12 +2272,14 @@ void M() public async Task LocalProperty_Minimal_PrivateSet() { await TestInClassAsync( -@"public DateTime Prop { get; private set; } + """ + public DateTime Prop { get; private set; } -void M() -{ - P$$rop.ToString(); -}", + void M() + { + P$$rop.ToString(); + } + """, MainDescription("DateTime C.Prop { get; private set; }")); } @@ -2026,12 +2287,14 @@ void M() public async Task LocalProperty_Minimal_PrivateSet1() { await TestInClassAsync( -@"protected internal int Prop { get; private set; } + """ + protected internal int Prop { get; private set; } -void M() -{ - P$$rop.ToString(); -}", + void M() + { + P$$rop.ToString(); + } + """, MainDescription("int C.Prop { get; private set; }")); } @@ -2039,12 +2302,14 @@ void M() public async Task LocalProperty_Qualified() { await TestInClassAsync( -@"System.IO.FileInfo Prop { get; set; } + """ + System.IO.FileInfo Prop { get; set; } -void M() -{ - P$$rop.ToString(); -}", + void M() + { + P$$rop.ToString(); + } + """, MainDescription("System.IO.FileInfo C.Prop { get; set; }")); } @@ -2059,9 +2324,11 @@ public async Task NonLocalProperty_Minimal() public async Task NonLocalProperty_Qualified() { await TestInMethodAsync( -@"System.IO.FileInfo f; + """ + System.IO.FileInfo f; -f.Att$$ributes.ToString();", + f.Att$$ributes.ToString(); + """, MainDescription("System.IO.FileAttributes System.IO.FileSystemInfo.Attributes { get; set; }")); } @@ -2069,18 +2336,20 @@ await TestInMethodAsync( public async Task ConstructedGenericProperty() { await TestAsync( -@"class C -{ - public T Property { get; set } -} + """ + class C + { + public T Property { get; set } + } -class D -{ - void M() - { - new C().Pro$$perty.ToString(); - } -}", + class D + { + void M() + { + new C().Pro$$perty.ToString(); + } + } + """, MainDescription("int C.Property { get; set; }")); } @@ -2088,15 +2357,17 @@ void M() public async Task UnconstructedGenericProperty() { await TestAsync( -@"class C -{ - public T Property { get; set} + """ + class C + { + public T Property { get; set} - void M() - { - Pro$$perty.ToString(); - } -}", + void M() + { + Pro$$perty.ToString(); + } + } + """, MainDescription("T C.Property { get; set; }")); } @@ -2104,13 +2375,15 @@ void M() public async Task ValueInProperty() { await TestInClassAsync( -@"public DateTime Property -{ - set - { - goo = val$$ue; - } -}", + """ + public DateTime Property + { + set + { + goo = val$$ue; + } + } + """, MainDescription($"({FeaturesResources.parameter}) DateTime value")); } @@ -2131,100 +2404,116 @@ await TestInClassAsync(@"enum E$$ : byte { A, B }", [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52490")] public async Task EnumNonDefaultUnderlyingType_AsField() { - await TestInClassAsync(@" -enum E : byte { A, B } + await TestInClassAsync(""" -private E$$ _E; -", + enum E : byte { A, B } + + private E$$ _E; + + """, MainDescription("enum C.E : byte")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52490")] public async Task EnumNonDefaultUnderlyingType_AsProperty() { - await TestInClassAsync(@" -enum E : byte { A, B } + await TestInClassAsync(""" + + enum E : byte { A, B } -private E$$ E{ get; set; }; -", + private E$$ E{ get; set; }; + + """, MainDescription("enum C.E : byte")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52490")] public async Task EnumNonDefaultUnderlyingType_AsParameter() { - await TestInClassAsync(@" -enum E : byte { A, B } + await TestInClassAsync(""" + + enum E : byte { A, B } + + private void M(E$$ e) { } -private void M(E$$ e) { } -", + """, MainDescription("enum C.E : byte")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52490")] public async Task EnumNonDefaultUnderlyingType_AsReturnType() { - await TestInClassAsync(@" -enum E : byte { A, B } + await TestInClassAsync(""" + + enum E : byte { A, B } -private E$$ M() { } -", + private E$$ M() { } + + """, MainDescription("enum C.E : byte")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52490")] public async Task EnumNonDefaultUnderlyingType_AsLocal() { - await TestInClassAsync(@" -enum E : byte { A, B } + await TestInClassAsync(""" -private void M() -{ - E$$ e = default; -} -", + enum E : byte { A, B } + + private void M() + { + E$$ e = default; + } + + """, MainDescription("enum C.E : byte")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52490")] public async Task EnumNonDefaultUnderlyingType_OnMemberAccessOnType() { - await TestInClassAsync(@" -enum EN : byte { A, B } + await TestInClassAsync(""" -private void M() -{ - var ea = E$$N.A; -} -", + enum EN : byte { A, B } + + private void M() + { + var ea = E$$N.A; + } + + """, MainDescription("enum C.EN : byte")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52490")] public async Task EnumNonDefaultUnderlyingType_OnMemberAccessOnType_OnDot() { - await TestInClassAsync(@" -enum E : byte { A, B } + await TestInClassAsync(""" -private void M() -{ - var ea = E$$.A; -} -", + enum E : byte { A, B } + + private void M() + { + var ea = E$$.A; + } + + """, MainDescription("E.A = 0")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52490")] public async Task EnumNonDefaultUnderlyingType_NotOnMemberAccessOnMember() { - await TestInClassAsync(@" -enum E : byte { A, B } + await TestInClassAsync(""" -private void M() -{ - var ea = E.A$$; -} -", + enum E : byte { A, B } + + private void M() + { + var ea = E.A$$; + } + + """, MainDescription("E.A = 0")); } @@ -2246,11 +2535,13 @@ private void M() [InlineData("ulong", "System.UInt64")] public async Task EnumNonDefaultUnderlyingType_ShowForNonDefaultTypes(string displayTypeName, string underlyingTypeName) { - await TestInClassAsync(@$" -enum E$$ : {underlyingTypeName} -{{ - A, B -}}", + await TestInClassAsync($$""" + + enum E$$ : {{underlyingTypeName}} + { + A, B + } + """, MainDescription($"enum C.E : {displayTypeName}")); } @@ -2260,11 +2551,13 @@ enum E$$ : {underlyingTypeName} [InlineData(": System.Int32")] public async Task EnumNonDefaultUnderlyingType_DoNotShowForDefaultType(string defaultType) { - await TestInClassAsync(@$" -enum E$$ {defaultType} -{{ - A, B -}}", + await TestInClassAsync($$""" + + enum E$$ {{defaultType}} + { + A, B + } + """, MainDescription("enum C.E")); } @@ -2293,20 +2586,22 @@ await TestInMethodAsync(@"AttributeTargets a = AttributeTargets.A$$ll", public async Task EnumMemberNameFromSource1() { await TestAsync( -@"enum E -{ - A = 1 << 0, - B = 1 << 1, - C = 1 << 2 -} + """ + enum E + { + A = 1 << 0, + B = 1 << 1, + C = 1 << 2 + } -class C -{ - void M() - { - var e = E.B$$; - } -}", + class C + { + void M() + { + var e = E.B$$; + } + } + """, MainDescription("E.B = 1 << 1")); } @@ -2314,20 +2609,22 @@ void M() public async Task EnumMemberNameFromSource2() { await TestAsync( -@"enum E -{ - A, - B, - C -} + """ + enum E + { + A, + B, + C + } -class C -{ - void M() - { - var e = E.B$$; - } -}", + class C + { + void M() + { + var e = E.B$$; + } + } + """, MainDescription("E.B = 1")); } @@ -2335,9 +2632,11 @@ void M() public async Task Parameter_InMethod_Minimal() { await TestInClassAsync( -@"void M(DateTime dt) -{ - d$$t.ToString();", + """ + void M(DateTime dt) + { + d$$t.ToString(); + """, MainDescription($"({FeaturesResources.parameter}) DateTime dt")); } @@ -2345,9 +2644,11 @@ await TestInClassAsync( public async Task Parameter_InMethod_Qualified() { await TestInClassAsync( -@"void M(System.IO.FileInfo fileInfo) -{ - file$$Info.ToString();", + """ + void M(System.IO.FileInfo fileInfo) + { + file$$Info.ToString(); + """, MainDescription($"({FeaturesResources.parameter}) System.IO.FileInfo fileInfo")); } @@ -2364,10 +2665,12 @@ public async Task Parameter_DefaultValue() // NOTE: Dev10 doesn't show the default value, but it would be nice if we did. // NOTE: The "DefaultValue" property isn't implemented yet. await TestInClassAsync( -@"void M(int param = 42) -{ - para$$m.ToString(); -}", + """ + void M(int param = 42) + { + para$$m.ToString(); + } + """, MainDescription($"({FeaturesResources.parameter}) int param = 42")); } @@ -2375,9 +2678,11 @@ await TestInClassAsync( public async Task Lambda_Parameter_DefaultValue_01() { await TestInMethodAsync( -@"(int param = 42) => { - return para$$m + 1; -}", + """ + (int param = 42) => { + return para$$m + 1; + } + """, MainDescription($"({FeaturesResources.parameter}) int param = 42")); } @@ -2385,9 +2690,11 @@ await TestInMethodAsync( public async Task Lambda_Parameter_DefaultValue_02() { await TestInMethodAsync( -@"(int param = $$int.MaxValue) => { - return param + 1; -}", + """ + (int param = $$int.MaxValue) => { + return param + 1; + } + """, MainDescription($"{FeaturesResources.struct_} System.Int32")); } @@ -2395,9 +2702,11 @@ await TestInMethodAsync( public async Task Lambda_Parameter_DefaultValue_03() { await TestInMethodAsync( -@"(int param = int.$$MaxValue) => { - return param + 1; -}", + """ + (int param = int.$$MaxValue) => { + return param + 1; + } + """, MainDescription($"({FeaturesResources.constant}) const int int.MaxValue = 2147483647")); } @@ -2405,9 +2714,11 @@ await TestInMethodAsync( public async Task Lambda_Parameter_ParamsArray() { await TestInMethodAsync( -@"(params int[] xs) => { - return x$$s.Length; -}", + """ + (params int[] xs) => { + return x$$s.Length; + } + """, MainDescription($"({FeaturesResources.parameter}) params int[] xs")); } @@ -2415,10 +2726,12 @@ await TestInMethodAsync( public async Task Parameter_Params() { await TestInClassAsync( -@"void M(params DateTime[] arg) -{ - ar$$g.ToString(); -}", + """ + void M(params DateTime[] arg) + { + ar$$g.ToString(); + } + """, MainDescription($"({FeaturesResources.parameter}) params DateTime[] arg")); } @@ -2426,10 +2739,12 @@ await TestInClassAsync( public async Task Parameter_Ref() { await TestInClassAsync( -@"void M(ref DateTime arg) -{ - ar$$g.ToString(); -}", + """ + void M(ref DateTime arg) + { + ar$$g.ToString(); + } + """, MainDescription($"({FeaturesResources.parameter}) ref DateTime arg")); } @@ -2437,10 +2752,12 @@ await TestInClassAsync( public async Task Parameter_Out() { await TestInClassAsync( -@"void M(out DateTime arg) -{ - ar$$g.ToString(); -}", + """ + void M(out DateTime arg) + { + ar$$g.ToString(); + } + """, MainDescription($"({FeaturesResources.parameter}) out DateTime arg")); } @@ -2448,9 +2765,11 @@ await TestInClassAsync( public async Task Local_Minimal() { await TestInMethodAsync( -@"DateTime dt; + """ + DateTime dt; -d$$t.ToString();", + d$$t.ToString(); + """, MainDescription($"({FeaturesResources.local_variable}) DateTime dt")); } @@ -2458,9 +2777,11 @@ await TestInMethodAsync( public async Task Local_Qualified() { await TestInMethodAsync( -@"System.IO.FileInfo fileInfo; + """ + System.IO.FileInfo fileInfo; -file$$Info.ToString();", + file$$Info.ToString(); + """, MainDescription($"({FeaturesResources.local_variable}) System.IO.FileInfo fileInfo")); } @@ -2475,14 +2796,16 @@ public async Task Method_MetadataOverload() public async Task Method_SimpleWithOverload() { await TestInClassAsync( -@"void Method() -{ - Met$$hod(); -} + """ + void Method() + { + Met$$hod(); + } -void Method(int i) -{ -}", + void Method(int i) + { + } + """, MainDescription($"void C.Method() (+ 1 {FeaturesResources.overload})")); } @@ -2490,22 +2813,24 @@ void Method(int i) public async Task Method_MoreOverloads() { await TestInClassAsync( -@"void Method() -{ - Met$$hod(null); -} + """ + void Method() + { + Met$$hod(null); + } -void Method(int i) -{ -} + void Method(int i) + { + } -void Method(DateTime dt) -{ -} + void Method(DateTime dt) + { + } -void Method(System.IO.FileInfo fileInfo) -{ -}", + void Method(System.IO.FileInfo fileInfo) + { + } + """, MainDescription($"void C.Method(System.IO.FileInfo fileInfo) (+ 3 {FeaturesResources.overloads_})")); } @@ -2513,10 +2838,12 @@ void Method(System.IO.FileInfo fileInfo) public async Task Method_SimpleInSameClass() { await TestInClassAsync( -@"DateTime GetDate(System.IO.FileInfo ft) -{ - Get$$Date(null); -}", + """ + DateTime GetDate(System.IO.FileInfo ft) + { + Get$$Date(null); + } + """, MainDescription("DateTime C.GetDate(System.IO.FileInfo ft)")); } @@ -2524,14 +2851,16 @@ await TestInClassAsync( public async Task Method_OptionalParameter() { await TestInClassAsync( -@"void M() -{ - Met$$hod(); -} + """ + void M() + { + Met$$hod(); + } -void Method(int i = 0) -{ -}", + void Method(int i = 0) + { + } + """, MainDescription("void C.Method([int i = 0])")); } @@ -2539,9 +2868,11 @@ void Method(int i = 0) public async Task Method_OptionalDecimalParameter() { await TestInClassAsync( -@"void Goo(decimal x$$yz = 10) -{ -}", + """ + void Goo(decimal x$$yz = 10) + { + } + """, MainDescription($"({FeaturesResources.parameter}) decimal xyz = 10")); } @@ -2551,10 +2882,12 @@ public async Task Method_Generic() // Generic method don't get the instantiation info yet. NOTE: We don't display // constraint info in Dev10. Should we? await TestInClassAsync( -@"TOut Goo(TIn arg) where TIn : IEquatable -{ - Go$$o(37); -}", + """ + TOut Goo(TIn arg) where TIn : IEquatable + { + Go$$o(37); + } + """, MainDescription("DateTime C.Goo(int arg)")); } @@ -2563,10 +2896,12 @@ await TestInClassAsync( public async Task Method_UnconstructedGeneric() { await TestInClassAsync( -@"TOut Goo(TIn arg) -{ - Go$$o(default(TIn); -}", + """ + TOut Goo(TIn arg) + { + Go$$o(default(TIn); + } + """, MainDescription("TOut C.Goo(TIn arg)")); } @@ -2575,10 +2910,12 @@ await TestInClassAsync( public async Task Method_Inferred() { await TestInClassAsync( -@"void Goo(TIn arg) -{ - Go$$o(42); -}", + """ + void Goo(TIn arg) + { + Go$$o(42); + } + """, MainDescription("void C.Goo(int arg)")); } @@ -2586,10 +2923,12 @@ await TestInClassAsync( public async Task Method_MultipleParams() { await TestInClassAsync( -@"void Goo(DateTime dt, System.IO.FileInfo fi, int number) -{ - Go$$o(DateTime.Now, null, 32); -}", + """ + void Goo(DateTime dt, System.IO.FileInfo fi, int number) + { + Go$$o(DateTime.Now, null, 32); + } + """, MainDescription("void C.Goo(DateTime dt, System.IO.FileInfo fi, int number)")); } @@ -2598,10 +2937,12 @@ public async Task Method_OptionalParam() { // NOTE - Default values aren't actually returned by symbols yet. await TestInClassAsync( -@"void Goo(int num = 42) -{ - Go$$o(); -}", + """ + void Goo(int num = 42) + { + Go$$o(); + } + """, MainDescription("void C.Goo([int num = 42])")); } @@ -2610,10 +2951,12 @@ public async Task Method_ParameterModifiers() { // NOTE - Default values aren't actually returned by symbols yet. await TestInClassAsync( -@"void Goo(ref DateTime dt, out System.IO.FileInfo fi, params int[] numbers) -{ - Go$$o(DateTime.Now, null, 32); -}", + """ + void Goo(ref DateTime dt, out System.IO.FileInfo fi, params int[] numbers) + { + Go$$o(DateTime.Now, null, 32); + } + """, MainDescription("void C.Goo(ref DateTime dt, out System.IO.FileInfo fi, params int[] numbers)")); } @@ -2634,14 +2977,16 @@ void Goo(ref readonly DateTime dt, ref readonly System.IO.FileInfo fi, params in public async Task Constructor() { await TestInClassAsync( -@"public C() -{ -} + """ + public C() + { + } -void M() -{ - new C$$().ToString(); -}", + void M() + { + new C$$().ToString(); + } + """, MainDescription("C.C()")); } @@ -2649,22 +2994,24 @@ void M() public async Task Constructor_Overloads() { await TestInClassAsync( -@"public C() -{ -} + """ + public C() + { + } -public C(DateTime dt) -{ -} + public C(DateTime dt) + { + } -public C(int i) -{ -} + public C(int i) + { + } -void M() -{ - new C$$(DateTime.MaxValue).ToString(); -}", + void M() + { + new C$$(DateTime.MaxValue).ToString(); + } + """, MainDescription($"C.C(DateTime dt) (+ 2 {FeaturesResources.overloads_})")); } @@ -2675,7 +3022,7 @@ void M() public async Task Constructor_OverloadFromStringLiteral() { await TestInMethodAsync( -@"new InvalidOperatio$$nException("""");", + @"new InvalidOperatio$$nException("""");", MainDescription($"InvalidOperationException.InvalidOperationException(string message) (+ 2 {FeaturesResources.overloads_})")); } @@ -2686,10 +3033,12 @@ await TestInMethodAsync( public async Task Constructor_UnknownType() { await TestInvalidTypeInClassAsync( -@"void M() -{ - new G$$oo(); -}"); + """ + void M() + { + new G$$oo(); + } + """); } /// @@ -2699,7 +3048,7 @@ await TestInvalidTypeInClassAsync( public async Task Constructor_OverloadFromProperty() { await TestInMethodAsync( -@"new InvalidOperatio$$nException(this.GetType().Name);", + @"new InvalidOperatio$$nException(this.GetType().Name);", MainDescription($"InvalidOperationException.InvalidOperationException(string message) (+ 2 {FeaturesResources.overloads_})")); } @@ -2707,7 +3056,7 @@ await TestInMethodAsync( public async Task Constructor_Metadata() { await TestInMethodAsync( -@"new Argument$$NullException();", + @"new Argument$$NullException();", MainDescription($"ArgumentNullException.ArgumentNullException() (+ 3 {FeaturesResources.overloads_})")); } @@ -2722,10 +3071,12 @@ public async Task Constructor_MetadataQualified() public async Task InterfaceProperty() { await TestInMethodAsync( -@"interface I -{ - string Name$$ { get; set; } -}", + """ + interface I + { + string Name$$ { get; set; } + } + """, MainDescription("string I.Name { get; set; }")); } @@ -2733,25 +3084,27 @@ await TestInMethodAsync( public async Task ExplicitInterfacePropertyImplementation() { await TestInMethodAsync( -@"interface I -{ - string Name { get; set; } -} + """ + interface I + { + string Name { get; set; } + } -class C : I -{ - string IEmployee.Name$$ - { - get - { - return """"; - } + class C : I + { + string IEmployee.Name$$ + { + get + { + return ""; + } - set - { - } - } -}", + set + { + } + } + } + """, MainDescription("string C.Name { get; set; }")); } @@ -2759,15 +3112,17 @@ string IEmployee.Name$$ public async Task Operator() { await TestInClassAsync( -@"public static C operator +(C left, C right) -{ - return null; -} + """ + public static C operator +(C left, C right) + { + return null; + } -void M(C left, C right) -{ - return left +$$ right; -}", + void M(C left, C right) + { + return left +$$ right; + } + """, MainDescription("C C.operator +(C left, C right)")); } @@ -2778,9 +3133,11 @@ void M(C left, C right) public async Task GenericMethodWithConstraintsAtDeclaration() { await TestInClassAsync( -@"TOut G$$oo(TIn arg) where TIn : IEquatable -{ -}", + """ + TOut G$$oo(TIn arg) where TIn : IEquatable + { + } + """, MainDescription("TOut C.Goo(TIn arg) where TIn : IEquatable")); } @@ -2792,13 +3149,15 @@ await TestInClassAsync( public async Task GenericMethodWithMultipleConstraintsAtDeclaration() { await TestInClassAsync( -@"TOut Goo(TIn arg) where TIn : Employee, new() -{ - Go$$o(default(TIn); -}", - - MainDescription("TOut C.Goo(TIn arg) where TIn : Employee, new()")); - } + """ + TOut Goo(TIn arg) where TIn : Employee, new() + { + Go$$o(default(TIn); + } + """, + + MainDescription("TOut C.Goo(TIn arg) where TIn : Employee, new()")); + } #pragma warning disable CA2243 // Attribute string literals should parse correctly [WorkItem(792629, "generic type parameter constraints for methods in quick info")] @@ -2807,10 +3166,12 @@ await TestInClassAsync( public async Task UnConstructedGenericMethodWithConstraintsAtInvocation() { await TestInClassAsync( -@"TOut Goo(TIn arg) where TIn : Employee -{ - Go$$o(default(TIn); -}", + """ + TOut Goo(TIn arg) where TIn : Employee + { + Go$$o(default(TIn); + } + """, MainDescription("TOut C.Goo(TIn arg) where TIn : Employee")); } @@ -2819,17 +3180,19 @@ await TestInClassAsync( public async Task GenericTypeWithConstraintsAtDeclaration() { await TestAsync( -@"public class Employee : IComparable -{ - public int CompareTo(Employee other) - { - throw new NotImplementedException(); - } -} + """ + public class Employee : IComparable + { + public int CompareTo(Employee other) + { + throw new NotImplementedException(); + } + } -class Emplo$$yeeList : IEnumerable where T : Employee, System.IComparable, new() -{ -}", + class Emplo$$yeeList : IEnumerable where T : Employee, System.IComparable, new() + { + } + """, MainDescription("class EmployeeList where T : Employee, System.IComparable, new()")); } @@ -2838,10 +3201,12 @@ public int CompareTo(Employee other) public async Task GenericType() { await TestAsync( -@"class T1 -{ - $$T11 i; -}", + """ + class T1 + { + $$T11 i; + } + """, MainDescription($"T11 {FeaturesResources.in_} T1")); } @@ -2849,10 +3214,12 @@ await TestAsync( public async Task GenericMethod() { await TestInClassAsync( -@"static void Meth1(T1 i) where T1 : struct -{ - $$T1 i; -}", + """ + static void Meth1(T1 i) where T1 : struct + { + $$T1 i; + } + """, MainDescription($"T1 {FeaturesResources.in_} C.Meth1 where T1 : struct")); } @@ -2860,8 +3227,10 @@ await TestInClassAsync( public async Task Var() { await TestInMethodAsync( -@"var x = new Exception(); -var y = $$x;", + """ + var x = new Exception(); + var y = $$x; + """, MainDescription($"({FeaturesResources.local_variable}) Exception x")); } @@ -2870,18 +3239,20 @@ public async Task NullableReference() { await TestWithOptionsAsync( Options.Regular.WithLanguageVersion(LanguageVersion.CSharp8), -@"class A -{ -} -class B -{ - static void M() - { - A? x = null!; - var y = x; - $$y.ToString(); - } -}", + """ + class A + { + } + class B + { + static void M() + { + A? x = null!; + var y = x; + $$y.ToString(); + } + } + """, // https://github.com/dotnet/roslyn/issues/26198 public API should show inferred nullability MainDescription($"({FeaturesResources.local_variable}) A y")); } @@ -2889,21 +3260,23 @@ static void M() [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26648")] public async Task NullableReference_InMethod() { - var code = @" -class G -{ - void M() - { - C c; - c.Go$$o(); - } -} -public class C -{ - public string? Goo(IEnumerable arg) - { - } -}"; + var code = """ + + class G + { + void M() + { + C c; + c.Go$$o(); + } + } + public class C + { + public string? Goo(IEnumerable arg) + { + } + } + """; await TestWithOptionsAsync( Options.Regular.WithLanguageVersion(LanguageVersion.CSharp8), code, MainDescription("string? C.Goo(IEnumerable arg)")); @@ -2913,29 +3286,37 @@ await TestWithOptionsAsync( public async Task NestedInGeneric() { await TestInMethodAsync( -@"List.Enu$$merator e;", + @"List.Enu$$merator e;", MainDescription("struct System.Collections.Generic.List.Enumerator"), - TypeParameterMap($"\r\nT {FeaturesResources.is_} int")); + TypeParameterMap($""" + + T {FeaturesResources.is_} int + """)); } [Fact] public async Task NestedGenericInGeneric() { await TestAsync( -@"class Outer -{ - class Inner - { - } + """ + class Outer + { + class Inner + { + } - static void M() - { - Outer.I$$nner e; - } -}", + static void M() + { + Outer.I$$nner e; + } + } + """, MainDescription("class Outer.Inner"), TypeParameterMap( - Lines($"\r\nT {FeaturesResources.is_} int", + Lines($""" + + T {FeaturesResources.is_} int + """, $"U {FeaturesResources.is_} string"))); } @@ -2943,15 +3324,17 @@ static void M() public async Task ObjectInitializer1() { await TestInClassAsync( -@"void M() -{ - var x = new test() { $$z = 5 }; -} + """ + void M() + { + var x = new test() { $$z = 5 }; + } -class test -{ - public int z; -}", + class test + { + public int z; + } + """, MainDescription($"({FeaturesResources.field}) int test.z")); } @@ -2959,18 +3342,20 @@ class test public async Task ObjectInitializer2() { await TestInMethodAsync( -@"class C -{ - void M() - { - var x = new test() { z = $$5 }; - } + """ + class C + { + void M() + { + var x = new test() { z = $$5 }; + } - class test - { - public int z; - } -}", + class test + { + public int z; + } + } + """, MainDescription("struct System.Int32")); } @@ -2978,14 +3363,16 @@ class test public async Task TypeArgument() { await TestAsync( -@"class C -{ - void M() - { - C variable; - $$variable = new C(); - } -}", + """ + class C + { + void M() + { + C variable; + $$variable = new C(); + } + } + """, MainDescription($"({FeaturesResources.local_variable}) C variable")); } @@ -2993,13 +3380,15 @@ void M() public async Task ForEachLoop_1() { await TestInMethodAsync( -@"int bb = 555; + """ + int bb = 555; -bb = bb + 1; -foreach (int cc in new int[]{ 1,2,3}){ -c$$c = 1; -bb = bb + 21; -}", + bb = bb + 1; + foreach (int cc in new int[]{ 1,2,3}){ + c$$c = 1; + bb = bb + 21; + } + """, MainDescription($"({FeaturesResources.local_variable}) int cc")); } @@ -3007,18 +3396,20 @@ await TestInMethodAsync( public async Task TryCatchFinally_1() { await TestInMethodAsync( -@"try - { - int aa = 555; - -a$$a = aa + 1; - } - catch (Exception ex) - { - } - finally - { - }", + """ + try + { + int aa = 555; + + a$$a = aa + 1; + } + catch (Exception ex) + { + } + finally + { + } + """, MainDescription($"({FeaturesResources.local_variable}) int aa")); } @@ -3026,17 +3417,19 @@ await TestInMethodAsync( public async Task TryCatchFinally_2() { await TestInMethodAsync( -@"try - { - } - catch (Exception ex) - { - var y = e$$x; -var z = y; - } - finally - { - }", + """ + try + { + } + catch (Exception ex) + { + var y = e$$x; + var z = y; + } + finally + { + } + """, MainDescription($"({FeaturesResources.local_variable}) Exception ex")); } @@ -3044,18 +3437,20 @@ await TestInMethodAsync( public async Task TryCatchFinally_3() { await TestInMethodAsync( -@"try - { - } - catch (Exception ex) - { - var aa = 555; - -aa = a$$a + 1; - } - finally - { - }", + """ + try + { + } + catch (Exception ex) + { + var aa = 555; + + aa = a$$a + 1; + } + finally + { + } + """, MainDescription($"({FeaturesResources.local_variable}) int aa")); } @@ -3063,18 +3458,20 @@ await TestInMethodAsync( public async Task TryCatchFinally_4() { await TestInMethodAsync( -@"try - { - } - catch (Exception ex) - { - } - finally - { - int aa = 555; - -aa = a$$a + 1; - }", + """ + try + { + } + catch (Exception ex) + { + } + finally + { + int aa = 555; + + aa = a$$a + 1; + } + """, MainDescription($"({FeaturesResources.local_variable}) int aa")); } @@ -3082,14 +3479,16 @@ await TestInMethodAsync( public async Task GenericVariable() { await TestAsync( -@"class C -{ - void M() - { - C variable; - var$$iable = new C(); - } -}", + """ + class C + { + void M() + { + C variable; + var$$iable = new C(); + } + } + """, MainDescription($"({FeaturesResources.local_variable}) C variable")); } @@ -3097,15 +3496,17 @@ void M() public async Task TestInstantiation() { await TestAsync( -@"using System.Collections.Generic; + """ + using System.Collections.Generic; -class Program -{ - static void Main(string[] args) - { - var p = new Dictio$$nary(); - } -}", + class Program + { + static void Main(string[] args) + { + var p = new Dictio$$nary(); + } + } + """, MainDescription($"Dictionary.Dictionary() (+ 5 {FeaturesResources.overloads_})")); } @@ -3113,18 +3514,20 @@ static void Main(string[] args) public async Task TestUsingAlias_Bug4141() { await TestAsync( -@"using X = A.C; + """ + using X = A.C; -class A -{ - public class C - { - } -} + class A + { + public class C + { + } + } -class D : X$$ -{ -}", + class D : X$$ + { + } + """, MainDescription(@"class A.C")); } @@ -3132,7 +3535,7 @@ class D : X$$ public async Task TestFieldOnDeclaration() { await TestInClassAsync( -@"DateTime fie$$ld;", + @"DateTime fie$$ld;", MainDescription($"({FeaturesResources.field}) DateTime C.field")); } @@ -3140,7 +3543,7 @@ await TestInClassAsync( public async Task TestGenericErrorFieldOnDeclaration() { await TestInClassAsync( -@"NonExistentType fi$$eld;", + @"NonExistentType fi$$eld;", MainDescription($"({FeaturesResources.field}) NonExistentType C.field")); } @@ -3148,10 +3551,13 @@ await TestInClassAsync( public async Task TestDelegateType() { await TestInClassAsync( -@"Fun$$c field;", + @"Fun$$c field;", MainDescription("delegate TResult System.Func(T arg)"), TypeParameterMap( - Lines($"\r\nT {FeaturesResources.is_} int", + Lines($""" + + T {FeaturesResources.is_} int + """, $"TResult {FeaturesResources.is_} string"))); } @@ -3159,16 +3565,18 @@ await TestInClassAsync( public async Task TestOnDelegateInvocation() { await TestAsync( -@"class Program -{ - delegate void D1(); + """ + class Program + { + delegate void D1(); - static void Main() - { - D1 d = Main; - $$d(); - } -}", + static void Main() + { + D1 d = Main; + $$d(); + } + } + """, MainDescription($"({FeaturesResources.local_variable}) D1 d")); } @@ -3176,26 +3584,30 @@ static void Main() public async Task TestOnArrayCreation1() { await TestAsync( -@"class Program -{ - static void Main() - { - int[] a = n$$ew int[0]; - } -}", MainDescription("int[]")); + """ + class Program + { + static void Main() + { + int[] a = n$$ew int[0]; + } + } + """, MainDescription("int[]")); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539240")] public async Task TestOnArrayCreation2() { await TestAsync( -@"class Program -{ - static void Main() - { - int[] a = new i$$nt[0]; - } -}", + """ + class Program + { + static void Main() + { + int[] a = new i$$nt[0]; + } + } + """, MainDescription("struct System.Int32")); } @@ -3203,14 +3615,16 @@ static void Main() public async Task Constructor_ImplicitObjectCreation() { await TestAsync( -@"class C -{ - static void Main() - { - C c = ne$$w(); - } -} -", + """ + class C + { + static void Main() + { + C c = ne$$w(); + } + } + + """, MainDescription("C.C()")); } @@ -3218,16 +3632,18 @@ static void Main() public async Task Constructor_ImplicitObjectCreation_WithParameters() { await TestAsync( -@"class C -{ - C(int i) { } - C(string s) { } - static void Main() - { - C c = ne$$w(1); - } -} -", + """ + class C + { + C(int i) { } + C(string s) { } + static void Main() + { + C c = ne$$w(1); + } + } + + """, MainDescription($"C.C(int i) (+ 1 {FeaturesResources.overload})")); } @@ -3235,16 +3651,18 @@ static void Main() public async Task TestIsNamedTypeAccessibleForErrorTypes() { await TestAsync( -@"sealed class B : A> -{ - protected sealed override B, A$$> N() - { - } -} + """ + sealed class B : A> + { + protected sealed override B, A$$> N() + { + } + } -internal class A -{ -}", + internal class A + { + } + """, MainDescription("class A")); } @@ -3252,29 +3670,33 @@ internal class A public async Task TestErrorType() { await TestAsync( -@"using Goo = Goo; - -class C -{ - void Main() - { - $$Goo - } -}", - MainDescription("Goo")); - } + """ + using Goo = Goo; + + class C + { + void Main() + { + $$Goo + } + } + """, + MainDescription("Goo")); + } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/16662")] public async Task TestShortDiscardInAssignment() { await TestAsync( -@"class C -{ - int M() - { - $$_ = M(); - } -}", + """ + class C + { + int M() + { + $$_ = M(); + } + } + """, MainDescription($"({FeaturesResources.discard}) int _")); } @@ -3282,13 +3704,15 @@ int M() public async Task TestUnderscoreLocalInAssignment() { await TestAsync( -@"class C -{ - int M() - { - var $$_ = M(); - } -}", + """ + class C + { + int M() + { + var $$_ = M(); + } + } + """, MainDescription($"({FeaturesResources.local_variable}) int _")); } @@ -3296,14 +3720,16 @@ int M() public async Task TestShortDiscardInOutVar() { await TestAsync( -@"class C -{ - void M(out int i) - { - M(out $$_); - i = 0; - } -}", + """ + class C + { + void M(out int i) + { + M(out $$_); + i = 0; + } + } + """, MainDescription($"({FeaturesResources.discard}) int _")); } @@ -3311,57 +3737,65 @@ void M(out int i) public async Task TestDiscardInOutVar() { await TestAsync( -@"class C -{ - void M(out int i) - { - M(out var $$_); - i = 0; - } -}"); // No quick info (see issue #16667) + """ + class C + { + void M(out int i) + { + M(out var $$_); + i = 0; + } + } + """); // No quick info (see issue #16667) } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/16667")] public async Task TestDiscardInIsPattern() { await TestAsync( -@"class C -{ - void M() - { - if (3 is int $$_) { } - } -}"); // No quick info (see issue #16667) + """ + class C + { + void M() + { + if (3 is int $$_) { } + } + } + """); // No quick info (see issue #16667) } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/16667")] public async Task TestDiscardInSwitchPattern() { await TestAsync( -@"class C -{ - void M() - { - switch (3) - { - case int $$_: - return; - } - } -}"); // No quick info (see issue #16667) + """ + class C + { + void M() + { + switch (3) + { + case int $$_: + return; + } + } + } + """); // No quick info (see issue #16667) } [Fact] public async Task TestLambdaDiscardParameter_FirstDiscard() { await TestAsync( -@"class C -{ - void M() - { - System.Func f = ($$_, _) => 1; - } -}", + """ + class C + { + void M() + { + System.Func f = ($$_, _) => 1; + } + } + """, MainDescription($"({FeaturesResources.discard}) string _")); } @@ -3369,13 +3803,15 @@ void M() public async Task TestLambdaDiscardParameter_SecondDiscard() { await TestAsync( -@"class C -{ - void M() - { - System.Func f = (_, $$_) => 1; - } -}", + """ + class C + { + void M() + { + System.Func f = (_, $$_) => 1; + } + } + """, MainDescription($"({FeaturesResources.discard}) int _")); } @@ -3383,24 +3819,26 @@ void M() public async Task TestLiterals() { await TestAsync( -@"class MyClass -{ - MyClass() : this($$10) - { - intI = 2; - } + """ + class MyClass + { + MyClass() : this($$10) + { + intI = 2; + } - public MyClass(int i) - { - } + public MyClass(int i) + { + } - static int intI = 1; + static int intI = 1; - public static int Main() - { - return 1; - } -}", + public static int Main() + { + return 1; + } + } + """, MainDescription("struct System.Int32")); } @@ -3408,16 +3846,18 @@ public static int Main() public async Task TestErrorInForeach() { await TestAsync( -@"class C -{ - void Main() - { - foreach (int cc in null) - { - $$cc = 1; - } - } -}", + """ + class C + { + void Main() + { + foreach (int cc in null) + { + $$cc = 1; + } + } + } + """, MainDescription($"({FeaturesResources.local_variable}) int cc")); } @@ -3425,30 +3865,32 @@ void Main() public async Task TestQuickInfoOnEvent() { await TestAsync( -@"using System; + """ + using System; -public class SampleEventArgs -{ - public SampleEventArgs(string s) - { - Text = s; - } + public class SampleEventArgs + { + public SampleEventArgs(string s) + { + Text = s; + } - public String Text { get; private set; } -} + public String Text { get; private set; } + } -public class Publisher -{ - public delegate void SampleEventHandler(object sender, SampleEventArgs e); + public class Publisher + { + public delegate void SampleEventHandler(object sender, SampleEventArgs e); - public event SampleEventHandler SampleEvent; + public event SampleEventHandler SampleEvent; - protected virtual void RaiseSampleEvent() - { - if (Sam$$pleEvent != null) - SampleEvent(this, new SampleEventArgs(""Hello"")); - } -}", + protected virtual void RaiseSampleEvent() + { + if (Sam$$pleEvent != null) + SampleEvent(this, new SampleEventArgs("Hello")); + } + } + """, MainDescription("SampleEventHandler Publisher.SampleEvent")); } @@ -3477,28 +3919,30 @@ public async Task TestEventMinusEqualsOperator() public async Task TestQuickInfoOnExtensionMethod() { await TestWithOptionsAsync(Options.Regular, -@"using System; -using System.Collections.Generic; -using System.Linq; + """ + using System; + using System.Collections.Generic; + using System.Linq; -class Program -{ - static void Main(string[] args) - { - int[] values = { - 1 - }; - bool isArray = 7.I$$n(values); - } -} + class Program + { + static void Main(string[] args) + { + int[] values = { + 1 + }; + bool isArray = 7.I$$n(values); + } + } -public static class MyExtensions -{ - public static bool In(this T o, IEnumerable items) - { - return true; - } -}", + public static class MyExtensions + { + public static bool In(this T o, IEnumerable items) + { + return true; + } + } + """, MainDescription($"({CSharpFeaturesResources.extension}) bool int.In(IEnumerable items)")); } @@ -3506,31 +3950,33 @@ public static bool In(this T o, IEnumerable items) public async Task TestQuickInfoOnExtensionMethodOverloads() { await TestWithOptionsAsync(Options.Regular, -@"using System; -using System.Linq; + """ + using System; + using System.Linq; -class Program -{ - static void Main(string[] args) - { - ""1"".Test$$Ext(); - } -} + class Program + { + static void Main(string[] args) + { + "1".Test$$Ext(); + } + } -public static class Ex -{ - public static void TestExt(this T ex) - { - } + public static class Ex + { + public static void TestExt(this T ex) + { + } - public static void TestExt(this T ex, T arg) - { - } + public static void TestExt(this T ex, T arg) + { + } - public static void TestExt(this string ex, int arg) - { - } -}", + public static void TestExt(this string ex, int arg) + { + } + } + """, MainDescription($"({CSharpFeaturesResources.extension}) void string.TestExt() (+ 2 {FeaturesResources.overloads_})")); } @@ -3538,31 +3984,33 @@ public static void TestExt(this string ex, int arg) public async Task TestQuickInfoOnExtensionMethodOverloads2() { await TestWithOptionsAsync(Options.Regular, -@"using System; -using System.Linq; + """ + using System; + using System.Linq; -class Program -{ - static void Main(string[] args) - { - ""1"".Test$$Ext(); - } -} + class Program + { + static void Main(string[] args) + { + "1".Test$$Ext(); + } + } -public static class Ex -{ - public static void TestExt(this T ex) - { - } + public static class Ex + { + public static void TestExt(this T ex) + { + } - public static void TestExt(this T ex, T arg) - { - } + public static void TestExt(this T ex, T arg) + { + } - public static void TestExt(this int ex, int arg) - { - } -}", + public static void TestExt(this int ex, int arg) + { + } + } + """, MainDescription($"({CSharpFeaturesResources.extension}) void string.TestExt() (+ 1 {FeaturesResources.overload})")); } @@ -3570,17 +4018,19 @@ public static void TestExt(this int ex, int arg) public async Task Query1() { await TestAsync( -@"using System.Linq; + """ + using System.Linq; -class C -{ - void M() - { - var q = from n in new int[] { 1, 2, 3, 4, 5 } + class C + { + void M() + { + var q = from n in new int[] { 1, 2, 3, 4, 5 } - select $$n; - } -}", + select $$n; + } + } + """, MainDescription($"({FeaturesResources.range_variable}) int n")); } @@ -3588,17 +4038,19 @@ void M() public async Task Query2() { await TestAsync( -@"using System.Linq; + """ + using System.Linq; -class C -{ - void M() - { - var q = from n$$ in new int[] { 1, 2, 3, 4, 5 } + class C + { + void M() + { + var q = from n$$ in new int[] { 1, 2, 3, 4, 5 } - select n; - } -}", + select n; + } + } + """, MainDescription($"({FeaturesResources.range_variable}) int n")); } @@ -3606,15 +4058,17 @@ void M() public async Task Query3() { await TestAsync( -@"class C -{ - void M() - { - var q = from n in new int[] { 1, 2, 3, 4, 5 } + """ + class C + { + void M() + { + var q = from n in new int[] { 1, 2, 3, 4, 5 } - select $$n; - } -}", + select $$n; + } + } + """, MainDescription($"({FeaturesResources.range_variable}) ? n")); } @@ -3622,15 +4076,17 @@ void M() public async Task Query4() { await TestAsync( -@"class C -{ - void M() - { - var q = from n$$ in new int[] { 1, 2, 3, 4, 5 } + """ + class C + { + void M() + { + var q = from n$$ in new int[] { 1, 2, 3, 4, 5 } - select n; - } -}", + select n; + } + } + """, MainDescription($"({FeaturesResources.range_variable}) ? n")); } @@ -3638,17 +4094,19 @@ void M() public async Task Query5() { await TestAsync( -@"using System.Collections.Generic; -using System.Linq; + """ + using System.Collections.Generic; + using System.Linq; -class C -{ - void M() - { - var q = from n in new List() - select $$n; - } -}", + class C + { + void M() + { + var q = from n in new List() + select $$n; + } + } + """, MainDescription($"({FeaturesResources.range_variable}) object n")); } @@ -3656,17 +4114,19 @@ void M() public async Task Query6() { await TestAsync( -@"using System.Collections.Generic; -using System.Linq; + """ + using System.Collections.Generic; + using System.Linq; -class C -{ - void M() - { - var q = from n$$ in new List() - select n; - } -}", + class C + { + void M() + { + var q = from n$$ in new List() + select n; + } + } + """, MainDescription($"({FeaturesResources.range_variable}) object n")); } @@ -3674,17 +4134,19 @@ void M() public async Task Query7() { await TestAsync( -@"using System.Collections.Generic; -using System.Linq; + """ + using System.Collections.Generic; + using System.Linq; -class C -{ - void M() - { - var q = from int n in new List() - select $$n; - } -}", + class C + { + void M() + { + var q = from int n in new List() + select $$n; + } + } + """, MainDescription($"({FeaturesResources.range_variable}) int n")); } @@ -3692,17 +4154,19 @@ void M() public async Task Query8() { await TestAsync( -@"using System.Collections.Generic; -using System.Linq; + """ + using System.Collections.Generic; + using System.Linq; -class C -{ - void M() - { - var q = from int n$$ in new List() - select n; - } -}", + class C + { + void M() + { + var q = from int n$$ in new List() + select n; + } + } + """, MainDescription($"({FeaturesResources.range_variable}) int n")); } @@ -3710,18 +4174,20 @@ void M() public async Task Query9() { await TestAsync( -@"using System.Collections.Generic; -using System.Linq; + """ + using System.Collections.Generic; + using System.Linq; -class C -{ - void M() - { - var q = from x$$ in new List>() - from y in x - select y; - } -}", + class C + { + void M() + { + var q = from x$$ in new List>() + from y in x + select y; + } + } + """, MainDescription($"({FeaturesResources.range_variable}) List x")); } @@ -3729,18 +4195,20 @@ from y in x public async Task Query10() { await TestAsync( -@"using System.Collections.Generic; -using System.Linq; + """ + using System.Collections.Generic; + using System.Linq; -class C -{ - void M() - { - var q = from x in new List>() - from y in $$x - select y; - } -}", + class C + { + void M() + { + var q = from x in new List>() + from y in $$x + select y; + } + } + """, MainDescription($"({FeaturesResources.range_variable}) List x")); } @@ -3748,18 +4216,20 @@ from y in $$x public async Task Query11() { await TestAsync( -@"using System.Collections.Generic; -using System.Linq; + """ + using System.Collections.Generic; + using System.Linq; -class C -{ - void M() - { - var q = from x in new List>() - from y$$ in x - select y; - } -}", + class C + { + void M() + { + var q = from x in new List>() + from y$$ in x + select y; + } + } + """, MainDescription($"({FeaturesResources.range_variable}) int y")); } @@ -3767,18 +4237,20 @@ from y$$ in x public async Task Query12() { await TestAsync( -@"using System.Collections.Generic; -using System.Linq; + """ + using System.Collections.Generic; + using System.Linq; -class C -{ - void M() - { - var q = from x in new List>() - from y in x - select $$y; - } -}", + class C + { + void M() + { + var q = from x in new List>() + from y in x + select $$y; + } + } + """, MainDescription($"({FeaturesResources.range_variable}) int y")); } @@ -3786,10 +4258,12 @@ from y in x public async Task QueryMethodinfoSelectMappedEnumerable() { await TestInMethodAsync( -@" - var q = from i in new int[0] - $$select i; -", + """ + + var q = from i in new int[0] + $$select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable IEnumerable.Select(Func selector)")); } @@ -3797,10 +4271,12 @@ await TestInMethodAsync( public async Task QueryMethodinfoSelectMappedQueryable() { await TestInMethodAsync( -@" - var q = from i in new int[0].AsQueryable() - $$select i; -", + """ + + var q = from i in new int[0].AsQueryable() + $$select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IQueryable IQueryable.Select(System.Linq.Expressions.Expression> selector)")); } @@ -3808,26 +4284,28 @@ await TestInMethodAsync( public async Task QueryMethodinfoSelectMappedCustom() { await TestAsync( -@" -using System; -using System.Linq; + """ -namespace N { - public static class LazyExt - { - public static Lazy Select(this Lazy source, Func selector) => new Lazy(() => selector(source.Value)); - } - public class C - { - public void M() - { - var lazy = new Lazy(); - var q = from i in lazy - $$select i; - } - } -} -", + using System; + using System.Linq; + + namespace N { + public static class LazyExt + { + public static Lazy Select(this Lazy source, Func selector) => new Lazy(() => selector(source.Value)); + } + public class C + { + public void M() + { + var lazy = new Lazy(); + var q = from i in lazy + $$select i; + } + } + } + + """, MainDescription($"({CSharpFeaturesResources.extension}) Lazy Lazy.Select(Func selector)")); } @@ -3835,37 +4313,45 @@ public void M() public async Task QueryMethodinfoSelectNotMapped() { await TestInMethodAsync( -@" - var q = from i in new int[0] - where true - $$select i; -"); + """ + + var q = from i in new int[0] + where true + $$select i; + + """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23394")] public async Task QueryMethodinfoLet() { await TestInMethodAsync( -@" - var q = from i in new int[0] - $$let j = true - select i; -", + """ + + var q = from i in new int[0] + $$let j = true + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable<'a> IEnumerable.Select(Func selector)"), - AnonymousTypes($@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} new {{ int i, bool j }}")); + AnonymousTypes($$""" + + {{FeaturesResources.Types_colon}} + 'a {{FeaturesResources.is_}} new { int i, bool j } + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23394")] public async Task QueryMethodinfoWhere() { await TestInMethodAsync( -@" - var q = from i in new int[0] - $$where true - select i; -", + """ + + var q = from i in new int[0] + $$where true + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable IEnumerable.Where(Func predicate)")); } @@ -3873,11 +4359,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoOrderByOneProperty() { await TestInMethodAsync( -@" - var q = from i in new int[0] - $$orderby i - select i; -", + """ + + var q = from i in new int[0] + $$orderby i + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IOrderedEnumerable IEnumerable.OrderBy(Func keySelector)")); } @@ -3885,11 +4373,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoOrderByOnePropertyWithOrdering1() { await TestInMethodAsync( -@" - var q = from i in new int[0] - orderby i $$ascending - select i; -", + """ + + var q = from i in new int[0] + orderby i $$ascending + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IOrderedEnumerable IEnumerable.OrderBy(Func keySelector)")); } @@ -3897,22 +4387,26 @@ orderby i $$ascending public async Task QueryMethodinfoOrderByOnePropertyWithOrdering2() { await TestInMethodAsync( -@" - var q = from i in new int[0] - $$orderby i ascending - select i; -"); + """ + + var q = from i in new int[0] + $$orderby i ascending + select i; + + """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23394")] public async Task QueryMethodinfoOrderByTwoPropertiesWithComma1() { await TestInMethodAsync( -@" - var q = from i in new int[0] - orderby i$$, i - select i; -", + """ + + var q = from i in new int[0] + orderby i$$, i + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IOrderedEnumerable IOrderedEnumerable.ThenBy(Func keySelector)")); } @@ -3920,11 +4414,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoOrderByTwoPropertiesWithComma2() { await TestInMethodAsync( -@" - var q = from i in new int[0] - $$orderby i, i - select i; -", + """ + + var q = from i in new int[0] + $$orderby i, i + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IOrderedEnumerable IEnumerable.OrderBy(Func keySelector)")); } @@ -3932,11 +4428,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoOrderByTwoPropertiesWithOrdering1() { await TestInMethodAsync( -@" - var q = from i in new int[0] - $$orderby i, i ascending - select i; -", + """ + + var q = from i in new int[0] + $$orderby i, i ascending + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IOrderedEnumerable IEnumerable.OrderBy(Func keySelector)")); } @@ -3944,22 +4442,26 @@ await TestInMethodAsync( public async Task QueryMethodinfoOrderByTwoPropertiesWithOrdering2() { await TestInMethodAsync( -@" - var q = from i in new int[0] - orderby i,$$ i ascending - select i; -"); + """ + + var q = from i in new int[0] + orderby i,$$ i ascending + select i; + + """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23394")] public async Task QueryMethodinfoOrderByTwoPropertiesWithOrdering3() { await TestInMethodAsync( -@" - var q = from i in new int[0] - orderby i, i $$ascending - select i; -", + """ + + var q = from i in new int[0] + orderby i, i $$ascending + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IOrderedEnumerable IOrderedEnumerable.ThenBy(Func keySelector)")); } @@ -3967,22 +4469,26 @@ await TestInMethodAsync( public async Task QueryMethodinfoOrderByTwoPropertiesWithOrderingOnEach1() { await TestInMethodAsync( -@" - var q = from i in new int[0] - $$orderby i ascending, i ascending - select i; -"); + """ + + var q = from i in new int[0] + $$orderby i ascending, i ascending + select i; + + """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23394")] public async Task QueryMethodinfoOrderByTwoPropertiesWithOrderingOnEach2() { await TestInMethodAsync( -@" - var q = from i in new int[0] - orderby i $$ascending, i ascending - select i; -", + """ + + var q = from i in new int[0] + orderby i $$ascending, i ascending + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IOrderedEnumerable IEnumerable.OrderBy(Func keySelector)")); } @@ -3990,22 +4496,26 @@ await TestInMethodAsync( public async Task QueryMethodinfoOrderByTwoPropertiesWithOrderingOnEach3() { await TestInMethodAsync( -@" - var q = from i in new int[0] - orderby i ascending ,$$ i ascending - select i; -"); + """ + + var q = from i in new int[0] + orderby i ascending ,$$ i ascending + select i; + + """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23394")] public async Task QueryMethodinfoOrderByTwoPropertiesWithOrderingOnEach4() { await TestInMethodAsync( -@" - var q = from i in new int[0] - orderby i ascending, i $$ascending - select i; -", + """ + + var q = from i in new int[0] + orderby i ascending, i $$ascending + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IOrderedEnumerable IOrderedEnumerable.ThenBy(Func keySelector)")); } @@ -4013,11 +4523,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoOrderByIncomplete() { await TestInMethodAsync( -@" - var q = from i in new int[0] - where i > 0 - orderby$$ -", + """ + + var q = from i in new int[0] + where i > 0 + orderby$$ + + """, MainDescription($"({CSharpFeaturesResources.extension}) IOrderedEnumerable IEnumerable.OrderBy(Func keySelector)")); } @@ -4025,11 +4537,13 @@ where i > 0 public async Task QueryMethodinfoSelectMany1() { await TestInMethodAsync( -@" - var q = from i1 in new int[0] - $$from i2 in new int[0] - select i1; -", + """ + + var q = from i1 in new int[0] + $$from i2 in new int[0] + select i1; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable IEnumerable.SelectMany(Func> collectionSelector, Func resultSelector)")); } @@ -4037,11 +4551,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoSelectMany2() { await TestInMethodAsync( -@" - var q = from i1 in new int[0] - from i2 $$in new int[0] - select i1; -", + """ + + var q = from i1 in new int[0] + from i2 $$in new int[0] + select i1; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable IEnumerable.SelectMany(Func> collectionSelector, Func resultSelector)")); } @@ -4049,10 +4565,12 @@ await TestInMethodAsync( public async Task QueryMethodinfoGroupBy1() { await TestInMethodAsync( -@" - var q = from i in new int[0] - $$group i by i; -", + """ + + var q = from i in new int[0] + $$group i by i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable> IEnumerable.GroupBy(Func keySelector)")); } @@ -4060,10 +4578,12 @@ await TestInMethodAsync( public async Task QueryMethodinfoGroupBy2() { await TestInMethodAsync( -@" - var q = from i in new int[0] - group i $$by i; -", + """ + + var q = from i in new int[0] + group i $$by i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable> IEnumerable.GroupBy(Func keySelector)")); } @@ -4071,11 +4591,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoGroupByInto() { await TestInMethodAsync( -@" - var q = from i in new int[0] - $$group i by i into g - select g; -", + """ + + var q = from i in new int[0] + $$group i by i into g + select g; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable> IEnumerable.GroupBy(Func keySelector)")); } @@ -4083,11 +4605,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoJoin1() { await TestInMethodAsync( -@" - var q = from i1 in new int[0] - $$join i2 in new int[0] on i1 equals i2 - select i1; -", + """ + + var q = from i1 in new int[0] + $$join i2 in new int[0] on i1 equals i2 + select i1; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable IEnumerable.Join(IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector)")); } @@ -4095,11 +4619,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoJoin2() { await TestInMethodAsync( -@" - var q = from i1 in new int[0] - join i2 $$in new int[0] on i1 equals i2 - select i1; -", + """ + + var q = from i1 in new int[0] + join i2 $$in new int[0] on i1 equals i2 + select i1; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable IEnumerable.Join(IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector)")); } @@ -4107,11 +4633,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoJoin3() { await TestInMethodAsync( -@" - var q = from i1 in new int[0] - join i2 in new int[0] $$on i1 equals i2 - select i1; -", + """ + + var q = from i1 in new int[0] + join i2 in new int[0] $$on i1 equals i2 + select i1; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable IEnumerable.Join(IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector)")); } @@ -4119,11 +4647,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoJoin4() { await TestInMethodAsync( -@" - var q = from i1 in new int[0] - join i2 in new int[0] on i1 $$equals i2 - select i1; -", + """ + + var q = from i1 in new int[0] + join i2 in new int[0] on i1 $$equals i2 + select i1; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable IEnumerable.Join(IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector)")); } @@ -4131,11 +4661,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoJoinInto1() { await TestInMethodAsync( -@" - var q = from i1 in new int[0] - $$join i2 in new int[0] on i1 equals i2 into g - select g; -", + """ + + var q = from i1 in new int[0] + $$join i2 in new int[0] on i1 equals i2 into g + select g; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable> IEnumerable.GroupJoin>(IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func, IEnumerable> resultSelector)")); } @@ -4143,31 +4675,37 @@ await TestInMethodAsync( public async Task QueryMethodinfoJoinInto2() { await TestInMethodAsync( -@" - var q = from i1 in new int[0] - join i2 in new int[0] on i1 equals i2 $$into g - select g; -"); + """ + + var q = from i1 in new int[0] + join i2 in new int[0] on i1 equals i2 $$into g + select g; + + """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23394")] public async Task QueryMethodinfoFromMissing() { await TestInMethodAsync( -@" - var q = $$from i in new int[0] - select i; -"); + """ + + var q = $$from i in new int[0] + select i; + + """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23394")] public async Task QueryMethodinfoRangeVariableSimple1() { await TestInMethodAsync( -@" - var q = $$from double i in new int[0] - select i; -", + """ + + var q = $$from double i in new int[0] + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable System.Collections.IEnumerable.Cast()")); } @@ -4175,10 +4713,12 @@ await TestInMethodAsync( public async Task QueryMethodinfoRangeVariableSimple2() { await TestInMethodAsync( -@" - var q = from double i $$in new int[0] - select i; -", + """ + + var q = from double i $$in new int[0] + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable System.Collections.IEnumerable.Cast()")); } @@ -4186,11 +4726,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoRangeVariableSelectMany1() { await TestInMethodAsync( -@" - var q = from i in new int[0] - $$from double d in new int[0] - select i; -", + """ + + var q = from i in new int[0] + $$from double d in new int[0] + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable IEnumerable.SelectMany(Func> collectionSelector, Func resultSelector)")); } @@ -4198,23 +4740,27 @@ await TestInMethodAsync( public async Task QueryMethodinfoRangeVariableSelectMany2() { await TestInMethodAsync( -@" - var q = from i in new int[0] - from double d $$in new int[0] - select i; -", - MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable System.Collections.IEnumerable.Cast()")); + """ + + var q = from i in new int[0] + from double d $$in new int[0] + select i; + + """, + MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable System.Collections.IEnumerable.Cast()")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23394")] public async Task QueryMethodinfoRangeVariableJoin1() { await TestInMethodAsync( -@" - var q = from i1 in new int[0] - $$join int i2 in new double[0] on i1 equals i2 - select i1; -", + """ + + var q = from i1 in new int[0] + $$join int i2 in new double[0] on i1 equals i2 + select i1; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable IEnumerable.Join(IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector)")); } @@ -4222,11 +4768,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoRangeVariableJoin2() { await TestInMethodAsync( -@" - var q = from i1 in new int[0] - join int i2 $$in new double[0] on i1 equals i2 - select i1; -", + """ + + var q = from i1 in new int[0] + join int i2 $$in new double[0] on i1 equals i2 + select i1; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable System.Collections.IEnumerable.Cast()")); } @@ -4234,11 +4782,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoRangeVariableJoin3() { await TestInMethodAsync( -@" - var q = from i1 in new int[0] - join int i2 in new double[0] $$on i1 equals i2 - select i1; -", + """ + + var q = from i1 in new int[0] + join int i2 in new double[0] $$on i1 equals i2 + select i1; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable IEnumerable.Join(IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector)")); } @@ -4246,11 +4796,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoRangeVariableJoin4() { await TestInMethodAsync( -@" - var q = from i1 in new int[0] - join int i2 in new double[0] on i1 $$equals i2 - select i1; -", + """ + + var q = from i1 in new int[0] + join int i2 in new double[0] on i1 $$equals i2 + select i1; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable IEnumerable.Join(IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector)")); } @@ -4258,16 +4810,18 @@ await TestInMethodAsync( public async Task TestErrorGlobal() { await TestAsync( -@"extern alias global; + """ + extern alias global; -class myClass -{ - static int Main() - { - $$global::otherClass oc = new global::otherClass(); - return 0; - } -}", + class myClass + { + static int Main() + { + $$global::otherClass oc = new global::otherClass(); + return 0; + } + } + """, MainDescription("")); } @@ -4275,12 +4829,14 @@ static int Main() public async Task DoNotRemoveAttributeSuffixAndProduceInvalidIdentifier1() { await TestAsync( -@"using System; + """ + using System; -class classAttribute : Attribute -{ - private classAttribute x$$; -}", + class classAttribute : Attribute + { + private classAttribute x$$; + } + """, MainDescription($"({FeaturesResources.field}) classAttribute classAttribute.x")); } @@ -4288,12 +4844,14 @@ class classAttribute : Attribute public async Task DoNotRemoveAttributeSuffix2() { await TestAsync( -@"using System; + """ + using System; -class class1Attribute : Attribute -{ - private class1Attribute x$$; -}", + class class1Attribute : Attribute + { + private class1Attribute x$$; + } + """, MainDescription($"({FeaturesResources.field}) class1Attribute class1Attribute.x")); } @@ -4301,21 +4859,23 @@ class class1Attribute : Attribute public async Task AttributeQuickInfoBindsToClassTest() { await TestAsync( -@"using System; + """ + using System; -/// -/// class comment -/// -[Some$$] -class SomeAttribute : Attribute -{ - /// - /// ctor comment - /// - public SomeAttribute() - { - } -}", + /// + /// class comment + /// + [Some$$] + class SomeAttribute : Attribute + { + /// + /// ctor comment + /// + public SomeAttribute() + { + } + } + """, Documentation("class comment")); } @@ -4323,21 +4883,23 @@ public SomeAttribute() public async Task AttributeConstructorQuickInfo() { await TestAsync( -@"using System; + """ + using System; -/// -/// class comment -/// -class SomeAttribute : Attribute -{ - /// - /// ctor comment - /// - public SomeAttribute() - { - var s = new Some$$Attribute(); - } -}", + /// + /// class comment + /// + class SomeAttribute : Attribute + { + /// + /// ctor comment + /// + public SomeAttribute() + { + var s = new Some$$Attribute(); + } + } + """, Documentation("ctor comment")); } @@ -4345,12 +4907,14 @@ public SomeAttribute() public async Task TestLabel() { await TestInClassAsync( -@"void M() -{ -Goo: - int Goo; - goto Goo$$; -}", + """ + void M() + { + Goo: + int Goo; + goto Goo$$; + } + """, MainDescription($"({FeaturesResources.label}) Goo")); } @@ -4358,16 +4922,18 @@ await TestInClassAsync( public async Task TestUnboundGeneric() { await TestAsync( -@"using System; -using System.Collections.Generic; + """ + using System; + using System.Collections.Generic; -class C -{ - void M() - { - Type t = typeof(L$$ist<>); - } -}", + class C + { + void M() + { + Type t = typeof(L$$ist<>); + } + } + """, MainDescription("class System.Collections.Generic.List"), NoTypeParameterMap); } @@ -4376,19 +4942,23 @@ void M() public async Task TestAnonymousTypeNew1() { await TestAsync( -@"class C -{ - void M() - { - var v = $$new { }; - } -}", + """ + class C + { + void M() + { + var v = $$new { }; + } + } + """, MainDescription(@"AnonymousType 'a"), NoTypeParameterMap, AnonymousTypes( -$@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} new {{ }}")); + $$""" + + {{FeaturesResources.Types_colon}} + 'a {{FeaturesResources.is_}} new { } + """)); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543873")] @@ -4397,68 +4967,82 @@ public async Task TestNestedAnonymousType() // verify nested anonymous types are listed in the same order for different properties // verify first property await TestInMethodAsync( -@"var x = new[] { new { Name = ""BillG"", Address = new { Street = ""1 Microsoft Way"", Zip = ""98052"" } } }; + """ + var x = new[] { new { Name = "BillG", Address = new { Street = "1 Microsoft Way", Zip = "98052" } } }; -x[0].$$Address", + x[0].$$Address + """, MainDescription(@"'b 'a.Address { get; }"), NoTypeParameterMap, AnonymousTypes( -$@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} new {{ string Name, 'b Address }} - 'b {FeaturesResources.is_} new {{ string Street, string Zip }}")); + $$""" + + {{FeaturesResources.Types_colon}} + 'a {{FeaturesResources.is_}} new { string Name, 'b Address } + 'b {{FeaturesResources.is_}} new { string Street, string Zip } + """)); // verify second property await TestInMethodAsync( -@"var x = new[] { new { Name = ""BillG"", Address = new { Street = ""1 Microsoft Way"", Zip = ""98052"" } } }; + """ + var x = new[] { new { Name = "BillG", Address = new { Street = "1 Microsoft Way", Zip = "98052" } } }; -x[0].$$Name", + x[0].$$Name + """, MainDescription(@"string 'a.Name { get; }"), NoTypeParameterMap, AnonymousTypes( -$@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} new {{ string Name, 'b Address }} - 'b {FeaturesResources.is_} new {{ string Street, string Zip }}")); + $$""" + + {{FeaturesResources.Types_colon}} + 'a {{FeaturesResources.is_}} new { string Name, 'b Address } + 'b {{FeaturesResources.is_}} new { string Street, string Zip } + """)); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543183")] public async Task TestAssignmentOperatorInAnonymousType() { await TestAsync( -@"class C -{ - void M() - { - var a = new { A $$= 0 }; - } -}"); + """ + class C + { + void M() + { + var a = new { A $$= 0 }; + } + } + """); } [Fact, WorkItem(10731, "DevDiv_Projects/Roslyn")] public async Task TestErrorAnonymousTypeDoesntShow() { await TestInMethodAsync( -@"var a = new { new { N = 0 }.N, new { } }.$$N;", + @"var a = new { new { N = 0 }.N, new { } }.$$N;", MainDescription(@"int 'a.N { get; }"), NoTypeParameterMap, AnonymousTypes( -$@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} new {{ int N }}")); + $$""" + + {{FeaturesResources.Types_colon}} + 'a {{FeaturesResources.is_}} new { int N } + """)); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543553")] public async Task TestArrayAssignedToVar() { await TestAsync( -@"class C -{ - static void M(string[] args) - { - v$$ar a = args; - } -}", + """ + class C + { + static void M(string[] args) + { + v$$ar a = args; + } + } + """, MainDescription("string[]")); } @@ -4466,23 +5050,25 @@ static void M(string[] args) public async Task ColorColorRangeVariable() { await TestAsync( -@"using System.Collections.Generic; -using System.Linq; + """ + using System.Collections.Generic; + using System.Linq; -namespace N1 -{ - class yield - { - public static IEnumerable Bar() - { - foreach (yield yield in from yield in new yield[0] - select y$$ield) + namespace N1 { - yield return yield; + class yield + { + public static IEnumerable Bar() + { + foreach (yield yield in from yield in new yield[0] + select y$$ield) + { + yield return yield; + } + } + } } - } - } -}", + """, MainDescription($"({FeaturesResources.range_variable}) N1.yield yield")); } @@ -4490,26 +5076,28 @@ public static IEnumerable Bar() public async Task QuickInfoOnOperator() { await TestAsync( -@"using System.Collections.Generic; + """ + using System.Collections.Generic; -class Program -{ - static void Main(string[] args) - { - var v = new Program() $$+ string.Empty; - } + class Program + { + static void Main(string[] args) + { + var v = new Program() $$+ string.Empty; + } - public static implicit operator Program(string s) - { - return null; - } + public static implicit operator Program(string s) + { + return null; + } - public static IEnumerable operator +(Program p1, Program p2) - { - yield return p1; - yield return p2; - } -}", + public static IEnumerable operator +(Program p1, Program p2) + { + yield return p1; + yield return p2; + } + } + """, MainDescription("IEnumerable Program.operator +(Program p1, Program p2)")); } @@ -4517,9 +5105,11 @@ public static implicit operator Program(string s) public async Task TestConstantField() { await TestAsync( -@"class C -{ - const int $$F = 1;", + """ + class C + { + const int $$F = 1; + """, MainDescription($"({FeaturesResources.constant}) int C.F = 1")); } @@ -4527,9 +5117,11 @@ await TestAsync( public async Task TestMultipleConstantFields() { await TestAsync( -@"class C -{ - public const double X = 1.0, Y = 2.0, $$Z = 3.5;", + """ + class C + { + public const double X = 1.0, Y = 2.0, $$Z = 3.5; + """, MainDescription($"({FeaturesResources.constant}) double C.Z = 3.5")); } @@ -4537,16 +5129,18 @@ await TestAsync( public async Task TestConstantDependencies() { await TestAsync( -@"class A -{ - public const int $$X = B.Z + 1; - public const int Y = 10; -} + """ + class A + { + public const int $$X = B.Z + 1; + public const int Y = 10; + } -class B -{ - public const int Z = A.Y + 1; -}", + class B + { + public const int Z = A.Y + 1; + } + """, MainDescription($"({FeaturesResources.constant}) int A.X = B.Z + 1")); } @@ -4554,15 +5148,17 @@ class B public async Task TestConstantCircularDependencies() { await TestAsync( -@"class A -{ - public const int X = B.Z + 1; -} + """ + class A + { + public const int X = B.Z + 1; + } -class B -{ - public const int Z$$ = A.X + 1; -}", + class B + { + public const int Z$$ = A.X + 1; + } + """, MainDescription($"({FeaturesResources.constant}) int B.Z = A.X + 1")); } @@ -4570,10 +5166,12 @@ class B public async Task TestConstantOverflow() { await TestAsync( -@"class B -{ - public const int Z$$ = int.MaxValue + 1; -}", + """ + class B + { + public const int Z$$ = int.MaxValue + 1; + } + """, MainDescription($"({FeaturesResources.constant}) int B.Z = int.MaxValue + 1")); } @@ -4581,10 +5179,12 @@ await TestAsync( public async Task TestConstantOverflowInUncheckedContext() { await TestAsync( -@"class B -{ - public const int Z$$ = unchecked(int.MaxValue + 1); -}", + """ + class B + { + public const int Z$$ = unchecked(int.MaxValue + 1); + } + """, MainDescription($"({FeaturesResources.constant}) int B.Z = unchecked(int.MaxValue + 1)")); } @@ -4592,24 +5192,26 @@ await TestAsync( public async Task TestEnumInConstantField() { await TestAsync( -@"public class EnumTest -{ - enum Days - { - Sun, - Mon, - Tue, - Wed, - Thu, - Fri, - Sat - }; - - static void Main() - { - const int $$x = (int)Days.Sun; - } -}", + """ + public class EnumTest + { + enum Days + { + Sun, + Mon, + Tue, + Wed, + Thu, + Fri, + Sat + }; + + static void Main() + { + const int $$x = (int)Days.Sun; + } + } + """, MainDescription($"({FeaturesResources.local_constant}) int x = (int)Days.Sun")); } @@ -4617,24 +5219,26 @@ static void Main() public async Task TestConstantInDefaultExpression() { await TestAsync( -@"public class EnumTest -{ - enum Days - { - Sun, - Mon, - Tue, - Wed, - Thu, - Fri, - Sat - }; - - static void Main() - { - const Days $$x = default(Days); - } -}", + """ + public class EnumTest + { + enum Days + { + Sun, + Mon, + Tue, + Wed, + Thu, + Fri, + Sat + }; + + static void Main() + { + const Days $$x = default(Days); + } + } + """, MainDescription($"({FeaturesResources.local_constant}) Days x = default(Days)")); } @@ -4642,10 +5246,12 @@ static void Main() public async Task TestConstantParameter() { await TestAsync( -@"class C -{ - void Bar(int $$b = 1); -}", + """ + class C + { + void Bar(int $$b = 1); + } + """, MainDescription($"({FeaturesResources.parameter}) int b = 1")); } @@ -4653,12 +5259,14 @@ await TestAsync( public async Task TestConstantLocal() { await TestAsync( -@"class C -{ - void Bar() - { - const int $$loc = 1; - }", + """ + class C + { + void Bar() + { + const int $$loc = 1; + } + """, MainDescription($"({FeaturesResources.local_constant}) int loc = 1")); } @@ -4666,7 +5274,7 @@ void Bar() public async Task TestErrorType1() { await TestInMethodAsync( -@"var $$v1 = new Goo();", + @"var $$v1 = new Goo();", MainDescription($"({FeaturesResources.local_variable}) Goo v1")); } @@ -4674,7 +5282,7 @@ await TestInMethodAsync( public async Task TestErrorType2() { await TestInMethodAsync( -@"var $$v1 = v1;", + @"var $$v1 = v1;", MainDescription($"({FeaturesResources.local_variable}) var v1")); } @@ -4682,7 +5290,7 @@ await TestInMethodAsync( public async Task TestErrorType3() { await TestInMethodAsync( -@"var $$v1 = new Goo();", + @"var $$v1 = new Goo();", MainDescription($"({FeaturesResources.local_variable}) Goo v1")); } @@ -4690,7 +5298,7 @@ await TestInMethodAsync( public async Task TestErrorType4() { await TestInMethodAsync( -@"var $$v1 = &(x => x);", + @"var $$v1 = &(x => x);", MainDescription($"({FeaturesResources.local_variable}) ?* v1")); } @@ -4712,17 +5320,19 @@ public async Task TestErrorType6() public async Task TestErrorType7() { await TestInClassAsync( -@"class C -{ - void Method() - { - } + """ + class C + { + void Method() + { + } - void Goo() - { - var $$v1 = MethodGroup; - } -}", + void Goo() + { + var $$v1 = MethodGroup; + } + } + """, MainDescription($"({FeaturesResources.local_variable}) ? v1")); } @@ -4737,7 +5347,7 @@ await TestInMethodAsync("var $$v1 = Unknown", public async Task TestDelegateSpecialTypes() { await TestAsync( -@"delegate void $$F(int x);", + @"delegate void $$F(int x);", MainDescription("delegate void F(int x)")); } @@ -4745,18 +5355,20 @@ await TestAsync( public async Task TestNullPointerParameter() { await TestAsync( -@"class C -{ - unsafe void $$Goo(int* x = null) - { - } -}", - MainDescription("void C.Goo([int* x = null])")); - } - - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545098")] - public async Task TestLetIdentifier1() - { + """ + class C + { + unsafe void $$Goo(int* x = null) + { + } + } + """, + MainDescription("void C.Goo([int* x = null])")); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545098")] + public async Task TestLetIdentifier1() + { await TestInMethodAsync("var q = from e in \"\" let $$y = 1 let a = new { y } select a;", MainDescription($"({FeaturesResources.range_variable}) int y")); } @@ -4765,12 +5377,14 @@ public async Task TestLetIdentifier1() public async Task TestNullableDefaultValue() { await TestAsync( -@"class Test -{ - void $$Method(int? t1 = null) - { - } -}", + """ + class Test + { + void $$Method(int? t1 = null) + { + } + } + """, MainDescription("void Test.Method([int? t1 = null])")); } @@ -4778,30 +5392,36 @@ await TestAsync( public async Task TestInvalidParameterInitializer() { await TestAsync( -@"class Program -{ - void M1(float $$j1 = ""Hello"" -+ -""World"") - { - } -}", - MainDescription($@"({FeaturesResources.parameter}) float j1 = ""Hello"" + ""World""")); + """ + class Program + { + void M1(float $$j1 = "Hello" + + + "World") + { + } + } + """, + MainDescription($""" + ({FeaturesResources.parameter}) float j1 = "Hello" + "World" + """)); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545230")] public async Task TestComplexConstLocal() { await TestAsync( -@"class Program -{ - void Main() - { - const int MEGABYTE = 1024 * - 1024 + true; - Blah($$MEGABYTE); - } -}", + """ + class Program + { + void Main() + { + const int MEGABYTE = 1024 * + 1024 + true; + Blah($$MEGABYTE); + } + } + """, MainDescription($@"({FeaturesResources.local_constant}) int MEGABYTE = 1024 * 1024 + true")); } @@ -4809,17 +5429,19 @@ void Main() public async Task TestComplexConstField() { await TestAsync( -@"class Program -{ - const int a = true - - - false; + """ + class Program + { + const int a = true + - + false; - void Main() - { - Goo($$a); - } -}", + void Main() + { + Goo($$a); + } + } + """, MainDescription($"({FeaturesResources.constant}) int Program.a = true - false")); } @@ -4827,26 +5449,30 @@ void Main() public async Task TestTypeParameterCrefDoesNotHaveQuickInfo() { await TestAsync( -@"class C -{ - /// - static void Main(string[] args) - { - } -}"); + """ + class C + { + /// + static void Main(string[] args) + { + } + } + """); } [Fact] public async Task TestCref1() { await TestAsync( -@"class Program -{ - /// - static void Main(string[] args) - { - } -}", + """ + class Program + { + /// + static void Main(string[] args) + { + } + } + """, MainDescription(@"void Program.Main(string[] args)")); } @@ -4854,13 +5480,15 @@ static void Main(string[] args) public async Task TestCref2() { await TestAsync( -@"class Program -{ - /// - static void Main(string[] args) - { - } -}", + """ + class Program + { + /// + static void Main(string[] args) + { + } + } + """, MainDescription(@"void Program.Main(string[] args)")); } @@ -4868,79 +5496,89 @@ static void Main(string[] args) public async Task TestCref3() { await TestAsync( -@"class Program -{ - /// - static void Main(string[] args) - { - } -}"); + """ + class Program + { + /// + static void Main(string[] args) + { + } + } + """); } [Fact] public async Task TestCref4() { await TestAsync( -@"class Program -{ - /// - static void Main(string[] args) - { - } -}"); + """ + class Program + { + /// + static void Main(string[] args) + { + } + } + """); } [Fact] public async Task TestCref5() { await TestAsync( -@"class Program -{ - /// - static void Main(string[] args) - { - } -}"); + """ + class Program + { + /// + static void Main(string[] args) + { + } + } + """); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546849")] public async Task TestIndexedProperty() { - var markup = @"class Program -{ - void M() - { - CCC c = new CCC(); - c.Index$$Prop[0] = ""s""; - } -}"; + var markup = """ + class Program + { + void M() + { + CCC c = new CCC(); + c.Index$$Prop[0] = "s"; + } + } + """; // Note that is required by compiler. Bug 17013 tracks enabling indexed property for non-COM types. - var referencedCode = @"Imports System.Runtime.InteropServices - - -Public Class CCC - -#Region ""COM GUIDs"" - Public Const ClassId As String = ""9d965fd2-1514-44f6-accd-257ce77c46b0"" - Public Const InterfaceId As String = ""a9415060-fdf0-47e3-bc80-9c18f7f39cf6"" - Public Const EventsId As String = ""c6a866a5-5f97-4b53-a5df-3739dc8ff1bb"" -# End Region - - ''' - ''' An index property from VB - ''' - ''' p1 is an integer index - ''' A string - Public Property IndexProp(ByVal p1 As Integer, Optional ByVal p2 As Integer = 0) As String - Get - Return Nothing - End Get - Set(ByVal value As String) - - End Set - End Property -End Class"; + var referencedCode = """ + Imports System.Runtime.InteropServices + + + Public Class CCC + + #Region "COM GUIDs" + Public Const ClassId As String = "9d965fd2-1514-44f6-accd-257ce77c46b0" + Public Const InterfaceId As String = "a9415060-fdf0-47e3-bc80-9c18f7f39cf6" + Public Const EventsId As String = "c6a866a5-5f97-4b53-a5df-3739dc8ff1bb" + # End Region + + ''' + ''' An index property from VB + ''' + ''' p1 is an integer index + ''' A string + Public Property IndexProp(ByVal p1 As Integer, Optional ByVal p2 As Integer = 0) As String + Get + Return Nothing + End Get + Set(ByVal value As String) + + End Set + End Property + End Class + """; await TestWithReferenceAsync(sourceCode: markup, referencedCode: referencedCode, @@ -4953,20 +5591,22 @@ await TestWithReferenceAsync(sourceCode: markup, public async Task TestUnconstructedGeneric() { await TestAsync( -@"class A -{ - enum SortOrder - { - Ascending, - Descending, - None - } + """ + class A + { + enum SortOrder + { + Ascending, + Descending, + None + } - void Goo() - { - var b = $$SortOrder.Ascending; - } -}", + void Goo() + { + var b = $$SortOrder.Ascending; + } + } + """, MainDescription(@"enum A.SortOrder")); } @@ -4974,24 +5614,28 @@ void Goo() public async Task TestUnconstructedGenericInCRef() { await TestAsync( -@"/// -class C -{ -}", + """ + /// + class C + { + } + """, MainDescription(@"class C")); } [Fact] public async Task TestAwaitableMethod() { - var markup = @"using System.Threading.Tasks; -class C -{ - async Task Goo() - { - Go$$o(); - } -}"; + var markup = """ + using System.Threading.Tasks; + class C + { + async Task Goo() + { + Go$$o(); + } + } + """; var description = $"({CSharpFeaturesResources.awaitable}) Task C.Goo()"; await VerifyWithMscorlib45Async(markup, MainDescription(description)); @@ -5000,37 +5644,41 @@ async Task Goo() [Fact] public async Task ObsoleteItem() { - var markup = @" -using System; + var markup = """ -class Program -{ - [Obsolete] - public void goo() - { - go$$o(); - } -}"; + using System; + + class Program + { + [Obsolete] + public void goo() + { + go$$o(); + } + } + """; await TestAsync(markup, MainDescription($"[{CSharpFeaturesResources.deprecated}] void Program.goo()")); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/751070")] public async Task DynamicOperator() { - var markup = @" + var markup = """ -public class Test -{ - public delegate void NoParam(); - static int Main() - { - dynamic x = new object(); - if (((System.Func)(() => (x =$$= null)))()) - return 0; - return 1; - } -}"; + public class Test + { + public delegate void NoParam(); + + static int Main() + { + dynamic x = new object(); + if (((System.Func)(() => (x =$$= null)))()) + return 0; + return 1; + } + } + """; await TestAsync(markup, MainDescription("dynamic dynamic.operator ==(dynamic left, dynamic right)")); } @@ -5038,154 +5686,180 @@ static int Main() public async Task TextOnlyDocComment() { await TestAsync( -@"/// -///goo -/// -class C$$ -{ -}", Documentation("goo")); + """ + /// + ///goo + /// + class C$$ + { + } + """, Documentation("goo")); } [Fact] public async Task TestTrimConcatMultiLine() { await TestAsync( -@"/// -/// goo -/// bar -/// -class C$$ -{ -}", Documentation("goo bar")); + """ + /// + /// goo + /// bar + /// + class C$$ + { + } + """, Documentation("goo bar")); } [Fact] public async Task TestCref() { await TestAsync( -@"/// -/// -/// -/// -class C$$ -{ -}", Documentation("C C")); + """ + /// + /// + /// + /// + class C$$ + { + } + """, Documentation("C C")); } [Fact] public async Task ExcludeTextOutsideSummaryBlock() { await TestAsync( -@"/// red -/// -/// green -/// -/// yellow -class C$$ -{ -}", Documentation("green")); + """ + /// red + /// + /// green + /// + /// yellow + class C$$ + { + } + """, Documentation("green")); } [Fact] public async Task NewlineAfterPara() { await TestAsync( -@"/// -/// goo -/// -class C$$ -{ -}", Documentation("goo")); + """ + /// + /// goo + /// + class C$$ + { + } + """, Documentation("goo")); } [Fact] public async Task TextOnlyDocComment_Metadata() { - var referenced = @" -/// -///goo -/// -public class C -{ -}"; + var referenced = """ - var code = @" -class G -{ - void goo() - { - C$$ c; - } -}"; + /// + ///goo + /// + public class C + { + } + """; + + var code = """ + + class G + { + void goo() + { + C$$ c; + } + } + """; await TestWithMetadataReferenceHelperAsync(code, referenced, "C#", "C#", Documentation("goo")); } [Fact] public async Task TestTrimConcatMultiLine_Metadata() { - var referenced = @" -/// -/// goo -/// bar -/// -public class C -{ -}"; + var referenced = """ - var code = @" -class G -{ - void goo() - { - C$$ c; - } -}"; + /// + /// goo + /// bar + /// + public class C + { + } + """; + + var code = """ + + class G + { + void goo() + { + C$$ c; + } + } + """; await TestWithMetadataReferenceHelperAsync(code, referenced, "C#", "C#", Documentation("goo bar")); } [Fact] public async Task TestCref_Metadata() { - var code = @" -class G -{ - void goo() - { - C$$ c; - } -}"; + var code = """ - var referenced = @"/// -/// -/// -/// -public class C -{ -}"; + class G + { + void goo() + { + C$$ c; + } + } + """; + + var referenced = """ + /// + /// + /// + /// + public class C + { + } + """; await TestWithMetadataReferenceHelperAsync(code, referenced, "C#", "C#", Documentation("C C")); } [Fact] public async Task ExcludeTextOutsideSummaryBlock_Metadata() { - var code = @" -class G -{ - void goo() - { - C$$ c; - } -}"; + var code = """ - var referenced = @" -/// red -/// -/// green -/// -/// yellow -public class C -{ -}"; + class G + { + void goo() + { + C$$ c; + } + } + """; + + var referenced = """ + + /// red + /// + /// green + /// + /// yellow + public class C + { + } + """; await TestWithMetadataReferenceHelperAsync(code, referenced, "C#", "C#", Documentation("green")); } @@ -5193,83 +5867,95 @@ public class C public async Task Param() { await TestAsync( -@"/// -public class C -{ - /// A type parameter of - /// First parameter of - /// Another parameter of - public void Goo(string[] arg$$s, T otherParam) - { - } -}", Documentation("First parameter of C.Goo(string[], T)")); + """ + /// + public class C + { + /// A type parameter of + /// First parameter of + /// Another parameter of + public void Goo(string[] arg$$s, T otherParam) + { + } + } + """, Documentation("First parameter of C.Goo(string[], T)")); } [Fact] public async Task Param_Metadata() { - var code = @" -class G -{ - void goo() - { - C c; - c.Goo(arg$$s: new string[] { }, 1); - } -}"; - var referenced = @" -/// -public class C -{ - /// A type parameter of - /// First parameter of - /// Another parameter of - public void Goo(string[] args, T otherParam) - { - } -}"; - await TestWithMetadataReferenceHelperAsync(code, referenced, "C#", "C#", Documentation("First parameter of C.Goo(string[], T)")); - } + var code = """ - [Fact] - public async Task Param2() - { - await TestAsync( -@"/// -public class C -{ - /// A type parameter of - /// First parameter of - /// Another parameter of - public void Goo(string[] args, T oth$$erParam) - { - } -}", Documentation("Another parameter of C.Goo(string[], T)")); + class G + { + void goo() + { + C c; + c.Goo(arg$$s: new string[] { }, 1); + } + } + """; + var referenced = """ + + /// + public class C + { + /// A type parameter of + /// First parameter of + /// Another parameter of + public void Goo(string[] args, T otherParam) + { + } + } + """; + await TestWithMetadataReferenceHelperAsync(code, referenced, "C#", "C#", Documentation("First parameter of C.Goo(string[], T)")); } [Fact] - public async Task Param2_Metadata() - { - var code = @" -class G -{ - void goo() + public async Task Param2() { - C c; - c.Goo(args: new string[] { }, other$$Param: 1); + await TestAsync( + """ + /// + public class C + { + /// A type parameter of + /// First parameter of + /// Another parameter of + public void Goo(string[] args, T oth$$erParam) + { + } + } + """, Documentation("Another parameter of C.Goo(string[], T)")); } -}"; - var referenced = @" -/// -public class C -{ - /// A type parameter of - /// First parameter of - /// Another parameter of - public void Goo(string[] args, T otherParam) + + [Fact] + public async Task Param2_Metadata() { - } -}"; + var code = """ + + class G + { + void goo() + { + C c; + c.Goo(args: new string[] { }, other$$Param: 1); + } + } + """; + var referenced = """ + + /// + public class C + { + /// A type parameter of + /// First parameter of + /// Another parameter of + public void Goo(string[] args, T otherParam) + { + } + } + """; await TestWithMetadataReferenceHelperAsync(code, referenced, "C#", "C#", Documentation("Another parameter of C.Goo(string[], T)")); } @@ -5277,173 +5963,199 @@ public void Goo(string[] args, T otherParam) public async Task TypeParam() { await TestAsync( -@"/// -public class C -{ - /// A type parameter of - /// First parameter of - /// Another parameter of - public void Goo(string[] args, T otherParam) - { - } -}", Documentation("A type parameter of C.Goo(string[], T)")); + """ + /// + public class C + { + /// A type parameter of + /// First parameter of + /// Another parameter of + public void Goo(string[] args, T otherParam) + { + } + } + """, Documentation("A type parameter of C.Goo(string[], T)")); } [Fact] public async Task UnboundCref() { await TestAsync( -@"/// -public class C -{ - /// A type parameter of - /// First parameter of - /// Another parameter of - public void Goo(string[] args, T otherParam) - { - } -}", Documentation("A type parameter of goo(string[], T)")); + """ + /// + public class C + { + /// A type parameter of + /// First parameter of + /// Another parameter of + public void Goo(string[] args, T otherParam) + { + } + } + """, Documentation("A type parameter of goo(string[], T)")); } [Fact] public async Task CrefInConstructor() { await TestAsync( -@"public class TestClass -{ - /// - /// This sample shows how to specify the constructor as a cref attribute. - /// - public TestClass$$() - { - } -}", Documentation("This sample shows how to specify the TestClass constructor as a cref attribute.")); + """ + public class TestClass + { + /// + /// This sample shows how to specify the constructor as a cref attribute. + /// + public TestClass$$() + { + } + } + """, Documentation("This sample shows how to specify the TestClass constructor as a cref attribute.")); } [Fact] public async Task CrefInConstructorOverloaded() { await TestAsync( -@"public class TestClass -{ - /// - /// This sample shows how to specify the constructor as a cref attribute. - /// - public TestClass() - { - } + """ + public class TestClass + { + /// + /// This sample shows how to specify the constructor as a cref attribute. + /// + public TestClass() + { + } - /// - /// This sample shows how to specify the constructor as a cref attribute. - /// - public TestC$$lass(int value) - { - } -}", Documentation("This sample shows how to specify the TestClass(int) constructor as a cref attribute.")); + /// + /// This sample shows how to specify the constructor as a cref attribute. + /// + public TestC$$lass(int value) + { + } + } + """, Documentation("This sample shows how to specify the TestClass(int) constructor as a cref attribute.")); } [Fact] public async Task CrefInGenericMethod1() { await TestAsync( -@"public class TestClass -{ - /// - /// The GetGenericValue method. - /// This sample shows how to specify the method as a cref attribute. - /// - public static T GetGenericVa$$lue(T para) - { - return para; - } -}", Documentation("The GetGenericValue method.\r\n\r\nThis sample shows how to specify the TestClass.GetGenericValue(T) method as a cref attribute.")); + """ + public class TestClass + { + /// + /// The GetGenericValue method. + /// This sample shows how to specify the method as a cref attribute. + /// + public static T GetGenericVa$$lue(T para) + { + return para; + } + } + """, Documentation(""" + The GetGenericValue method. + + This sample shows how to specify the TestClass.GetGenericValue(T) method as a cref attribute. + """)); } [Fact] public async Task CrefInGenericMethod2() { await TestAsync( -@"public class TestClass -{ - /// - /// The GetGenericValue method. - /// This sample shows how to specify the method as a cref attribute. - /// - public static T GetGenericVa$$lue(T para) - { - return para; - } -}", Documentation("The GetGenericValue method.\r\n\r\nThis sample shows how to specify the TestClass.GetGenericValue(T) method as a cref attribute.")); + """ + public class TestClass + { + /// + /// The GetGenericValue method. + /// This sample shows how to specify the method as a cref attribute. + /// + public static T GetGenericVa$$lue(T para) + { + return para; + } + } + """, Documentation(""" + The GetGenericValue method. + + This sample shows how to specify the TestClass.GetGenericValue(T) method as a cref attribute. + """)); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/813350")] public async Task CrefInMethodOverloading1() { await TestAsync( -@"public class TestClass -{ - public static int GetZero() - { - GetGenericValu$$e(); - GetGenericValue(5); - } + """ + public class TestClass + { + public static int GetZero() + { + GetGenericValu$$e(); + GetGenericValue(5); + } - /// - /// This sample shows how to call the method - /// - public static T GetGenericValue(T para) - { - return para; - } + /// + /// This sample shows how to call the method + /// + public static T GetGenericValue(T para) + { + return para; + } - /// - /// This sample shows how to specify the method as a cref attribute. - /// - public static void GetGenericValue() - { - } -}", Documentation("This sample shows how to specify the TestClass.GetGenericValue() method as a cref attribute.")); + /// + /// This sample shows how to specify the method as a cref attribute. + /// + public static void GetGenericValue() + { + } + } + """, Documentation("This sample shows how to specify the TestClass.GetGenericValue() method as a cref attribute.")); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/813350")] public async Task CrefInMethodOverloading2() { await TestAsync( -@"public class TestClass -{ - public static int GetZero() - { - GetGenericValue(); - GetGenericVal$$ue(5); - } + """ + public class TestClass + { + public static int GetZero() + { + GetGenericValue(); + GetGenericVal$$ue(5); + } - /// - /// This sample shows how to call the method - /// - public static T GetGenericValue(T para) - { - return para; - } + /// + /// This sample shows how to call the method + /// + public static T GetGenericValue(T para) + { + return para; + } - /// - /// This sample shows how to specify the method as a cref attribute. - /// - public static void GetGenericValue() - { - } -}", Documentation("This sample shows how to call the TestClass.GetGenericValue(T) method")); + /// + /// This sample shows how to specify the method as a cref attribute. + /// + public static void GetGenericValue() + { + } + } + """, Documentation("This sample shows how to call the TestClass.GetGenericValue(T) method")); } [Fact] public async Task CrefInGenericType() { await TestAsync( -@"/// -/// This example shows how to specify the cref. -/// -class Generic$$Class -{ -}", + """ + /// + /// This example shows how to specify the cref. + /// + class Generic$$Class + { + } + """, Documentation("This example shows how to specify the GenericClass cref.", ExpectedClassifications( Text("This example shows how to specify the"), @@ -5459,26 +6171,30 @@ class Generic$$Class [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/812720")] public async Task ClassificationOfCrefsFromMetadata() { - var code = @" -class G -{ - void goo() - { - C c; - c.Go$$o(); - } -}"; - var referenced = @" -/// -public class C -{ - /// - /// See method - /// - public void Goo() - { - } -}"; + var code = """ + + class G + { + void goo() + { + C c; + c.Go$$o(); + } + } + """; + var referenced = """ + + /// + public class C + { + /// + /// See method + /// + public void Goo() + { + } + } + """; await TestWithMetadataReferenceHelperAsync(code, referenced, "C#", "C#", Documentation("See C.Goo() method", ExpectedClassifications( @@ -5496,24 +6212,26 @@ await TestWithMetadataReferenceHelperAsync(code, referenced, "C#", "C#", [Fact] public async Task FieldAvailableInBothLinkedFiles() { - var markup = @" - - - - - - - -"; + var markup = """ + + + + + + + + + + """; await VerifyWithReferenceWorkerAsync(markup, new[] { MainDescription($"({FeaturesResources.field}) int C.x"), Usage("") }); } @@ -5521,27 +6239,35 @@ void goo() [Fact] public async Task FieldUnavailableInOneLinkedFile() { - var markup = @" - - - - - - - -"; - var expectedDescription = Usage($"\r\n{string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Available)}\r\n{string.Format(FeaturesResources._0_1, "Proj2", FeaturesResources.Not_Available)}\r\n\r\n{FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts}", expectsWarningGlyph: true); + var markup = """ + + + + + + + + + + """; + var expectedDescription = Usage($""" + + {string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Available)} + {string.Format(FeaturesResources._0_1, "Proj2", FeaturesResources.Not_Available)} + + {FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts} + """, expectsWarningGlyph: true); await VerifyWithReferenceWorkerAsync(markup, new[] { expectedDescription }); } @@ -5549,27 +6275,35 @@ void goo() [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/37097")] public async Task BindSymbolInOtherFile() { - var markup = @" - - - - - - - -"; - var expectedDescription = Usage($"\r\n{string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Not_Available)}\r\n{string.Format(FeaturesResources._0_1, "Proj2", FeaturesResources.Available)}\r\n\r\n{FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts}", expectsWarningGlyph: true); + var markup = """ + + + + + + + + + + """; + var expectedDescription = Usage($""" + + {string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Not_Available)} + {string.Format(FeaturesResources._0_1, "Proj2", FeaturesResources.Available)} + + {FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts} + """, expectsWarningGlyph: true); await VerifyWithReferenceWorkerAsync(markup, new[] { expectedDescription }); } @@ -5577,31 +6311,40 @@ void goo() [Fact] public async Task FieldUnavailableInTwoLinkedFiles() { - var markup = @" - - - - - - - - - - -"; + var markup = """ + + + + + + + + + + + + + """; var expectedDescription = Usage( - $"\r\n{string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Available)}\r\n{string.Format(FeaturesResources._0_1, "Proj2", FeaturesResources.Not_Available)}\r\n{string.Format(FeaturesResources._0_1, "Proj3", FeaturesResources.Not_Available)}\r\n\r\n{FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts}", + $""" + + {string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Available)} + {string.Format(FeaturesResources._0_1, "Proj2", FeaturesResources.Not_Available)} + {string.Format(FeaturesResources._0_1, "Proj3", FeaturesResources.Not_Available)} + + {FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts} + """, expectsWarningGlyph: true); await VerifyWithReferenceWorkerAsync(markup, new[] { expectedDescription }); @@ -5610,83 +6353,95 @@ void goo() [Fact] public async Task ExcludeFilesWithInactiveRegions() { - var markup = @" - - + + - - - - - - - - -"; - var expectedDescription = Usage($"\r\n{string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Available)}\r\n{string.Format(FeaturesResources._0_1, "Proj3", FeaturesResources.Not_Available)}\r\n\r\n{FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts}", expectsWarningGlyph: true); + #if BAR + void goo() + { + x$$ + } + #endif + } + ]]> + + + + + + + + + + """; + var expectedDescription = Usage($""" + + {string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Available)} + {string.Format(FeaturesResources._0_1, "Proj3", FeaturesResources.Not_Available)} + + {FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts} + """, expectsWarningGlyph: true); await VerifyWithReferenceWorkerAsync(markup, new[] { expectedDescription }); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/962353")] public async Task NoValidSymbolsInLinkedDocuments() { - var markup = @" - - - - - - - -"; - await VerifyWithReferenceWorkerAsync(markup); + var markup = """ + + + + + + + + + + """; + await VerifyWithReferenceWorkerAsync(markup); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1020944")] public async Task LocalsValidInLinkedDocuments() { - var markup = @" - - - - - - - -"; + var markup = """ + + + + + + + + + + """; await VerifyWithReferenceWorkerAsync(markup, new[] { MainDescription($"({FeaturesResources.local_variable}) int x"), Usage("") }); } @@ -5694,51 +6449,61 @@ void M() [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1020944")] public async Task LocalWarningInLinkedDocuments() { - var markup = @" - - + + - - - - - -"; + int y = x$$; + } + } + ]]> + + + + + + + """; + + await VerifyWithReferenceWorkerAsync(markup, new[] { MainDescription($"({FeaturesResources.local_variable}) int x"), Usage($""" + + {string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Available)} + {string.Format(FeaturesResources._0_1, "Proj2", FeaturesResources.Not_Available)} - await VerifyWithReferenceWorkerAsync(markup, new[] { MainDescription($"({FeaturesResources.local_variable}) int x"), Usage($"\r\n{string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Available)}\r\n{string.Format(FeaturesResources._0_1, "Proj2", FeaturesResources.Not_Available)}\r\n\r\n{FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts}", expectsWarningGlyph: true) }); + {FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts} + """, expectsWarningGlyph: true) }); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1020944")] public async Task LabelsValidInLinkedDocuments() { - var markup = @" - - - - - - - -"; + var markup = """ + + + + + + + + + + """; await VerifyWithReferenceWorkerAsync(markup, new[] { MainDescription($"({FeaturesResources.label}) LABEL"), Usage("") }); } @@ -5746,24 +6511,26 @@ void M() [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1020944")] public async Task RangeVariablesValidInLinkedDocuments() { - var markup = @" - - - - - - - -"; + var markup = """ + + + + + + + + + + """; await VerifyWithReferenceWorkerAsync(markup, new[] { MainDescription($"({FeaturesResources.range_variable}) int y"), Usage("") }); } @@ -5771,76 +6538,84 @@ void M() [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1019766")] public async Task PointerAccessibility() { - var markup = @"class C -{ - unsafe static void Main() - { - void* p = null; - void* q = null; - dynamic d = true; - var x = p =$$= q == d; - } -}"; + var markup = """ + class C + { + unsafe static void Main() + { + void* p = null; + void* q = null; + dynamic d = true; + var x = p =$$= q == d; + } + } + """; await TestAsync(markup, MainDescription("bool void*.operator ==(void* left, void* right)")); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1114300")] public async Task AwaitingTaskOfArrayType() { - var markup = @" -using System.Threading.Tasks; + var markup = """ -class Program -{ - async Task M() - { - awa$$it M(); - } -}"; + using System.Threading.Tasks; + + class Program + { + async Task M() + { + awa$$it M(); + } + } + """; await TestAsync(markup, MainDescription(string.Format(FeaturesResources.Awaited_task_returns_0, "int[]"))); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1114300")] public async Task AwaitingTaskOfDynamic() { - var markup = @" -using System.Threading.Tasks; + var markup = """ -class Program -{ - async Task M() - { - awa$$it M(); - } -}"; + using System.Threading.Tasks; + + class Program + { + async Task M() + { + awa$$it M(); + } + } + """; await TestAsync(markup, MainDescription(string.Format(FeaturesResources.Awaited_task_returns_0, "dynamic"))); } [Fact, Trait(Traits.Feature, Traits.Features.Completion)] public async Task MethodOverloadDifferencesIgnored() { - var markup = @" - - + + - - - - -"; + }]]> + + + + + + """; var expectedDescription = $"void C.Do(int x)"; await VerifyWithReferenceWorkerAsync(markup, MainDescription(expectedDescription)); @@ -5849,51 +6624,53 @@ void Shared() [Fact, Trait(Traits.Feature, Traits.Features.Completion)] public async Task MethodOverloadDifferencesIgnored_ContainingType() { - var markup = @" - - + + - - - - - -"; + #if TWO + public class Methods2 + { + public void Do(string x) { } + } + #endif + ]]> + + + + + + + """; var expectedDescription = $"void Methods1.Do(string x)"; await VerifyWithReferenceWorkerAsync(markup, MainDescription(expectedDescription)); @@ -5903,408 +6680,507 @@ public void Do(string x) { } public async Task QuickInfoExceptions() { await TestAsync( -@"using System; + """ + using System; -namespace MyNs -{ - class MyException1 : Exception - { - } + namespace MyNs + { + class MyException1 : Exception + { + } - class MyException2 : Exception - { - } + class MyException2 : Exception + { + } - class TestClass - { - /// - /// - /// - /// - /// - void M() - { - M$$(); - } - } -}", - Exceptions($"\r\n{WorkspacesResources.Exceptions_colon}\r\n MyException1\r\n MyException2\r\n int\r\n double\r\n Not_A_Class_But_Still_Displayed")); + class TestClass + { + /// + /// + /// + /// + /// + void M() + { + M$$(); + } + } + } + """, + Exceptions($""" + + {WorkspacesResources.Exceptions_colon} + MyException1 + MyException2 + int + double + Not_A_Class_But_Still_Displayed + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23307")] public async Task QuickInfoCapturesOnLocalFunction() { - await TestAsync(@" -class C -{ - void M() - { - int i; - local$$(); + await TestAsync(""" - void local() { i++; this.M(); } - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} this, i")); + class C + { + void M() + { + int i; + local$$(); + + void local() { i++; this.M(); } + } + } + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} this, i + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23307")] public async Task QuickInfoCapturesOnLocalFunction2() { - await TestAsync(@" -class C -{ - void M() - { - int i; - local$$(i); + await TestAsync(""" - void local(int j) { j++; M(); } - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} this")); + class C + { + void M() + { + int i; + local$$(i); + + void local(int j) { j++; M(); } + } + } + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} this + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23307")] public async Task QuickInfoCapturesOnLocalFunction3() { - await TestAsync(@" -class C -{ - public void M(int @this) - { - int i = 0; - local$$(); + await TestAsync(""" - void local() - { - M(1); - i++; - @this++; - } - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} this, @this, i")); + class C + { + public void M(int @this) + { + int i = 0; + local$$(); + + void local() + { + M(1); + i++; + @this++; + } + } + } + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} this, @this, i + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26101")] public async Task QuickInfoCapturesOnLocalFunction4() { - await TestAsync(@" -class C -{ - int field; - void M() - { - void OuterLocalFunction$$() - { - int local = 0; - int InnerLocalFunction() + await TestAsync(""" + + class C { - field++; - return local; + int field; + void M() + { + void OuterLocalFunction$$() + { + int local = 0; + int InnerLocalFunction() + { + field++; + return local; + } + } + } } - } - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} this")); + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} this + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26101")] public async Task QuickInfoCapturesOnLocalFunction5() { - await TestAsync(@" -class C -{ - int field; - void M() - { - void OuterLocalFunction() - { - int local = 0; - int InnerLocalFunction$$() + await TestAsync(""" + + class C { - field++; - return local; + int field; + void M() + { + void OuterLocalFunction() + { + int local = 0; + int InnerLocalFunction$$() + { + field++; + return local; + } + } + } } - } - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} this, local")); + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} this, local + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26101")] public async Task QuickInfoCapturesOnLocalFunction6() { - await TestAsync(@" -class C -{ - int field; - void M() - { - int local1 = 0; - int local2 = 0; + await TestAsync(""" - void OuterLocalFunction$$() - { - _ = local1; - void InnerLocalFunction() + class C { - _ = local2; + int field; + void M() + { + int local1 = 0; + int local2 = 0; + + void OuterLocalFunction$$() + { + _ = local1; + void InnerLocalFunction() + { + _ = local2; + } + } + } } - } - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} local1, local2")); + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} local1, local2 + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26101")] public async Task QuickInfoCapturesOnLocalFunction7() { - await TestAsync(@" -class C -{ - int field; - void M() - { - int local1 = 0; - int local2 = 0; + await TestAsync(""" - void OuterLocalFunction() - { - _ = local1; - void InnerLocalFunction$$() + class C { - _ = local2; + int field; + void M() + { + int local1 = 0; + int local2 = 0; + + void OuterLocalFunction() + { + _ = local1; + void InnerLocalFunction$$() + { + _ = local2; + } + } + } } - } - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} local2")); + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} local2 + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23307")] public async Task QuickInfoCapturesOnLambda() { - await TestAsync(@" -class C -{ - void M() - { - int i; - System.Action a = () =$$> { i++; M(); }; - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} this, i")); + await TestAsync(""" + + class C + { + void M() + { + int i; + System.Action a = () =$$> { i++; M(); }; + } + } + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} this, i + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23307")] public async Task QuickInfoCapturesOnLambda2() { - await TestAsync(@" -class C -{ - void M() - { - int i; - System.Action a = j =$$> { i++; j++; M(); }; - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} this, i")); + await TestAsync(""" + + class C + { + void M() + { + int i; + System.Action a = j =$$> { i++; j++; M(); }; + } + } + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} this, i + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23307")] public async Task QuickInfoCapturesOnLambda2_DifferentOrder() { - await TestAsync(@" -class C -{ - void M(int j) - { - int i; - System.Action a = () =$$> { M(); i++; j++; }; - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} this, j, i")); + await TestAsync(""" + + class C + { + void M(int j) + { + int i; + System.Action a = () =$$> { M(); i++; j++; }; + } + } + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} this, j, i + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23307")] public async Task QuickInfoCapturesOnLambda3() { - await TestAsync(@" -class C -{ - void M() - { - int i; - int @this; - N(() =$$> { M(); @this++; }, () => { i++; }); - } - void N(System.Action x, System.Action y) { } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} this, @this")); + await TestAsync(""" + + class C + { + void M() + { + int i; + int @this; + N(() =$$> { M(); @this++; }, () => { i++; }); + } + void N(System.Action x, System.Action y) { } + } + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} this, @this + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23307")] public async Task QuickInfoCapturesOnLambda4() { - await TestAsync(@" -class C -{ - void M() - { - int i; - N(() => { M(); }, () =$$> { i++; }); - } - void N(System.Action x, System.Action y) { } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} i")); + await TestAsync(""" + + class C + { + void M() + { + int i; + N(() => { M(); }, () =$$> { i++; }); + } + void N(System.Action x, System.Action y) { } + } + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} i + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26101")] public async Task QuickInfoCapturesOnLambda5() { - await TestAsync(@" -class C -{ - int field; - void M() - { - System.Action a = () =$$> - { - int local = 0; - System.Func b = () => + await TestAsync(""" + + class C { - field++; - return local; - }; - }; - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} this")); + int field; + void M() + { + System.Action a = () =$$> + { + int local = 0; + System.Func b = () => + { + field++; + return local; + }; + }; + } + } + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} this + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26101")] public async Task QuickInfoCapturesOnLambda6() { - await TestAsync(@" -class C -{ - int field; - void M() - { - System.Action a = () => - { - int local = 0; - System.Func b = () =$$> + await TestAsync(""" + + class C { - field++; - return local; - }; - }; - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} this, local")); + int field; + void M() + { + System.Action a = () => + { + int local = 0; + System.Func b = () =$$> + { + field++; + return local; + }; + }; + } + } + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} this, local + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26101")] public async Task QuickInfoCapturesOnLambda7() { - await TestAsync(@" -class C -{ - int field; - void M() - { - int local1 = 0; - int local2 = 0; + await TestAsync(""" - System.Action a = () =$$> - { - _ = local1; - System.Action b = () => + class C { - _ = local2; - }; - }; - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} local1, local2")); + int field; + void M() + { + int local1 = 0; + int local2 = 0; + + System.Action a = () =$$> + { + _ = local1; + System.Action b = () => + { + _ = local2; + }; + }; + } + } + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} local1, local2 + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26101")] public async Task QuickInfoCapturesOnLambda8() { - await TestAsync(@" -class C -{ - int field; - void M() - { - int local1 = 0; - int local2 = 0; + await TestAsync(""" - System.Action a = () => - { - _ = local1; - System.Action b = () =$$> + class C { - _ = local2; - }; - }; - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} local2")); + int field; + void M() + { + int local1 = 0; + int local2 = 0; + + System.Action a = () => + { + _ = local1; + System.Action b = () =$$> + { + _ = local2; + }; + }; + } + } + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} local2 + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23307")] public async Task QuickInfoCapturesOnDelegate() { - await TestAsync(@" -class C -{ - void M() - { - int i; - System.Func f = dele$$gate(bool b) { i++; return 1; }; - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} i")); + await TestAsync(""" + + class C + { + void M() + { + int i; + System.Func f = dele$$gate(bool b) { i++; return 1; }; + } + } + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} i + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/1516")] public async Task QuickInfoWithNonStandardSeeAttributesAppear() { await TestAsync( -@"class C -{ - /// - /// - /// - /// - /// - /// - void M() - { - M$$(); - } -}", + """ + class C + { + /// + /// + /// + /// + /// + /// + void M() + { + M$$(); + } + } + """, Documentation(@"string http://microsoft.com null cat")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/6657")] public async Task OptionalParameterFromPreviousSubmission() { - const string workspaceDefinition = @" - - - void M(int x = 1) { } - - - M(x$$: 2) - - -"; + const string workspaceDefinition = """ + + + + void M(int x = 1) { } + + + M(x$$: 2) + + + + """; using var workspace = EditorTestWorkspace.Create(XElement.Parse(workspaceDefinition), workspaceKind: WorkspaceKind.Interactive); await TestWithOptionsAsync(workspace, MainDescription($"({FeaturesResources.parameter}) int x = 1")); } @@ -6313,25 +7189,27 @@ void M(int x = 1) { } public async Task TupleProperty() { await TestInMethodAsync( -@"interface I -{ - (int, int) Name { get; set; } -} + """ + interface I + { + (int, int) Name { get; set; } + } -class C : I -{ - (int, int) I.Name$$ - { - get - { - throw new System.Exception(); - } + class C : I + { + (int, int) I.Name$$ + { + get + { + throw new System.Exception(); + } - set - { - } - } -}", + set + { + } + } + } + """, MainDescription("(int, int) C.Name { get; set; }")); } @@ -6339,16 +7217,18 @@ class C : I public async Task ValueTupleWithArity0VariableName() { await TestAsync( -@" -using System; -public class C -{ - void M() - { - var y$$ = ValueTuple.Create(); - } -} -", + """ + + using System; + public class C + { + void M() + { + var y$$ = ValueTuple.Create(); + } + } + + """, MainDescription($"({FeaturesResources.local_variable}) ValueTuple y")); } @@ -6356,16 +7236,18 @@ void M() public async Task ValueTupleWithArity0ImplicitVar() { await TestAsync( -@" -using System; -public class C -{ - void M() - { - var$$ y = ValueTuple.Create(); - } -} -", + """ + + using System; + public class C + { + void M() + { + var$$ y = ValueTuple.Create(); + } + } + + """, MainDescription("struct System.ValueTuple")); } @@ -6373,16 +7255,18 @@ void M() public async Task ValueTupleWithArity1VariableName() { await TestAsync( -@" -using System; -public class C -{ - void M() - { - var y$$ = ValueTuple.Create(1); - } -} -", + """ + + using System; + public class C + { + void M() + { + var y$$ = ValueTuple.Create(1); + } + } + + """, MainDescription($"({FeaturesResources.local_variable}) ValueTuple y")); } @@ -6390,16 +7274,18 @@ void M() public async Task ValueTupleWithArity1ImplicitVar() { await TestAsync( -@" -using System; -public class C -{ - void M() - { - var$$ y = ValueTuple.Create(1); - } -} -", + """ + + using System; + public class C + { + void M() + { + var$$ y = ValueTuple.Create(1); + } + } + + """, MainDescription("struct System.ValueTuple")); } @@ -6407,16 +7293,18 @@ void M() public async Task ValueTupleWithArity2VariableName() { await TestAsync( -@" -using System; -public class C -{ - void M() - { - var y$$ = ValueTuple.Create(1, 1); - } -} -", + """ + + using System; + public class C + { + void M() + { + var y$$ = ValueTuple.Create(1, 1); + } + } + + """, MainDescription($"({FeaturesResources.local_variable}) (int, int) y")); } @@ -6424,16 +7312,18 @@ void M() public async Task ValueTupleWithArity2ImplicitVar() { await TestAsync( -@" -using System; -public class C -{ - void M() - { - var$$ y = ValueTuple.Create(1, 1); - } -} -", + """ + + using System; + public class C + { + void M() + { + var$$ y = ValueTuple.Create(1, 1); + } + } + + """, MainDescription("(int, int)")); } @@ -6441,20 +7331,22 @@ void M() public async Task TestRefMethod() { await TestInMethodAsync( -@"using System; + """ + using System; -class Program -{ - static void Main(string[] args) - { - ref int i = ref $$goo(); - } + class Program + { + static void Main(string[] args) + { + ref int i = ref $$goo(); + } - private static ref int goo() - { - throw new NotImplementedException(); - } -}", + private static ref int goo() + { + throw new NotImplementedException(); + } + } + """, MainDescription("ref int Program.goo()")); } @@ -6462,20 +7354,22 @@ private static ref int goo() public async Task TestRefLocal() { await TestInMethodAsync( -@"using System; + """ + using System; -class Program -{ - static void Main(string[] args) - { - ref int $$i = ref goo(); - } + class Program + { + static void Main(string[] args) + { + ref int $$i = ref goo(); + } - private static ref int goo() - { - throw new NotImplementedException(); - } -}", + private static ref int goo() + { + throw new NotImplementedException(); + } + } + """, MainDescription($"({FeaturesResources.local_variable}) ref int i")); } @@ -6483,21 +7377,23 @@ private static ref int goo() public async Task TestGenericMethodInDocComment() { await TestAsync( -@" -class Test -{ - T F() - { - F(); - } + """ - /// - /// - /// - void S() - { } -} -", + class Test + { + T F() + { + F(); + } + + /// + /// + /// + void S() + { } + } + + """, MainDescription("T Test.F()")); } @@ -6505,15 +7401,17 @@ void S() public async Task TestExceptionWithCrefToConstructorDoesNotCrash() { await TestAsync( -@" -class Test -{ - /// - /// - /// - public Test$$() {} -} -", + """ + + class Test + { + /// + /// + /// + public Test$$() {} + } + + """, MainDescription("Test.Test()")); } @@ -6527,11 +7425,13 @@ public async Task TestRefStruct() [Fact] public async Task TestRefStruct_Nested() { - var markup = @" -namespace Nested -{ - ref struct X$$ {} -}"; + var markup = """ + + namespace Nested + { + ref struct X$$ {} + } + """; await TestAsync(markup, MainDescription("ref struct Nested.X")); } @@ -6545,11 +7445,13 @@ public async Task TestReadOnlyStruct() [Fact] public async Task TestReadOnlyStruct_Nested() { - var markup = @" -namespace Nested -{ - readonly struct X$$ {} -}"; + var markup = """ + + namespace Nested + { + readonly struct X$$ {} + } + """; await TestAsync(markup, MainDescription("readonly struct Nested.X")); } @@ -6563,35 +7465,39 @@ public async Task TestReadOnlyRefStruct() [Fact] public async Task TestReadOnlyRefStruct_Nested() { - var markup = @" -namespace Nested -{ - readonly ref struct X$$ {} -}"; + var markup = """ + + namespace Nested + { + readonly ref struct X$$ {} + } + """; await TestAsync(markup, MainDescription("readonly ref struct Nested.X")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/22450")] public async Task TestRefLikeTypesNoDeprecated() { - var xmlString = @" - - - - -public ref struct TestRef -{ -} - - - -ref struct Test -{ - private $$TestRef _field; -} - - -"; + var xmlString = """ + + + + + + public ref struct TestRef + { + } + + + + ref struct Test + { + private $$TestRef _field; + } + + + + """; // There should be no [deprecated] attribute displayed. await VerifyWithReferenceWorkerAsync(xmlString, MainDescription($"ref struct TestRef")); @@ -6601,24 +7507,26 @@ ref struct Test public async Task PropertyWithSameNameAsOtherType() { await TestAsync( -@"namespace ConsoleApplication1 -{ - class Program - { - static A B { get; set; } - static B A { get; set; } + """ + namespace ConsoleApplication1 + { + class Program + { + static A B { get; set; } + static B A { get; set; } - static void Main(string[] args) - { - B = ConsoleApplication1.B$$.F(); - } - } - class A { } - class B - { - public static A F() => null; - } -}", + static void Main(string[] args) + { + B = ConsoleApplication1.B$$.F(); + } + } + class A { } + class B + { + public static A F() => null; + } + } + """, MainDescription($"ConsoleApplication1.A ConsoleApplication1.B.F()")); } @@ -6626,26 +7534,28 @@ class B public async Task PropertyWithSameNameAsOtherType2() { await TestAsync( -@"using System.Collections.Generic; + """ + using System.Collections.Generic; -namespace ConsoleApplication1 -{ - class Program - { - public static List Bar { get; set; } + namespace ConsoleApplication1 + { + class Program + { + public static List Bar { get; set; } - static void Main(string[] args) - { - Tes$$t(); - } + static void Main(string[] args) + { + Tes$$t(); + } - static void Test() { } - } + static void Test() { } + } - class Bar - { - } -}", + class Bar + { + } + } + """, MainDescription($"void Program.Test()")); } @@ -6653,36 +7563,40 @@ class Bar public async Task InMalformedEmbeddedStatement_01() { await TestAsync( -@" -class Program -{ - void method1() - { - if (method2()) - .Any(b => b.Content$$Type, out var chars) - { - } - } -} -"); + """ + + class Program + { + void method1() + { + if (method2()) + .Any(b => b.Content$$Type, out var chars) + { + } + } + } + + """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23883")] public async Task InMalformedEmbeddedStatement_02() { await TestAsync( -@" -class Program -{ - void method1() - { - if (method2()) - .Any(b => b$$.ContentType, out var chars) - { - } - } -} -", + """ + + class Program + { + void method1() + { + if (method2()) + .Any(b => b$$.ContentType, out var chars) + { + } + } + } + + """, MainDescription($"({FeaturesResources.parameter}) ? b")); } @@ -6690,11 +7604,13 @@ void method1() public async Task EnumConstraint() { await TestInMethodAsync( -@" -class X where T : System.Enum -{ - private $$T x; -}", + """ + + class X where T : System.Enum + { + private $$T x; + } + """, MainDescription($"T {FeaturesResources.in_} X where T : Enum")); } @@ -6702,11 +7618,13 @@ class X where T : System.Enum public async Task DelegateConstraint() { await TestInMethodAsync( -@" -class X where T : System.Delegate -{ - private $$T x; -}", + """ + + class X where T : System.Delegate + { + private $$T x; + } + """, MainDescription($"T {FeaturesResources.in_} X where T : Delegate")); } @@ -6714,11 +7632,13 @@ class X where T : System.Delegate public async Task MulticastDelegateConstraint() { await TestInMethodAsync( -@" -class X where T : System.MulticastDelegate -{ - private $$T x; -}", + """ + + class X where T : System.MulticastDelegate + { + private $$T x; + } + """, MainDescription($"T {FeaturesResources.in_} X where T : MulticastDelegate")); } @@ -6726,10 +7646,12 @@ class X where T : System.MulticastDelegate public async Task UnmanagedConstraint_Type() { await TestAsync( -@" -class $$X where T : unmanaged -{ -}", + """ + + class $$X where T : unmanaged + { + } + """, MainDescription("class X where T : unmanaged")); } @@ -6737,11 +7659,13 @@ class $$X where T : unmanaged public async Task UnmanagedConstraint_Method() { await TestAsync( -@" -class X -{ - void $$M() where T : unmanaged { } -}", + """ + + class X + { + void $$M() where T : unmanaged { } + } + """, MainDescription("void X.M() where T : unmanaged")); } @@ -6757,14 +7681,16 @@ await TestAsync( public async Task UnmanagedConstraint_LocalFunction() { await TestAsync( -@" -class X -{ - void N() - { - void $$M() where T : unmanaged { } - } -}", + """ + + class X + { + void N() + { + void $$M() where T : unmanaged { } + } + } + """, MainDescription("void M() where T : unmanaged")); } @@ -6772,12 +7698,14 @@ void N() public async Task TestGetAccessorDocumentation() { await TestAsync( -@" -class X -{ - /// Summary for property Goo - int Goo { g$$et; set; } -}", + """ + + class X + { + /// Summary for property Goo + int Goo { g$$et; set; } + } + """, Documentation("Summary for property Goo")); } @@ -6785,12 +7713,14 @@ class X public async Task TestSetAccessorDocumentation() { await TestAsync( -@" -class X -{ - /// Summary for property Goo - int Goo { get; s$$et; } -}", + """ + + class X + { + /// Summary for property Goo + int Goo { get; s$$et; } + } + """, Documentation("Summary for property Goo")); } @@ -6798,18 +7728,20 @@ class X public async Task TestEventAddDocumentation1() { await TestAsync( -@" -using System; + """ -class X -{ - /// Summary for event Goo - event EventHandler Goo - { - a$$dd => throw null; - remove => throw null; - } -}", + using System; + + class X + { + /// Summary for event Goo + event EventHandler Goo + { + a$$dd => throw null; + remove => throw null; + } + } + """, Documentation("Summary for event Goo")); } @@ -6817,16 +7749,18 @@ event EventHandler Goo public async Task TestEventAddDocumentation2() { await TestAsync( -@" -using System; + """ -class X -{ - /// Summary for event Goo - event EventHandler Goo; + using System; + + class X + { + /// Summary for event Goo + event EventHandler Goo; - void M() => Goo +$$= null; -}", + void M() => Goo +$$= null; + } + """, Documentation("Summary for event Goo")); } @@ -6834,18 +7768,20 @@ class X public async Task TestEventRemoveDocumentation1() { await TestAsync( -@" -using System; + """ -class X -{ - /// Summary for event Goo - event EventHandler Goo - { - add => throw null; - r$$emove => throw null; - } -}", + using System; + + class X + { + /// Summary for event Goo + event EventHandler Goo + { + add => throw null; + r$$emove => throw null; + } + } + """, Documentation("Summary for event Goo")); } @@ -6853,16 +7789,18 @@ event EventHandler Goo public async Task TestEventRemoveDocumentation2() { await TestAsync( -@" -using System; + """ -class X -{ - /// Summary for event Goo - event EventHandler Goo; + using System; + + class X + { + /// Summary for event Goo + event EventHandler Goo; - void M() => Goo -$$= null; -}", + void M() => Goo -$$= null; + } + """, Documentation("Summary for event Goo")); } @@ -6870,14 +7808,16 @@ class X public async Task BuiltInOperatorWithUserDefinedEquivalent() { await TestAsync( -@" -class X -{ - void N(string a, string b) - { - var v = a $$== b; - } -}", + """ + + class X + { + void N(string a, string b) + { + var v = a $$== b; + } + } + """, MainDescription("bool string.operator ==(string a, string b)"), SymbolGlyph(Glyph.Operator)); } @@ -6886,10 +7826,12 @@ void N(string a, string b) public async Task NotNullConstraint_Type() { await TestAsync( -@" -class $$X where T : notnull -{ -}", + """ + + class $$X where T : notnull + { + } + """, MainDescription("class X where T : notnull")); } @@ -6897,11 +7839,13 @@ class $$X where T : notnull public async Task NotNullConstraint_Method() { await TestAsync( -@" -class X -{ - void $$M() where T : notnull { } -}", + """ + + class X + { + void $$M() where T : notnull { } + } + """, MainDescription("void X.M() where T : notnull")); } @@ -6909,10 +7853,12 @@ class X public async Task MultipleConstraints_Type() { await TestAsync( -@" -class $$X where T : notnull where U : notnull -{ -}", + """ + + class $$X where T : notnull where U : notnull + { + } + """, MainDescription(""" class X where T : notnull @@ -6924,11 +7870,13 @@ class X public async Task MultipleConstraints_Method() { await TestAsync( -@" -class X -{ - void $$M() where T : notnull where U : notnull { } -}", + """ + + class X + { + void $$M() where T : notnull where U : notnull { } + } + """, MainDescription(""" void X.M() where T : notnull @@ -6948,14 +7896,16 @@ await TestAsync( public async Task NotNullConstraint_LocalFunction() { await TestAsync( -@" -class X -{ - void N() - { - void $$M() where T : notnull { } - } -}", + """ + + class X + { + void N() + { + void $$M() where T : notnull { } + } + } + """, MainDescription("void M() where T : notnull")); } @@ -6963,15 +7913,17 @@ void N() public async Task NullableParameterThatIsMaybeNull() { await TestWithOptionsAsync(TestOptions.Regular8, -@"#nullable enable + """ + #nullable enable -class X -{ - void N(string? s) - { - string s2 = $$s; - } -}", + class X + { + void N(string? s) + { + string s2 = $$s; + } + } + """, MainDescription($"({FeaturesResources.parameter}) string? s"), NullabilityAnalysis(string.Format(FeaturesResources._0_may_be_null_here, "s"))); } @@ -6980,16 +7932,18 @@ void N(string? s) public async Task NullableParameterThatIsNotNull() { await TestWithOptionsAsync(TestOptions.Regular8, -@"#nullable enable + """ + #nullable enable -class X -{ - void N(string? s) - { - s = """"; - string s2 = $$s; - } -}", + class X + { + void N(string? s) + { + s = ""; + string s2 = $$s; + } + } + """, MainDescription($"({FeaturesResources.parameter}) string? s"), NullabilityAnalysis(string.Format(FeaturesResources._0_is_not_null_here, "s"))); } @@ -6998,17 +7952,19 @@ void N(string? s) public async Task NullableFieldThatIsMaybeNull() { await TestWithOptionsAsync(TestOptions.Regular8, -@"#nullable enable + """ + #nullable enable -class X -{ - string? s = null; + class X + { + string? s = null; - void N() - { - string s2 = $$s; - } -}", + void N() + { + string s2 = $$s; + } + } + """, MainDescription($"({FeaturesResources.field}) string? X.s"), NullabilityAnalysis(string.Format(FeaturesResources._0_may_be_null_here, "s"))); } @@ -7017,18 +7973,20 @@ void N() public async Task NullableFieldThatIsNotNull() { await TestWithOptionsAsync(TestOptions.Regular8, -@"#nullable enable + """ + #nullable enable -class X -{ - string? s = null; + class X + { + string? s = null; - void N() - { - s = """"; - string s2 = $$s; - } -}", + void N() + { + s = ""; + string s2 = $$s; + } + } + """, MainDescription($"({FeaturesResources.field}) string? X.s"), NullabilityAnalysis(string.Format(FeaturesResources._0_is_not_null_here, "s"))); } @@ -7037,17 +7995,19 @@ void N() public async Task NullablePropertyThatIsMaybeNull() { await TestWithOptionsAsync(TestOptions.Regular8, -@"#nullable enable + """ + #nullable enable -class X -{ - string? S { get; set; } + class X + { + string? S { get; set; } - void N() - { - string s2 = $$S; - } -}", + void N() + { + string s2 = $$S; + } + } + """, MainDescription("string? X.S { get; set; }"), NullabilityAnalysis(string.Format(FeaturesResources._0_may_be_null_here, "S"))); } @@ -7056,18 +8016,20 @@ void N() public async Task NullablePropertyThatIsNotNull() { await TestWithOptionsAsync(TestOptions.Regular8, -@"#nullable enable + """ + #nullable enable -class X -{ - string? S { get; set; } + class X + { + string? S { get; set; } - void N() - { - S = """"; - string s2 = $$S; - } -}", + void N() + { + S = ""; + string s2 = $$S; + } + } + """, MainDescription("string? X.S { get; set; }"), NullabilityAnalysis(string.Format(FeaturesResources._0_is_not_null_here, "S"))); } @@ -7076,22 +8038,24 @@ void N() public async Task NullableRangeVariableThatIsMaybeNull() { await TestWithOptionsAsync(TestOptions.Regular8, -@"#nullable enable + """ + #nullable enable -using System.Collections.Generic; + using System.Collections.Generic; -class X -{ - void N() - { - IEnumerable enumerable; + class X + { + void N() + { + IEnumerable enumerable; - foreach (var s in enumerable) - { - string s2 = $$s; - } - } -}", + foreach (var s in enumerable) + { + string s2 = $$s; + } + } + } + """, MainDescription($"({FeaturesResources.local_variable}) string? s"), NullabilityAnalysis(string.Format(FeaturesResources._0_may_be_null_here, "s"))); } @@ -7100,22 +8064,24 @@ void N() public async Task NullableRangeVariableThatIsNotNull() { await TestWithOptionsAsync(TestOptions.Regular8, -@"#nullable enable + """ + #nullable enable -using System.Collections.Generic; + using System.Collections.Generic; -class X -{ - void N() - { - IEnumerable enumerable; + class X + { + void N() + { + IEnumerable enumerable; - foreach (var s in enumerable) - { - string s2 = $$s; - } - } -}", + foreach (var s in enumerable) + { + string s2 = $$s; + } + } + } + """, MainDescription($"({FeaturesResources.local_variable}) string? s"), NullabilityAnalysis(string.Format(FeaturesResources._0_is_not_null_here, "s"))); } @@ -7124,18 +8090,20 @@ void N() public async Task NullableLocalThatIsMaybeNull() { await TestWithOptionsAsync(TestOptions.Regular8, -@"#nullable enable + """ + #nullable enable -using System.Collections.Generic; + using System.Collections.Generic; -class X -{ - void N() - { - string? s = null; - string s2 = $$s; - } -}", + class X + { + void N() + { + string? s = null; + string s2 = $$s; + } + } + """, MainDescription($"({FeaturesResources.local_variable}) string? s"), NullabilityAnalysis(string.Format(FeaturesResources._0_may_be_null_here, "s"))); } @@ -7144,18 +8112,20 @@ void N() public async Task NullableLocalThatIsNotNull() { await TestWithOptionsAsync(TestOptions.Regular8, -@"#nullable enable + """ + #nullable enable -using System.Collections.Generic; + using System.Collections.Generic; -class X -{ - void N() - { - string? s = """"; - string s2 = $$s; - } -}", + class X + { + void N() + { + string? s = ""; + string s2 = $$s; + } + } + """, MainDescription($"({FeaturesResources.local_variable}) string? s"), NullabilityAnalysis(string.Format(FeaturesResources._0_is_not_null_here, "s"))); } @@ -7164,18 +8134,20 @@ void N() public async Task NullableNotShownPriorToLanguageVersion8() { await TestWithOptionsAsync(TestOptions.Regular7_3, -@"#nullable enable + """ + #nullable enable -using System.Collections.Generic; + using System.Collections.Generic; -class X -{ - void N() - { - string s = """"; - string s2 = $$s; - } -}", + class X + { + void N() + { + string s = ""; + string s2 = $$s; + } + } + """, MainDescription($"({FeaturesResources.local_variable}) string s"), NullabilityAnalysis("")); } @@ -7184,18 +8156,20 @@ void N() public async Task NullableNotShownInNullableDisable() { await TestWithOptionsAsync(TestOptions.Regular8, -@"#nullable disable + """ + #nullable disable -using System.Collections.Generic; + using System.Collections.Generic; -class X -{ - void N() - { - string s = """"; - string s2 = $$s; - } -}", + class X + { + void N() + { + string s = ""; + string s2 = $$s; + } + } + """, MainDescription($"({FeaturesResources.local_variable}) string s"), NullabilityAnalysis("")); } @@ -7204,16 +8178,18 @@ void N() public async Task NullableShownWhenEnabledGlobally() { await TestWithOptionsAsync(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, nullableContextOptions: NullableContextOptions.Enable), -@"using System.Collections.Generic; + """ + using System.Collections.Generic; -class X -{ - void N() - { - string s = """"; - string s2 = $$s; - } -}", + class X + { + void N() + { + string s = ""; + string s2 = $$s; + } + } + """, MainDescription($"({FeaturesResources.local_variable}) string s"), NullabilityAnalysis(string.Format(FeaturesResources._0_is_not_null_here, "s"))); } @@ -7222,18 +8198,20 @@ void N() public async Task NullableNotShownForValueType() { await TestWithOptionsAsync(TestOptions.Regular8, -@"#nullable enable + """ + #nullable enable -using System.Collections.Generic; + using System.Collections.Generic; -class X -{ - void N() - { - int a = 0; - int b = $$a; - } -}", + class X + { + void N() + { + int a = 0; + int b = $$a; + } + } + """, MainDescription($"({FeaturesResources.local_variable}) int a"), NullabilityAnalysis("")); } @@ -7242,18 +8220,20 @@ void N() public async Task NullableNotShownForConst() { await TestWithOptionsAsync(TestOptions.Regular8, -@"#nullable enable + """ + #nullable enable -using System.Collections.Generic; + using System.Collections.Generic; -class X -{ - void N() - { - const string? s = null; - string? s2 = $$s; - } -}", + class X + { + void N() + { + const string? s = null; + string? s2 = $$s; + } + } + """, MainDescription($"({FeaturesResources.local_constant}) string? s = null"), NullabilityAnalysis("")); } @@ -7262,13 +8242,15 @@ void N() public async Task TestInheritdocInlineSummary() { var markup = -@" -/// Summary documentation -/// Remarks documentation -void M(int x) { } + """ + + /// Summary documentation + /// Remarks documentation + void M(int x) { } -/// -void $$M(int x, int y) { }"; + /// + void $$M(int x, int y) { } + """; await TestInClassAsync(markup, MainDescription("void C.M(int x, int y)"), @@ -7279,16 +8261,18 @@ await TestInClassAsync(markup, public async Task TestInheritdocTwoLevels1() { var markup = -@" -/// Summary documentation -/// Remarks documentation -void M() { } + """ -/// -void M(int x) { } + /// Summary documentation + /// Remarks documentation + void M() { } -/// -void $$M(int x, int y) { }"; + /// + void M(int x) { } + + /// + void $$M(int x, int y) { } + """; await TestInClassAsync(markup, MainDescription("void C.M(int x, int y)"), @@ -7299,16 +8283,18 @@ await TestInClassAsync(markup, public async Task TestInheritdocTwoLevels2() { var markup = -@" -/// Summary documentation -/// Remarks documentation -void M() { } + """ + + /// Summary documentation + /// Remarks documentation + void M() { } -/// -void M(int x) { } + /// + void M(int x) { } -/// -void $$M(int x, int y) { }"; + /// + void $$M(int x, int y) { } + """; await TestInClassAsync(markup, MainDescription("void C.M(int x, int y)"), @@ -7319,26 +8305,28 @@ await TestInClassAsync(markup, public async Task TestInheritdocWithTypeParamRef() { var markup = -@" -public class Program -{ - public static void Main() => _ = new Test().$$Clone(); -} + """ -public class Test : ICloneable> -{ - /// - public Test Clone() => new(); -} + public class Program + { + public static void Main() => _ = new Test().$$Clone(); + } -/// A type that has clonable instances. -/// The type of instances that can be cloned. -public interface ICloneable -{ - /// Clones a . - /// A clone of the . - public T Clone(); -}"; + public class Test : ICloneable> + { + /// + public Test Clone() => new(); + } + + /// A type that has clonable instances. + /// The type of instances that can be cloned. + public interface ICloneable + { + /// Clones a . + /// A clone of the . + public T Clone(); + } + """; await TestInClassAsync(markup, MainDescription("Test Test.Clone()"), @@ -7349,21 +8337,23 @@ await TestInClassAsync(markup, public async Task TestInheritdocWithTypeParamRef1() { var markup = -@" -public interface ITest -{ - /// - /// A generic method . - /// - /// A generic type. - void Foo(); -} + """ -public class Test : ITest -{ - /// - public void $$Foo() { } -}"; + public interface ITest + { + /// + /// A generic method . + /// + /// A generic type. + void Foo(); + } + + public class Test : ITest + { + /// + public void $$Foo() { } + } + """; await TestWithOptionsAsync(TestOptions.Regular8, markup, @@ -7378,12 +8368,14 @@ await TestWithOptionsAsync(TestOptions.Regular8, public async Task TestInheritdocCycle1() { var markup = -@" -/// -void M(int x) { } + """ -/// -void $$M(int x, int y) { }"; + /// + void M(int x) { } + + /// + void $$M(int x, int y) { } + """; await TestInClassAsync(markup, MainDescription("void C.M(int x, int y)"), @@ -7394,9 +8386,11 @@ await TestInClassAsync(markup, public async Task TestInheritdocCycle2() { var markup = -@" -/// -void $$M(int x) { }"; + """ + + /// + void $$M(int x) { } + """; await TestInClassAsync(markup, MainDescription("void C.M(int x)"), @@ -7407,9 +8401,11 @@ await TestInClassAsync(markup, public async Task TestInheritdocCycle3() { var markup = -@" -/// -void $$M(int x) { }"; + """ + + /// + void $$M(int x) { } + """; await TestInClassAsync(markup, MainDescription("void C.M(int x)"), @@ -7420,13 +8416,15 @@ await TestInClassAsync(markup, public async Task TestLinqGroupVariableDeclaration() { var code = -@" -void M(string[] a) -{ - var v = from x in a - group x by x.Length into $$g - select g; -}"; + """ + + void M(string[] a) + { + var v = from x in a + group x by x.Length into $$g + select g; + } + """; await TestInClassAsync(code, MainDescription($"({FeaturesResources.range_variable}) IGrouping g")); @@ -7435,247 +8433,297 @@ await TestInClassAsync(code, [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38283")] public async Task QuickInfoOnIndexerCloseBracket() { - await TestAsync(@" -class C -{ - public int this[int x] { get { return 1; } } + await TestAsync(""" - void M() - { - var x = new C()[5$$]; - } -}", + class C + { + public int this[int x] { get { return 1; } } + + void M() + { + var x = new C()[5$$]; + } + } + """, MainDescription("int C.this[int x] { get; }")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38283")] public async Task QuickInfoOnIndexerOpenBracket() { - await TestAsync(@" -class C -{ - public int this[int x] { get { return 1; } } + await TestAsync(""" - void M() - { - var x = new C()$$[5]; - } -}", + class C + { + public int this[int x] { get { return 1; } } + + void M() + { + var x = new C()$$[5]; + } + } + """, MainDescription("int C.this[int x] { get; }")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38283")] public async Task QuickInfoOnIndexer_NotOnArrayAccess() { - await TestAsync(@" -class Program -{ - void M() - { - int[] x = new int[4]; - int y = x[3$$]; - } -}", + await TestAsync(""" + + class Program + { + void M() + { + int[] x = new int[4]; + int y = x[3$$]; + } + } + """, MainDescription("struct System.Int32")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31618")] public async Task QuickInfoWithRemarksOnMethod() { - await TestAsync(@" -class Program -{ - /// - /// Summary text - /// - /// - /// Remarks text - /// - int M() - { - return $$M(); - } -}", + await TestAsync(""" + + class Program + { + /// + /// Summary text + /// + /// + /// Remarks text + /// + int M() + { + return $$M(); + } + } + """, MainDescription("int Program.M()"), Documentation("Summary text"), - Remarks("\r\nRemarks text")); + Remarks(""" + + Remarks text + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31618")] public async Task QuickInfoWithRemarksOnPropertyAccessor() { - await TestAsync(@" -class Program -{ - /// - /// Summary text - /// - /// - /// Remarks text - /// - int M { $$get; } -}", + await TestAsync(""" + + class Program + { + /// + /// Summary text + /// + /// + /// Remarks text + /// + int M { $$get; } + } + """, MainDescription("int Program.M.get"), Documentation("Summary text"), - Remarks("\r\nRemarks text")); + Remarks(""" + + Remarks text + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31618")] public async Task QuickInfoWithReturnsOnMethod() { - await TestAsync(@" -class Program -{ - /// - /// Summary text - /// - /// - /// Returns text - /// - int M() - { - return $$M(); - } -}", + await TestAsync(""" + + class Program + { + /// + /// Summary text + /// + /// + /// Returns text + /// + int M() + { + return $$M(); + } + } + """, MainDescription("int Program.M()"), Documentation("Summary text"), - Returns($"\r\n{FeaturesResources.Returns_colon}\r\n Returns text")); + Returns($""" + + {FeaturesResources.Returns_colon} + Returns text + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31618")] public async Task QuickInfoWithReturnsOnPropertyAccessor() { - await TestAsync(@" -class Program -{ - /// - /// Summary text - /// - /// - /// Returns text - /// - int M { $$get; } -}", + await TestAsync(""" + + class Program + { + /// + /// Summary text + /// + /// + /// Returns text + /// + int M { $$get; } + } + """, MainDescription("int Program.M.get"), Documentation("Summary text"), - Returns($"\r\n{FeaturesResources.Returns_colon}\r\n Returns text")); + Returns($""" + + {FeaturesResources.Returns_colon} + Returns text + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31618")] public async Task QuickInfoWithValueOnMethod() { - await TestAsync(@" -class Program -{ - /// - /// Summary text - /// - /// - /// Value text - /// - int M() - { - return $$M(); - } -}", + await TestAsync(""" + + class Program + { + /// + /// Summary text + /// + /// + /// Value text + /// + int M() + { + return $$M(); + } + } + """, MainDescription("int Program.M()"), Documentation("Summary text"), - Value($"\r\n{FeaturesResources.Value_colon}\r\n Value text")); + Value($""" + + {FeaturesResources.Value_colon} + Value text + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31618")] public async Task QuickInfoWithValueOnPropertyAccessor() { - await TestAsync(@" -class Program -{ - /// - /// Summary text - /// - /// - /// Value text - /// - int M { $$get; } -}", + await TestAsync(""" + + class Program + { + /// + /// Summary text + /// + /// + /// Value text + /// + int M { $$get; } + } + """, MainDescription("int Program.M.get"), Documentation("Summary text"), - Value($"\r\n{FeaturesResources.Value_colon}\r\n Value text")); + Value($""" + + {FeaturesResources.Value_colon} + Value text + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42368")] public async Task QuickInfoNotPattern1() { - await TestAsync(@" -class Person -{ - void Goo(object o) - { - if (o is not $$Person p) - { - } - } -}", + await TestAsync(""" + + class Person + { + void Goo(object o) + { + if (o is not $$Person p) + { + } + } + } + """, MainDescription("class Person")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42368")] public async Task QuickInfoNotPattern2() { - await TestAsync(@" -class Person -{ - void Goo(object o) - { - if (o is $$not Person p) - { - } - } -}"); + await TestAsync(""" + + class Person + { + void Goo(object o) + { + if (o is $$not Person p) + { + } + } + } + """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42368")] public async Task QuickInfoOrPattern1() { - await TestAsync(@" -class Person -{ - void Goo(object o) - { - if (o is $$Person or int) - { - } - } -}", MainDescription("class Person")); + await TestAsync(""" + + class Person + { + void Goo(object o) + { + if (o is $$Person or int) + { + } + } + } + """, MainDescription("class Person")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42368")] public async Task QuickInfoOrPattern2() { - await TestAsync(@" -class Person -{ - void Goo(object o) - { - if (o is Person or $$int) - { - } - } -}", MainDescription("struct System.Int32")); + await TestAsync(""" + + class Person + { + void Goo(object o) + { + if (o is Person or $$int) + { + } + } + } + """, MainDescription("struct System.Int32")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42368")] public async Task QuickInfoOrPattern3() { - await TestAsync(@" -class Person -{ - void Goo(object o) - { - if (o is Person $$or int) - { - } - } -}"); + await TestAsync(""" + + class Person + { + void Goo(object o) + { + if (o is Person $$or int) + { + } + } + } + """); } [Fact] @@ -7683,12 +8731,14 @@ public async Task QuickInfoRecord() { await TestWithOptionsAsync( Options.Regular.WithLanguageVersion(LanguageVersion.CSharp9), -@"record Person(string First, string Last) -{ - void M($$Person p) - { - } -}", MainDescription("record Person")); + """ + record Person(string First, string Last) + { + void M($$Person p) + { + } + } + """, MainDescription("record Person")); } [Fact] @@ -7696,105 +8746,119 @@ public async Task QuickInfoDerivedRecord() { await TestWithOptionsAsync( Options.Regular.WithLanguageVersion(LanguageVersion.CSharp9), -@"record Person(string First, string Last) -{ -} -record Student(string Id) -{ - void M($$Student p) - { - } -} -", MainDescription("record Student")); + """ + record Person(string First, string Last) + { + } + record Student(string Id) + { + void M($$Student p) + { + } + } + + """, MainDescription("record Student")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/44904")] public async Task QuickInfoRecord_BaseTypeList() { - await TestAsync(@" -record Person(string First, string Last); -record Student(int Id) : $$Person(null, null); -", MainDescription("Person.Person(string First, string Last)")); + await TestAsync(""" + + record Person(string First, string Last); + record Student(int Id) : $$Person(null, null); + + """, MainDescription("Person.Person(string First, string Last)")); } [Fact] public async Task QuickInfoClass_BaseTypeList() { - await TestAsync(@" -class Person(string First, string Last); -class Student(int Id) : $$Person(null, null); -", MainDescription("Person.Person(string First, string Last)")); + await TestAsync(""" + + class Person(string First, string Last); + class Student(int Id) : $$Person(null, null); + + """, MainDescription("Person.Person(string First, string Last)")); } [Fact] public async Task QuickInfo_BaseConstructorInitializer() { - await TestAsync(@" -public class Person { public Person(int id) { } } -public class Student : Person { public Student() : $$base(0) { } } -", MainDescription("Person.Person(int id)")); + await TestAsync(""" + + public class Person { public Person(int id) { } } + public class Student : Person { public Student() : $$base(0) { } } + + """, MainDescription("Person.Person(int id)")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/57031")] public async Task QuickInfo_DotInInvocation() { - await TestAsync(@" -public class C -{ - public void M(int a) { } - public void M(int a, params int[] b) { } -} + await TestAsync(""" -class Program -{ - static void Main() - { - var c = new C(); - c$$.M(1, 2); - } -}", + public class C + { + public void M(int a) { } + public void M(int a, params int[] b) { } + } + + class Program + { + static void Main() + { + var c = new C(); + c$$.M(1, 2); + } + } + """, MainDescription($"void C.M(int a, params int[] b) (+ 1 {FeaturesResources.overload})")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/57031")] public async Task QuickInfo_BeforeMemberNameInInvocation() { - await TestAsync(@" -public class C -{ - public void M(int a) { } - public void M(int a, params int[] b) { } -} + await TestAsync(""" -class Program -{ - static void Main() - { - var c = new C(); - c.$$M(1, 2); - } -}", + public class C + { + public void M(int a) { } + public void M(int a, params int[] b) { } + } + + class Program + { + static void Main() + { + var c = new C(); + c.$$M(1, 2); + } + } + """, MainDescription($"void C.M(int a, params int[] b) (+ 1 {FeaturesResources.overload})")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/57031")] public async Task QuickInfo_AfterMemberNameInInvocation() { - await TestAsync(@" -public class C -{ - public void M(int a) { } - public void M(int a, params int[] b) { } -} + await TestAsync(""" -class Program -{ - static void Main() - { - var c = new C(); - c.M$$(1, 2); - } -}", + public class C + { + public void M(int a) { } + public void M(int a, params int[] b) { } + } + + class Program + { + static void Main() + { + var c = new C(); + c.M$$(1, 2); + } + } + """, MainDescription($"void C.M(int a, params int[] b) (+ 1 {FeaturesResources.overload})")); } @@ -7803,12 +8867,14 @@ public async Task QuickInfoRecordClass() { await TestWithOptionsAsync( Options.Regular.WithLanguageVersion(LanguageVersion.CSharp9), -@"record class Person(string First, string Last) -{ - void M($$Person p) - { - } -}", MainDescription("record Person")); + """ + record class Person(string First, string Last) + { + void M($$Person p) + { + } + } + """, MainDescription("record Person")); } [Fact] @@ -7816,12 +8882,14 @@ public async Task QuickInfoRecordStruct() { await TestWithOptionsAsync( Options.Regular.WithLanguageVersion(LanguageVersion.CSharp9), -@"record struct Person(string First, string Last) -{ - void M($$Person p) - { - } -}", MainDescription("record struct Person")); + """ + record struct Person(string First, string Last) + { + void M($$Person p) + { + } + } + """, MainDescription("record struct Person")); } [Fact] @@ -7829,12 +8897,14 @@ public async Task QuickInfoReadOnlyRecordStruct() { await TestWithOptionsAsync( Options.Regular.WithLanguageVersion(LanguageVersion.CSharp9), -@"readonly record struct Person(string First, string Last) -{ - void M($$Person p) - { - } -}", MainDescription("readonly record struct Person")); + """ + readonly record struct Person(string First, string Last) + { + void M($$Person p) + { + } + } + """, MainDescription("readonly record struct Person")); } [Fact] @@ -7842,14 +8912,16 @@ public async Task QuickInfoRecordProperty() { await TestWithOptionsAsync( Options.Regular.WithLanguageVersion(LanguageVersion.CSharp9), -@"/// The person's first name. -record Person(string First, string Last) -{ - void M(Person p) - { - _ = p.$$First; - } -}", + """ + /// The person's first name. + record Person(string First, string Last) + { + void M(Person p) + { + _ = p.$$First; + } + } + """, MainDescription("string Person.First { get; init; }"), Documentation("The person's first name.")); } @@ -7858,17 +8930,19 @@ void M(Person p) public async Task TestVarPatternOnVarKeyword() { await TestAsync( -@"class C -{ - string M() { } + """ + class C + { + string M() { } - void M2() - { - if (M() is va$$r x && x.Length > 0) - { - } - } -}", + void M2() + { + if (M() is va$$r x && x.Length > 0) + { + } + } + } + """, MainDescription("class System.String")); } @@ -7876,17 +8950,19 @@ void M2() public async Task TestVarPatternOnVariableItself() { await TestAsync( -@"class C -{ - string M() { } + """ + class C + { + string M() { } - void M2() - { - if (M() is var x$$ && x.Length > 0) - { - } - } -}", + void M2() + { + if (M() is var x$$ && x.Length > 0) + { + } + } + } + """, MainDescription($"({FeaturesResources.local_variable}) string? x")); } @@ -7894,15 +8970,17 @@ void M2() public async Task TestVarPatternOnVarKeyword_InListPattern() { await TestAsync( -@"class C -{ - void M(char[] array) - { - if (array is [ va$$r one ]) - { - } - } -}", + """ + class C + { + void M(char[] array) + { + if (array is [ va$$r one ]) + { + } + } + } + """, MainDescription("struct System.Char")); } @@ -7910,15 +8988,17 @@ void M(char[] array) public async Task TestVarPatternOnVariableItself_InListPattern() { await TestAsync( -@"class C -{ - void M(char[] array) - { - if (array is [ var o$$ne ]) - { - } - } -}", + """ + class C + { + void M(char[] array) + { + if (array is [ var o$$ne ]) + { + } + } + } + """, MainDescription($"({FeaturesResources.local_variable}) char one")); } @@ -7926,15 +9006,17 @@ void M(char[] array) public async Task TestVarPatternOnVarKeyword_InSlicePattern() { await TestAsync( -@"class C -{ - void M(char[] array) - { - if (array is [..va$$r one ]) - { - } - } -}" + TestSources.Index + TestSources.Range, + """ + class C + { + void M(char[] array) + { + if (array is [..va$$r one ]) + { + } + } + } + """ + TestSources.Index + TestSources.Range, MainDescription("char[]")); } @@ -7942,15 +9024,17 @@ void M(char[] array) public async Task TestVarPatternOnVariableItself_InSlicePattern() { await TestAsync( -@"class C -{ - void M(char[] array) - { - if (array is [ ..var o$$ne ]) - { - } - } -}" + TestSources.Index + TestSources.Range, + """ + class C + { + void M(char[] array) + { + if (array is [ ..var o$$ne ]) + { + } + } + } + """ + TestSources.Index + TestSources.Range, MainDescription($"({FeaturesResources.local_variable}) char[]? one")); } @@ -7958,73 +9042,87 @@ void M(char[] array) public async Task TestDocumentationCData() { var markup = -@"using I$$ = IGoo; -/// -/// summary for interface IGoo -/// y = null; -/// ]]> -/// -interface IGoo { }"; + """ + using I$$ = IGoo; + /// + /// summary for interface IGoo + /// y = null; + /// ]]> + /// + interface IGoo { } + """; await TestAsync(markup, MainDescription("interface IGoo"), - Documentation(@"summary for interface IGoo + Documentation(""" + summary for interface IGoo -List y = null;")); + List y = null; + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/37503")] public async Task DoNotNormalizeWhitespaceForCode() { var markup = -@"using I$$ = IGoo; -/// -/// Normalize this, and Also this -/// -/// line 1 -/// line 2 -/// -/// -interface IGoo { }"; + """ + using I$$ = IGoo; + /// + /// Normalize this, and Also this + /// + /// line 1 + /// line 2 + /// + /// + interface IGoo { } + """; await TestAsync(markup, MainDescription("interface IGoo"), - Documentation(@"Normalize this, and Also this + Documentation(""" + Normalize this, and Also this -line 1 -line 2")); + line 1 + line 2 + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/57262")] public async Task DoNotNormalizeLeadingWhitespaceForCode() { var markup = - @"using I$$ = IGoo; -/// -/// Normalize this, and Also this -/// -/// line 1 -/// line 2 -/// -/// -interface IGoo { }"; + """ + using I$$ = IGoo; + /// + /// Normalize this, and Also this + /// + /// line 1 + /// line 2 + /// + /// + interface IGoo { } + """; await TestAsync(markup, MainDescription("interface IGoo"), - Documentation(@"Normalize this, and Also this + Documentation(""" + Normalize this, and Also this -line 1 - line 2")); + line 1 + line 2 + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/57262")] public async Task ParsesEmptySummary() { var markup = - @"using I$$ = IGoo; -/// -interface IGoo { }"; + """ + using I$$ = IGoo; + /// + interface IGoo { } + """; await TestAsync(markup, MainDescription("interface IGoo"), @@ -8034,18 +9132,20 @@ await TestAsync(markup, [Fact] public async Task TestStaticAbstract_ImplicitImplementation() { - var code = @" -interface I1 -{ - /// Summary text - static abstract void M1(); -} + var code = """ -class C1_1 : I1 -{ - public static void $$M1() { } -} -"; + interface I1 + { + /// Summary text + static abstract void M1(); + } + + class C1_1 : I1 + { + public static void $$M1() { } + } + + """; await TestAsync( code, @@ -8056,23 +9156,25 @@ await TestAsync( [Fact] public async Task TestStaticAbstract_ImplicitImplementation_FromReference() { - var code = @" -interface I1 -{ - /// Summary text - static abstract void M1(); -} + var code = """ -class C1_1 : I1 -{ - public static void M1() { } -} + interface I1 + { + /// Summary text + static abstract void M1(); + } -class R -{ - public static void M() { C1_1.$$M1(); } -} -"; + class C1_1 : I1 + { + public static void M1() { } + } + + class R + { + public static void M() { C1_1.$$M1(); } + } + + """; await TestAsync( code, @@ -8083,18 +9185,20 @@ await TestAsync( [Fact] public async Task TestStaticAbstract_FromTypeParameterReference() { - var code = @" -interface I1 -{ - /// Summary text - static abstract void M1(); -} + var code = """ -class R -{ - public static void M() where T : I1 { T.$$M1(); } -} -"; + interface I1 + { + /// Summary text + static abstract void M1(); + } + + class R + { + public static void M() where T : I1 { T.$$M1(); } + } + + """; await TestAsync( code, @@ -8105,19 +9209,21 @@ await TestAsync( [Fact] public async Task TestStaticAbstract_ExplicitInheritdoc_ImplicitImplementation() { - var code = @" -interface I1 -{ - /// Summary text - static abstract void M1(); -} + var code = """ -class C1_1 : I1 -{ - /// - public static void $$M1() { } -} -"; + interface I1 + { + /// Summary text + static abstract void M1(); + } + + class C1_1 : I1 + { + /// + public static void $$M1() { } + } + + """; await TestAsync( code, @@ -8128,18 +9234,20 @@ await TestAsync( [Fact] public async Task TestStaticAbstract_ExplicitImplementation() { - var code = @" -interface I1 -{ - /// Summary text - static abstract void M1(); -} + var code = """ -class C1_1 : I1 -{ - static void I1.$$M1() { } -} -"; + interface I1 + { + /// Summary text + static abstract void M1(); + } + + class C1_1 : I1 + { + static void I1.$$M1() { } + } + + """; await TestAsync( code, @@ -8150,19 +9258,21 @@ await TestAsync( [Fact] public async Task TestStaticAbstract_ExplicitInheritdoc_ExplicitImplementation() { - var code = @" -interface I1 -{ - /// Summary text - static abstract void M1(); -} + var code = """ -class C1_1 : I1 -{ - /// - static void I1.$$M1() { } -} -"; + interface I1 + { + /// Summary text + static abstract void M1(); + } + + class C1_1 : I1 + { + /// + static void I1.$$M1() { } + } + + """; await TestAsync( code, @@ -8175,10 +9285,12 @@ public async Task QuickInfoLambdaReturnType_01() { await TestWithOptionsAsync( Options.Regular.WithLanguageVersion(LanguageVersion.CSharp9), -@"class Program -{ - System.Delegate D = bo$$ol () => true; -}", + """ + class Program + { + System.Delegate D = bo$$ol () => true; + } + """, MainDescription("struct System.Boolean")); } @@ -8187,11 +9299,13 @@ public async Task QuickInfoLambdaReturnType_02() { await TestWithOptionsAsync( Options.Regular.WithLanguageVersion(LanguageVersion.CSharp9), -@"class A -{ - struct B { } - System.Delegate D = A.B$$ () => null; -}", + """ + class A + { + struct B { } + System.Delegate D = A.B$$ () => null; + } + """, MainDescription("struct A.B")); } @@ -8200,13 +9314,15 @@ public async Task QuickInfoLambdaReturnType_03() { await TestWithOptionsAsync( Options.Regular.WithLanguageVersion(LanguageVersion.CSharp9), -@"class A -{ -} -struct B -{ - System.Delegate D = A () => null; -}", + """ + class A + { + } + struct B + { + System.Delegate D = A () => null; + } + """, MainDescription("struct B")); } @@ -8214,30 +9330,36 @@ struct B public async Task TestNormalFuncSynthesizedLambdaType() { await TestAsync( -@"class C -{ - void M() - { - $$var v = (int i) => i.ToString(); - } -}", + """ + class C + { + void M() + { + $$var v = (int i) => i.ToString(); + } + } + """, MainDescription("delegate TResult System.Func(T arg)"), - TypeParameterMap($@" -T {FeaturesResources.is_} int -TResult {FeaturesResources.is_} string")); + TypeParameterMap($""" + + T {FeaturesResources.is_} int + TResult {FeaturesResources.is_} string + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58871")] public async Task TestInferredNonAnonymousDelegateType1() { await TestAsync( -@"class C -{ - void M() - { - $$var v = (int i) => i.ToString(); - } -}", + """ + class C + { + void M() + { + $$var v = (int i) => i.ToString(); + } + } + """, MainDescription("delegate TResult System.Func(T arg)"), AnonymousTypes("")); } @@ -8246,13 +9368,15 @@ void M() public async Task TestAnonymousSynthesizedLambdaType() { await TestAsync( -@"class C -{ - void M() - { - $$var v = (ref int i) => i.ToString(); - } -}", + """ + class C + { + void M() + { + $$var v = (ref int i) => i.ToString(); + } + } + """, MainDescription("delegate string (ref int arg)"), AnonymousTypes("")); } @@ -8261,120 +9385,140 @@ void M() public async Task TestAnonymousSynthesizedLambdaType2() { await TestAsync( -@"class C -{ - void M() - { - var $$v = (ref int i) => i.ToString(); - } -}", + """ + class C + { + void M() + { + var $$v = (ref int i) => i.ToString(); + } + } + """, MainDescription($"({FeaturesResources.local_variable}) 'a v"), AnonymousTypes( -$@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} delegate string (ref int arg)")); + $""" + + {FeaturesResources.Types_colon} + 'a {FeaturesResources.is_} delegate string (ref int arg) + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58871")] public async Task TestAnonymousSynthesizedLambdaType3() { await TestAsync( -@"class C -{ - void M() - { - var v = (ref int i) => i.ToString(); - $$Goo(v); - } + """ + class C + { + void M() + { + var v = (ref int i) => i.ToString(); + $$Goo(v); + } - T Goo(T t) => default; -}", + T Goo(T t) => default; + } + """, MainDescription("'a C.Goo<'a>('a t)"), AnonymousTypes( -$@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} delegate string (ref int arg)")); + $""" + + {FeaturesResources.Types_colon} + 'a {FeaturesResources.is_} delegate string (ref int arg) + """)); } [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] public async Task TestAnonymousSynthesizedLambdaType4() { await TestAsync( -@" -class C -{ - void M() - { - var lam = (int param = 42) => param + 1; - $$lam(); - } -} -", + """ + + class C + { + void M() + { + var lam = (int param = 42) => param + 1; + $$lam(); + } + } + + """, MainDescription($"({FeaturesResources.local_variable}) 'a lam"), AnonymousTypes( -$@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} delegate int (int arg = 42)")); + $""" + + {FeaturesResources.Types_colon} + 'a {FeaturesResources.is_} delegate int (int arg = 42) + """)); } [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] public async Task TestAnonymousSynthesizedLambdaType5() { await TestAsync( -@" -class C -{ - void M() - { - $$var lam = (int param = 42) => param; - } -} -", MainDescription("delegate int (int arg = 42)")); + """ + + class C + { + void M() + { + $$var lam = (int param = 42) => param; + } + } + + """, MainDescription("delegate int (int arg = 42)")); } [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] public async Task TestAnonymousSynthesizedLambdaType6() { await TestAsync( -@" -class C -{ - void M() - { - var lam = (i$$nt param = 42) => param; - } -} -", MainDescription("struct System.Int32")); + """ + + class C + { + void M() + { + var lam = (i$$nt param = 42) => param; + } + } + + """, MainDescription("struct System.Int32")); } [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] public async Task TestAnonymousSynthesizedLambdaType7() { await TestAsync( -@" -class C -{ - void M() - { - var lam = (int pa$$ram = 42) => param; - } -} -", MainDescription($"({FeaturesResources.parameter}) int param = 42")); + """ + + class C + { + void M() + { + var lam = (int pa$$ram = 42) => param; + } + } + + """, MainDescription($"({FeaturesResources.parameter}) int param = 42")); } [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] public async Task TestAnonymousSynthesizedLambdaType8() { await TestAsync( -@" -class C -{ - void M() - { - var lam = (int param = 4$$2) => param; - } -} -", MainDescription("struct System.Int32")); + """ + + class C + { + void M() + { + var lam = (int param = 4$$2) => param; + } + } + + """, MainDescription("struct System.Int32")); } [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] @@ -8447,11 +9591,13 @@ void M() public async Task TestSingleTupleType() { await TestInClassAsync( -@"void M((int x, string y) t) { } - void N() - { - $$M(default); - }", + """ + void M((int x, string y) t) { } + void N() + { + $$M(default); + } + """, MainDescription(@"void C.M((int x, string y) t)"), NoTypeParameterMap, AnonymousTypes(string.Empty)); @@ -8461,27 +9607,33 @@ void N() public async Task TestMultipleTupleTypesSameType() { await TestInClassAsync( -@"void M((int x, string y) s, (int x, string y) t) { } - void N() - { - $$M(default); - }", + """ + void M((int x, string y) s, (int x, string y) t) { } + void N() + { + $$M(default); + } + """, MainDescription(@"void C.M('a s, 'a t)"), NoTypeParameterMap, - AnonymousTypes($@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} (int x, string y)")); + AnonymousTypes($""" + + {FeaturesResources.Types_colon} + 'a {FeaturesResources.is_} (int x, string y) + """)); } [Fact] public async Task TestMultipleTupleTypesDifferentTypes1() { await TestInClassAsync( -@"void M((int x, string y) s, (int a, string b) u) { } - void N() - { - $$M(default); - }", + """ + void M((int x, string y) s, (int a, string b) u) { } + void N() + { + $$M(default); + } + """, MainDescription(@"void C.M((int x, string y) s, (int a, string b) u)"), NoTypeParameterMap); } @@ -8490,94 +9642,116 @@ void N() public async Task TestMultipleTupleTypesDifferentTypes2() { await TestInClassAsync( -@"void M((int x, string y) s, (int x, string y) t, (int a, string b) u, (int a, string b) v) { } - void N() - { - $$M(default); - }", + """ + void M((int x, string y) s, (int x, string y) t, (int a, string b) u, (int a, string b) v) { } + void N() + { + $$M(default); + } + """, MainDescription(@"void C.M('a s, 'a t, 'b u, 'b v)"), NoTypeParameterMap, - AnonymousTypes($@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} (int x, string y) - 'b {FeaturesResources.is_} (int a, string b)")); + AnonymousTypes($""" + + {FeaturesResources.Types_colon} + 'a {FeaturesResources.is_} (int x, string y) + 'b {FeaturesResources.is_} (int a, string b) + """)); } [Fact] public async Task TestMultipleTupleTypesDifferentTypes3() { await TestInClassAsync( -@"void M((int x, string y) s, (int x, string y) t, (int a, string b) u) { } - void N() - { - $$M(default); - }", + """ + void M((int x, string y) s, (int x, string y) t, (int a, string b) u) { } + void N() + { + $$M(default); + } + """, MainDescription(@"void C.M('a s, 'a t, 'b u)"), NoTypeParameterMap, - AnonymousTypes($@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} (int x, string y) - 'b {FeaturesResources.is_} (int a, string b)")); + AnonymousTypes($""" + + {FeaturesResources.Types_colon} + 'a {FeaturesResources.is_} (int x, string y) + 'b {FeaturesResources.is_} (int a, string b) + """)); } [Fact] public async Task TestMultipleTupleTypesInference() { await TestInClassAsync( -@"T M(T t) { } - void N() - { - (int a, string b) x = default; - $$M(x); - }", + """ + T M(T t) { } + void N() + { + (int a, string b) x = default; + $$M(x); + } + """, MainDescription(@"'a C.M<'a>('a t)"), NoTypeParameterMap, - AnonymousTypes($@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} (int a, string b)")); + AnonymousTypes($""" + + {FeaturesResources.Types_colon} + 'a {FeaturesResources.is_} (int a, string b) + """)); } [Fact] public async Task TestAnonymousTypeWithTupleTypesInference1() { await TestInClassAsync( -@"T M(T t) { } - void N() - { - var v = new { x = default((int a, string b)) }; - $$M(v); - }", + """ + T M(T t) { } + void N() + { + var v = new { x = default((int a, string b)) }; + $$M(v); + } + """, MainDescription(@"'a C.M<'a>('a t)"), NoTypeParameterMap, - AnonymousTypes($@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} new {{ (int a, string b) x }}")); + AnonymousTypes($$""" + + {{FeaturesResources.Types_colon}} + 'a {{FeaturesResources.is_}} new { (int a, string b) x } + """)); } [Fact] public async Task TestAnonymousTypeWithTupleTypesInference2() { await TestInClassAsync( -@"T M(T t) { } - void N() - { - var v = new { x = default((int a, string b)), y = default((int a, string b)) }; - $$M(v); - }", + """ + T M(T t) { } + void N() + { + var v = new { x = default((int a, string b)), y = default((int a, string b)) }; + $$M(v); + } + """, MainDescription(@"'a C.M<'a>('a t)"), NoTypeParameterMap, - AnonymousTypes($@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} new {{ 'b x, 'b y }} - 'b {FeaturesResources.is_} (int a, string b)")); + AnonymousTypes($$""" + + {{FeaturesResources.Types_colon}} + 'a {{FeaturesResources.is_}} new { 'b x, 'b y } + 'b {{FeaturesResources.is_}} (int a, string b) + """)); } [Fact] public async Task TestInRawStringInterpolation_SingleLine() { await TestInMethodAsync( -@"var x = 1; -var s = $""""""Hello world {$$x}""""""", + """" + var x = 1; + var s = $"""Hello world {$$x}""" + """", MainDescription($"({FeaturesResources.local_variable}) int x")); } @@ -8585,8 +9759,10 @@ await TestInMethodAsync( public async Task TestInRawStringInterpolation_SingleLine_MultiBrace() { await TestInMethodAsync( -@"var x = 1; -var s = ${|#0:|}$""""""Hello world {{$$x}}""""""", + """" + var x = 1; + var s = ${|#0:|}$"""Hello world {{$$x}}""" + """", MainDescription($"({FeaturesResources.local_variable}) int x")); } @@ -8594,18 +9770,24 @@ await TestInMethodAsync( public async Task TestInRawStringLiteral_SingleLine_Const() { await TestInClassAsync( -@"const string $$s = """"""Hello world""""""", - MainDescription(@$"({FeaturesResources.constant}) string C.s = """"""Hello world""""""")); + """" + const string $$s = """Hello world""" + """", + MainDescription($"""" + ({FeaturesResources.constant}) string C.s = """Hello world""" + """")); } [Fact] public async Task TestInRawStringInterpolation_MultiLine() { await TestInMethodAsync( -@"var x = 1; -var s = $"""""" -Hello world {$$x} -""""""", + """" + var x = 1; + var s = $""" + Hello world {$$x} + """ + """", MainDescription($"({FeaturesResources.local_variable}) int x")); } @@ -8613,10 +9795,12 @@ Hello world {$$x} public async Task TestInRawStringInterpolation_MultiLine_MultiBrace() { await TestInMethodAsync( -@"var x = 1; -var s = ${|#0:|}$"""""" -Hello world {{$$x}} -""""""", + """" + var x = 1; + var s = ${|#0:|}$""" + Hello world {{$$x}} + """ + """", MainDescription($"({FeaturesResources.local_variable}) int x")); } @@ -8624,23 +9808,29 @@ Hello world {{$$x}} public async Task TestInRawStringLiteral_MultiLine_Const() { await TestInClassAsync( -@"const string $$s = """""" - Hello world - """"""", - MainDescription(@$"({FeaturesResources.constant}) string C.s = """""" - Hello world - """"""")); + """" + const string $$s = """ + Hello world + """ + """", + MainDescription($"""" + ({FeaturesResources.constant}) string C.s = """ + Hello world + """ + """")); } [Fact] public async Task TestArgsInTopLevel() { var markup = -@" -forach (var arg in $$args) -{ -} -"; + """ + + forach (var arg in $$args) + { + } + + """; await TestWithOptionsAsync( Options.Regular, markup, @@ -8651,17 +9841,19 @@ await TestWithOptionsAsync( public async Task TestArgsInNormalProgram() { var markup = -@" -class Program -{ - static void Main(string[] args) - { - foreach (var arg in $$args) - { - } - } -} -"; + """ + + class Program + { + static void Main(string[] args) + { + foreach (var arg in $$args) + { + } + } + } + + """; await TestAsync(markup, MainDescription($"({FeaturesResources.parameter}) string[] args")); @@ -8670,13 +9862,15 @@ await TestAsync(markup, [Fact] public async Task TestParameterInMethodAttributeNameof() { - var source = @" -class Program -{ - [My(nameof($$s))] - void M(string s) { } -} -"; + var source = """ + + class Program + { + [My(nameof($$s))] + void M(string s) { } + } + + """; await TestWithOptionsAsync(Options.Regular.WithLanguageVersion(LanguageVersion.CSharp11), source, MainDescription($"({FeaturesResources.parameter}) string s")); } @@ -8684,12 +9878,14 @@ await TestWithOptionsAsync(Options.Regular.WithLanguageVersion(LanguageVersion.C [Fact] public async Task TestParameterInMethodParameterAttributeNameof() { - var source = @" -class Program -{ - void M([My(nameof($$s))] string s) { } -} -"; + var source = """ + + class Program + { + void M([My(nameof($$s))] string s) { } + } + + """; await TestWithOptionsAsync(Options.Regular.WithLanguageVersion(LanguageVersion.CSharp11), source, MainDescription($"({FeaturesResources.parameter}) string s")); } @@ -8697,16 +9893,18 @@ await TestWithOptionsAsync(Options.Regular.WithLanguageVersion(LanguageVersion.C [Fact] public async Task TestParameterInLocalFunctionAttributeNameof() { - var source = @" -class Program -{ - void M() - { - [My(nameof($$s))] - void local(string s) { } - } -} -"; + var source = """ + + class Program + { + void M() + { + [My(nameof($$s))] + void local(string s) { } + } + } + + """; await TestWithOptionsAsync(Options.Regular.WithLanguageVersion(LanguageVersion.CSharp11), source, MainDescription($"({FeaturesResources.parameter}) string s")); } @@ -8715,20 +9913,22 @@ await TestWithOptionsAsync(Options.Regular.WithLanguageVersion(LanguageVersion.C public async Task TestScopedParameter() { var source = -@"ref struct R { } -class Program -{ - static void F(R r1, scoped R r2, ref R r3, scoped ref R r4, in R r5, scoped in R r6, out R r7, scoped out R r8) - { - r7 = default; - r8 = default; - } - static void Main() - { - R r = default; - $$F(r, r, ref r, ref r, r, r, out r, out r); - } -}"; + """ + ref struct R { } + class Program + { + static void F(R r1, scoped R r2, ref R r3, scoped ref R r4, in R r5, scoped in R r6, out R r7, scoped out R r8) + { + r7 = default; + r8 = default; + } + static void Main() + { + R r = default; + $$F(r, r, ref r, ref r, r, r, out r, out r); + } + } + """; await TestAsync(source, MainDescription($"void Program.F(R r1, scoped R r2, ref R r3, scoped ref R r4, in R r5, scoped in R r6, out R r7, out R r8)")); } @@ -8737,15 +9937,17 @@ await TestAsync(source, public async Task TestScopedLocal() { var source = -@"class Program -{ - static void Main() - { - int i = 0; - scoped ref int r = ref i; - i = $$r; - } -}"; + """ + class Program + { + static void Main() + { + int i = 0; + scoped ref int r = ref i; + i = $$r; + } + } + """; await TestAsync(source, MainDescription($"({FeaturesResources.local_variable}) scoped ref int r")); } @@ -8797,7 +9999,7 @@ await TestAsync(source, public async Task TestUsingAliasToType1() { var source = -@"using X = $$int;"; + @"using X = $$int;"; await TestAsync(source, MainDescription($"struct System.Int32")); } @@ -8806,7 +10008,7 @@ await TestAsync(source, public async Task TestUsingAliasToType1_A() { var source = -@"using $$X = int;"; + @"using $$X = int;"; await TestAsync(source, MainDescription($"struct System.Int32")); } @@ -8815,7 +10017,7 @@ await TestAsync(source, public async Task TestUsingAliasToType2() { var source = -@"using X = ($$int a, int b);"; + @"using X = ($$int a, int b);"; await TestAsync(source, MainDescription($"struct System.Int32")); } @@ -8824,7 +10026,7 @@ await TestAsync(source, public async Task TestUsingAliasToType2_A() { var source = -@"using $$X = (int a, int b);"; + @"using $$X = (int a, int b);"; await TestAsync(source, MainDescription($"(int a, int b)")); } @@ -8833,7 +10035,7 @@ await TestAsync(source, public async Task TestUsingAliasToType3() { var source = -@"using X = $$(int a, int b);"; + @"using X = $$(int a, int b);"; await TestAsync(source); } @@ -8841,7 +10043,7 @@ public async Task TestUsingAliasToType3() public async Task TestUsingAliasToType4() { var source = -@"using unsafe X = $$delegate*;"; + @"using unsafe X = $$delegate*;"; await TestAsync(source); } @@ -8849,7 +10051,7 @@ public async Task TestUsingAliasToType4() public async Task TestUsingAliasToType4_A() { var source = -@"using unsafe $$X = delegate*;"; + @"using unsafe $$X = delegate*;"; await TestAsync(source, MainDescription($"delegate*")); } @@ -8858,7 +10060,7 @@ await TestAsync(source, public async Task TestUsingAliasToType5() { var source = -@"using unsafe X = $$int*;"; + @"using unsafe X = $$int*;"; await TestAsync(source, MainDescription($"struct System.Int32")); } @@ -8867,7 +10069,7 @@ await TestAsync(source, public async Task TestUsingAliasToType5_A() { var source = -@"using unsafe $$X = int*;"; + @"using unsafe $$X = int*;"; await TestAsync(source, MainDescription($"int*")); } @@ -8876,7 +10078,7 @@ await TestAsync(source, public async Task TestCollectionExpression_Start() { var source = -"int[] x = $$[1, 2]"; + "int[] x = $$[1, 2]"; await TestAsync(source, MainDescription($"int[]")); } @@ -8885,7 +10087,7 @@ await TestAsync(source, public async Task TestCollectionExpression_Middle() { var source = -"int[] x = [1 $$, 2]"; + "int[] x = [1 $$, 2]"; await TestAsync(source); } @@ -8893,7 +10095,7 @@ public async Task TestCollectionExpression_Middle() public async Task TestCollectionExpression_End() { var source = -"int[] x = [1, 2]$$"; + "int[] x = [1, 2]$$"; await TestAsync(source, MainDescription($"int[]")); } @@ -8902,7 +10104,7 @@ await TestAsync(source, public async Task TestCollectionExpression_Start_Typeless() { var source = -"var x = $$[1, 2]"; + "var x = $$[1, 2]"; await TestAsync(source); } @@ -8921,9 +10123,11 @@ await VerifyWithMscorlib45Async(markup, new[] { MainDescription(description), AnonymousTypes( -$@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} new {{ string @string }}") + $$""" + + {{FeaturesResources.Types_colon}} + 'a {{FeaturesResources.is_}} new { string @string } + """) }); } From e0f7781614f6a67a3e8307593918187f668cf235 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 10:42:37 -0800 Subject: [PATCH 275/508] Add support for running lightbulbs even when not fully loaded. --- .../Core.Wpf/Suggestions/SuggestedActionsSource.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs index 91222cfcb6840..549acf728a56a 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs @@ -156,6 +156,10 @@ private void OnTextViewClosed(object sender, EventArgs e) public async Task GetSuggestedActionCategoriesAsync( ISuggestedActionCategorySet requestedActionCategories, SnapshotSpan range, CancellationToken cancellationToken) { + // Make sure we're explicitly on the background, to do as much as possible in a non-blocking fashion. + await TaskScheduler.Default; + cancellationToken.ThrowIfCancellationRequested(); + // This function gets called immediately after operations like scrolling. We want to wait just a small // amount to ensure that we don't immediately start consuming CPU/memory which then impedes the very // action the user is trying to perform. To accomplish this, we wait 100ms. That's longer than normal @@ -177,12 +181,6 @@ private void OnTextViewClosed(object sender, EventArgs e) if (workspace == null) return null; - // never show light bulb if solution is not fully loaded yet - if (!await workspace.Services.GetRequiredService().IsFullyLoadedAsync(cancellationToken).ConfigureAwait(false)) - return null; - - cancellationToken.ThrowIfCancellationRequested(); - using var asyncToken = state.Target.Owner.OperationListener.BeginAsyncOperation(nameof(GetSuggestedActionCategoriesAsync)); var document = range.Snapshot.GetOpenTextDocumentInCurrentContextWithChanges(); if (document == null) From 08b3ecc2752e6050e0d10b023a34e045715b687e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 11:47:38 -0800 Subject: [PATCH 276/508] Faster syncing and more caching --- .../Core/Remote/SolutionChecksumUpdater.cs | 76 ++++++++++--------- .../Solution/Solution_SemanticModelCaching.cs | 67 ++++++++-------- .../IRemoteAssetSynchronizationService.cs | 10 ++- 3 files changed, 75 insertions(+), 78 deletions(-) diff --git a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs index 057bbb6c73925..858ed943754f6 100644 --- a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs +++ b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs @@ -3,13 +3,16 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Notification; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote; @@ -62,20 +65,22 @@ public SolutionChecksumUpdater( _workspace = workspace; _documentTrackingService = workspace.Services.GetRequiredService(); - _textChangeQueue = new AsyncBatchingWorkQueue<(Document oldDocument, Document newDocument)>( + _synchronizeWorkspaceQueue = new AsyncBatchingWorkQueue( DelayTimeSpan.NearImmediate, - SynchronizeTextChangesAsync, + SynchronizePrimaryWorkspaceAsync, listener, shutdownToken); - _synchronizeWorkspaceQueue = new AsyncBatchingWorkQueue( - DelayTimeSpan.NearImmediate, - SynchronizePrimaryWorkspaceAsync, + // Text changes and active doc info are tiny messages. So attempt to send them immediately. Just batching + // things up if we get a flurry of notifications. + _textChangeQueue = new AsyncBatchingWorkQueue<(Document oldDocument, Document newDocument)>( + TimeSpan.Zero, + SynchronizeTextChangesAsync, listener, shutdownToken); _synchronizeActiveDocumentQueue = new AsyncBatchingWorkQueue( - DelayTimeSpan.NearImmediate, + TimeSpan.Zero, SynchronizeActiveDocumentAsync, listener, shutdownToken); @@ -203,55 +208,52 @@ private async ValueTask SynchronizeTextChangesAsync( ImmutableSegmentedList<(Document oldDocument, Document newDocument)> values, CancellationToken cancellationToken) { - foreach (var (oldDocument, newDocument) in values) - { - cancellationToken.ThrowIfCancellationRequested(); - await SynchronizeTextChangesAsync(oldDocument, newDocument, cancellationToken).ConfigureAwait(false); - } + var client = await RemoteHostClient.TryGetClientAsync(_workspace, cancellationToken).ConfigureAwait(false); + if (client == null) + return; - return; + // this pushes text changes to the remote side if it can. this is purely perf optimization. whether this + // pushing text change worked or not doesn't affect feature's functionality. + // + // this basically see whether it can cheaply find out text changes between 2 snapshots, if it can, it will + // send out that text changes to remote side. + // + // the remote side, once got the text change, will again see whether it can use that text change information + // without any high cost and create new snapshot from it. + // + // otherwise, it will do the normal behavior of getting full text from VS side. this optimization saves + // times we need to do full text synchronization for typing scenario. + using var _ = ArrayBuilder<(DocumentId id, Checksum textChecksum, ImmutableArray changes)>.GetInstance(out var builder); - async ValueTask SynchronizeTextChangesAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken) + foreach (var (oldDocument, newDocument) in values) { - // this pushes text changes to the remote side if it can. this is purely perf optimization. whether this - // pushing text change worked or not doesn't affect feature's functionality. - // - // this basically see whether it can cheaply find out text changes between 2 snapshots, if it can, it will - // send out that text changes to remote side. - // - // the remote side, once got the text change, will again see whether it can use that text change information - // without any high cost and create new snapshot from it. - // - // otherwise, it will do the normal behavior of getting full text from VS side. this optimization saves - // times we need to do full text synchronization for typing scenario. - if (!oldDocument.TryGetText(out var oldText) || !newDocument.TryGetText(out var newText)) { // we only support case where text already exist - return; + continue; } // Avoid allocating text before seeing if we can bail out. var changeRanges = newText.GetChangeRanges(oldText).AsImmutable(); if (changeRanges.Length == 0) - return; + continue; // no benefit here. pulling from remote host is more efficient if (changeRanges is [{ Span.Length: var singleChangeLength }] && singleChangeLength == oldText.Length) - return; - - var textChanges = newText.GetTextChanges(oldText).AsImmutable(); - - var client = await RemoteHostClient.TryGetClientAsync(_workspace, cancellationToken).ConfigureAwait(false); - if (client == null) - return; + continue; var state = await oldDocument.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); - await client.TryInvokeAsync( - (service, cancellationToken) => service.SynchronizeTextAsync(oldDocument.Id, state.Text, textChanges, cancellationToken), - cancellationToken).ConfigureAwait(false); + var textChanges = newText.GetTextChanges(oldText).AsImmutable(); + builder.Add((oldDocument.Id, state.Text, textChanges)); } + + if (builder.Count == 0) + return; + + await client.TryInvokeAsync( + (service, cancellationToken) => service.SynchronizeTextAsync(builder.ToImmutableAndClear(), cancellationToken), + cancellationToken).ConfigureAwait(false); } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs index 32bebdc86d8d5..74360acfef28d 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs @@ -2,58 +2,51 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Concurrent; +using System.Collections.Generic; +using Microsoft.CodeAnalysis.PooledObjects; +using Roslyn.Utilities; + namespace Microsoft.CodeAnalysis; public partial class Solution { /// - /// Strongly held reference to the semantic model for the active document. By strongly holding onto it, we ensure - /// that it won't be GC'ed between feature requests from multiple features that care about it. As the active - /// document has the most features running on it continuously, we definitely do not want to drop this. Note: this - /// cached value is only to help with performance. Not with correctness. Importantly, the concept of 'active - /// document' is itself fundamentally racy. That's ok though as we simply want to settle on these semantic models - /// settling into a stable state over time. We don't need to be perfect about it. They are intentionally not - /// locked either as we would only have contention right when switching to a new active document, and we would still - /// latch onto the new document very quickly. + /// Strongly held reference to the semantic models for the active document (and its related documents linked into + /// other projects). By strongly holding onto then, we ensure that it won't be GC'ed between feature requests from + /// multiple features that care about it. As the active document has the most features running on it continuously, + /// we definitely do not want to drop this. Note: this cached value is only to help with performance. Not with + /// correctness. Importantly, the concept of 'active document' is itself fundamentally racy. That's ok though as + /// we simply want to settle on these semantic models settling into a stable state over time. We don't need to be + /// perfect about it. /// - /// - /// It is fine for these fields to never be read. The purpose is simply to keep a strong reference around so that - /// they will not be GC'ed as long as the active document stays the same. - /// -#pragma warning disable IDE0052 // Remove unread private members - private SemanticModel? _activeDocumentSemanticModel; - - /// - private SemanticModel? _activeDocumentNullableDisabledSemanticModel; -#pragma warning restore IDE0052 // Remove unread private members - - internal void OnSemanticModelObtained(DocumentId documentId, SemanticModel semanticModel) + private readonly ConcurrentDictionary> _activeSemanticModels = []; + + internal void OnSemanticModelObtained( + DocumentId documentId, SemanticModel semanticModel) { var service = this.Services.GetRequiredService(); var activeDocumentId = service.TryGetActiveDocument(); if (activeDocumentId is null) { - // no active document? then clear out any caches we have. - _activeDocumentSemanticModel = null; - _activeDocumentNullableDisabledSemanticModel = null; - } - else if (activeDocumentId != documentId) - { - // We have an active document, but we just obtained the semantic model for some other doc. Nothing to do - // here, we don't want to cache this. + // No known active document. Clear out any cached semantic models we have. + _activeSemanticModels.Clear(); return; } - else + + using var _1 = PooledHashSet.GetInstance(out var relatedDocumentIdsSet); + relatedDocumentIdsSet.AddRange(this.GetRelatedDocumentIds(activeDocumentId)); + + // Clear out any entries for cached documents that are no longer active. + foreach (var (existingDocId, _) in _activeSemanticModels) { - // Ok. We just obtained the semantic model for the active document. Make a strong reference to it so that - // other features that wake up for this active document are sure to be able to reuse the same one. -#pragma warning disable RSEXPERIMENTAL001 // sym-shipped usage of experimental API - if (semanticModel.NullableAnalysisIsDisabled) - _activeDocumentNullableDisabledSemanticModel = semanticModel; - else - _activeDocumentSemanticModel = semanticModel; -#pragma warning restore RSEXPERIMENTAL001 + if (!relatedDocumentIdsSet.Contains(existingDocId)) + _activeSemanticModels.TryRemove(existingDocId, out _); } + + // If this is a semantic model for the active document (or any of its related documents), cache it. + if (relatedDocumentIdsSet.Contains(documentId)) + _activeSemanticModels.GetOrAdd(documentId, static _ => []).Add(semanticModel); } } diff --git a/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs b/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs index 916e9357792d8..e14c426173d38 100644 --- a/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs +++ b/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs @@ -21,11 +21,13 @@ internal interface IRemoteAssetSynchronizationService /// Synchronize the text changes made by a user to a particular document as they are editing it. By sending over /// the text changes as they happen, we can attempt to 'prime' the remote asset cache with a final that is built based off of retrieving the remote source text with a checksum corresponding - /// to and then applying the to it. Then, when - /// the next remote call comes in for the new solution snapshot, it can hopefully just pluck that text out of the - /// cache without having to sync the entire contents of the file over. + /// to baseTextChecksum and then applying the textChanges to it. Then, when the next remote call comes in for the + /// new solution snapshot, it can hopefully just pluck that text out of the cache without having to sync the + /// entire contents of the file over. /// - ValueTask SynchronizeTextAsync(DocumentId documentId, Checksum baseTextChecksum, ImmutableArray textChanges, CancellationToken cancellationToken); + ValueTask SynchronizeTextAsync( + ImmutableArray<(DocumentId documentId, Checksum baseTextChecksum, ImmutableArray textChanges)>, + CancellationToken cancellationToken); /// /// Synchronize over what the user's current active document is that they're editing. This can then be used by the From 18e3bc677698697263b05b4ab2717050ae75402f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 11:52:11 -0800 Subject: [PATCH 277/508] Remove side --- .../Core/Remote/SolutionChecksumUpdater.cs | 2 +- .../IRemoteAssetSynchronizationService.cs | 4 +-- .../RemoteAssetSynchronizationService.cs | 35 +++++++++++-------- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs index 858ed943754f6..f88ba465bca6a 100644 --- a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs +++ b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs @@ -253,7 +253,7 @@ private async ValueTask SynchronizeTextChangesAsync( return; await client.TryInvokeAsync( - (service, cancellationToken) => service.SynchronizeTextAsync(builder.ToImmutableAndClear(), cancellationToken), + (service, cancellationToken) => service.SynchronizeTextChangesAsync(builder.ToImmutableAndClear(), cancellationToken), cancellationToken).ConfigureAwait(false); } } diff --git a/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs b/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs index e14c426173d38..03e5d8e76a289 100644 --- a/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs +++ b/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs @@ -25,8 +25,8 @@ internal interface IRemoteAssetSynchronizationService /// new solution snapshot, it can hopefully just pluck that text out of the cache without having to sync the /// entire contents of the file over. /// - ValueTask SynchronizeTextAsync( - ImmutableArray<(DocumentId documentId, Checksum baseTextChecksum, ImmutableArray textChanges)>, + ValueTask SynchronizeTextChangesAsync( + ImmutableArray<(DocumentId documentId, Checksum baseTextChecksum, ImmutableArray textChanges)> changes, CancellationToken cancellationToken); /// diff --git a/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs b/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs index c86c7482b169d..8bec38df958f1 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs @@ -48,30 +48,35 @@ public ValueTask SynchronizeActiveDocumentAsync(DocumentId? documentId, Cancella return ValueTaskFactory.CompletedTask; } - public ValueTask SynchronizeTextAsync(DocumentId documentId, Checksum baseTextChecksum, ImmutableArray textChanges, CancellationToken cancellationToken) + public ValueTask SynchronizeTextChangesAsync( + ImmutableArray<(DocumentId documentId, Checksum baseTextChecksum, ImmutableArray textChanges)> changes, + CancellationToken cancellationToken) { return RunServiceAsync(async cancellationToken => { var workspace = GetWorkspace(); - using (RoslynLogger.LogBlock(FunctionId.RemoteHostService_SynchronizeTextAsync, Checksum.GetChecksumLogInfo, baseTextChecksum, cancellationToken)) + using (RoslynLogger.LogBlock(FunctionId.RemoteHostService_SynchronizeTextAsync, cancellationToken)) { - // Try to get the text associated with baseTextChecksum - var text = await TryGetSourceTextAsync(WorkspaceManager, workspace, documentId, baseTextChecksum, cancellationToken).ConfigureAwait(false); - if (text == null) + foreach (var (documentId, baseTextChecksum, textChanges) in changes) { - // it won't bring in base text if it is not there already. - // text needed will be pulled in when there is request - return; - } + // Try to get the text associated with baseTextChecksum + var text = await TryGetSourceTextAsync(WorkspaceManager, workspace, documentId, baseTextChecksum, cancellationToken).ConfigureAwait(false); + if (text == null) + { + // it won't bring in base text if it is not there already. + // text needed will be pulled in when there is request + continue; + } - // Now attempt to manually apply the edit, producing the new forked text. Store that directly in - // the asset cache so that future calls to retrieve it can do so quickly, without synchronizing over - // the entire document. - var newText = text.WithChanges(textChanges); - var newSerializableText = new SerializableSourceText(newText, newText.GetContentHash()); + // Now attempt to manually apply the edit, producing the new forked text. Store that directly in + // the asset cache so that future calls to retrieve it can do so quickly, without synchronizing over + // the entire document. + var newText = text.WithChanges(textChanges); + var newSerializableText = new SerializableSourceText(newText, newText.GetContentHash()); - WorkspaceManager.SolutionAssetCache.GetOrAdd(newSerializableText.ContentChecksum, newSerializableText); + WorkspaceManager.SolutionAssetCache.GetOrAdd(newSerializableText.ContentChecksum, newSerializableText); + } } return; From 84e2b49e3ef396a68ef83623fdd982521e34e313 Mon Sep 17 00:00:00 2001 From: tmat Date: Tue, 22 Oct 2024 19:48:29 -0700 Subject: [PATCH 278/508] Update VSSDK and Gladstone versions --- eng/Directory.Packages.props | 63 ++++++++++++++++++------------------ eng/Versions.props | 2 +- 2 files changed, 33 insertions(+), 32 deletions(-) diff --git a/eng/Directory.Packages.props b/eng/Directory.Packages.props index d75a411948dc8..04b6244650e96 100644 --- a/eng/Directory.Packages.props +++ b/eng/Directory.Packages.props @@ -8,7 +8,7 @@ <_BasicReferenceAssembliesVersion>1.7.9 4.8.0-3.final - 17.10.191 + 17.12.145-preview 9.0.0-rc.2.24462.10 6.0.0-rtm.21518.12 7.0.0-alpha.1.22060.1 @@ -22,7 +22,7 @@ 8.0.10 <_xunitVersion>2.6.6 2.1.0 - 17.10.2079 + 17.13.2-preview1 - + @@ -91,32 +91,33 @@ - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + 4.8.0-3.final - 17.12.145-preview + 17.10.191 9.0.0-rc.2.24462.10 6.0.0-rtm.21518.12 7.0.0-alpha.1.22060.1 @@ -22,7 +22,7 @@ 8.0.10 <_xunitVersion>2.6.6 2.1.0 - 17.13.2-preview1 + 17.10.2079 - - - + + + @@ -105,19 +105,19 @@ - + - + - - + + - + 4.8.0-3.final - 17.10.191 + 17.12.145-preview 9.0.0-rc.2.24462.10 6.0.0-rtm.21518.12 7.0.0-alpha.1.22060.1 From 2a067ba3c3ae9df7450c591a2f3073f4b8f8b8df Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 12:24:56 -0800 Subject: [PATCH 281/508] Switch to immutable interlocked --- .../Solution/Solution_SemanticModelCaching.cs | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs index 74360acfef28d..a90de2457f63b 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs @@ -4,6 +4,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; +using System.Collections.Immutable; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; @@ -13,14 +14,14 @@ public partial class Solution { /// /// Strongly held reference to the semantic models for the active document (and its related documents linked into - /// other projects). By strongly holding onto then, we ensure that it won't be GC'ed between feature requests from - /// multiple features that care about it. As the active document has the most features running on it continuously, - /// we definitely do not want to drop this. Note: this cached value is only to help with performance. Not with - /// correctness. Importantly, the concept of 'active document' is itself fundamentally racy. That's ok though as - /// we simply want to settle on these semantic models settling into a stable state over time. We don't need to be - /// perfect about it. + /// other projects). By strongly holding onto then, we ensure that they won't be GC'ed between feature requests + /// from multiple features that care about it. As the active document has the most features running on it + /// continuously, we definitely do not want to drop this. Note: this cached value is only to help with performance. + /// Not with correctness. Importantly, the concept of 'active document' is itself fundamentally racy. That's ok + /// though as we simply want to settle on these semantic models settling into a stable state over time. We don't + /// need to be perfect about it. /// - private readonly ConcurrentDictionary> _activeSemanticModels = []; + private ImmutableDictionary> _activeSemanticModels = ImmutableDictionary>.Empty; internal void OnSemanticModelObtained( DocumentId documentId, SemanticModel semanticModel) @@ -31,7 +32,7 @@ internal void OnSemanticModelObtained( if (activeDocumentId is null) { // No known active document. Clear out any cached semantic models we have. - _activeSemanticModels.Clear(); + _activeSemanticModels = _activeSemanticModels.Clear(); return; } @@ -42,11 +43,17 @@ internal void OnSemanticModelObtained( foreach (var (existingDocId, _) in _activeSemanticModels) { if (!relatedDocumentIdsSet.Contains(existingDocId)) - _activeSemanticModels.TryRemove(existingDocId, out _); + ImmutableInterlocked.TryRemove(ref _activeSemanticModels, existingDocId, out _); } // If this is a semantic model for the active document (or any of its related documents), cache it. if (relatedDocumentIdsSet.Contains(documentId)) - _activeSemanticModels.GetOrAdd(documentId, static _ => []).Add(semanticModel); + { + ImmutableInterlocked.AddOrUpdate( + ref _activeSemanticModels, + documentId, + addValueFactory: documentId => [semanticModel], + updateValueFactory: (_, set) => set.Add(semanticModel)); + } } } From c9b7528be1a2dec1ce3cfea9123e4a11b2710ff0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 12:26:00 -0800 Subject: [PATCH 282/508] Switch to array --- .../Workspace/Solution/Solution_SemanticModelCaching.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs index a90de2457f63b..edd6b599ddb3a 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs @@ -36,13 +36,12 @@ internal void OnSemanticModelObtained( return; } - using var _1 = PooledHashSet.GetInstance(out var relatedDocumentIdsSet); - relatedDocumentIdsSet.AddRange(this.GetRelatedDocumentIds(activeDocumentId)); + var relatedDocumentIds = this.GetRelatedDocumentIds(activeDocumentId); // Clear out any entries for cached documents that are no longer active. foreach (var (existingDocId, _) in _activeSemanticModels) { - if (!relatedDocumentIdsSet.Contains(existingDocId)) + if (!relatedDocumentIds.Contains(existingDocId)) ImmutableInterlocked.TryRemove(ref _activeSemanticModels, existingDocId, out _); } From 8e0b41f9087514c773653df0d20841825d5cac26 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 12:29:48 -0800 Subject: [PATCH 283/508] Simplify --- .../Workspace/Solution/Solution_SemanticModelCaching.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs index edd6b599ddb3a..526c4f9c80172 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs @@ -2,10 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Concurrent; -using System.Collections.Generic; using System.Collections.Immutable; -using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; @@ -46,7 +43,7 @@ internal void OnSemanticModelObtained( } // If this is a semantic model for the active document (or any of its related documents), cache it. - if (relatedDocumentIdsSet.Contains(documentId)) + if (relatedDocumentIds.Contains(documentId)) { ImmutableInterlocked.AddOrUpdate( ref _activeSemanticModels, From 856a1a2e71579220ee4bc3bf8ea2cc75d6decc66 Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Tue, 12 Nov 2024 12:43:29 -0800 Subject: [PATCH 284/508] Change tagging to free a SegmentedList back to a pool (#75873) The GetTags calls into our taggers require passing back an IEnumerable. Previously, we created a SegmentedList to hold all the data and then passed that back. However, that suffers from not being able to add back the returned list into a pool. Instead, switch to using yield return over the populated segmented list as that will allow the list to be freed back to the pool upon completion. Utilizing the yield state machinery does incur an allocation, but this should be a much smaller allocation than the potentially very large arrays that our taggers were returning. --- ...assificationBufferTaggerProvider.Tagger.cs | 16 ++++++--- .../Core/Tagging/EfficientTagger.cs | 18 +++++++--- .../Portable/Utilities/SegmentedListPool.cs | 35 ------------------- 3 files changed, 25 insertions(+), 44 deletions(-) diff --git a/src/EditorFeatures/Core/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs b/src/EditorFeatures/Core/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs index 138d4e1317667..26e47c2026d5c 100644 --- a/src/EditorFeatures/Core/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs +++ b/src/EditorFeatures/Core/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs @@ -89,10 +89,18 @@ public IEnumerable> GetTags(NormalizedSnapshotSpanC return []; } - private static IReadOnlyList> GetIntersectingTags(NormalizedSnapshotSpanCollection spans, TagSpanIntervalTree cachedTags) - => SegmentedListPool>.ComputeList( - static (args, tags) => args.cachedTags.AddIntersectingTagSpans(args.spans, tags), - (cachedTags, spans)); + private static IEnumerable> GetIntersectingTags(NormalizedSnapshotSpanCollection spans, TagSpanIntervalTree cachedTags) + { + using var pooledObject = SegmentedListPool.GetPooledList>(out var list); + + cachedTags.AddIntersectingTagSpans(spans, list); + + // Use yield return mechanism to allow the segmented list to get returned back to the + // pool after usage. This does cause an allocation for the yield state machinery, but + // that is better than not freeing a potentially large segmented list back to the pool. + foreach (var item in list) + yield return item; + } IEnumerable> IAccurateTagger.GetAllTags(NormalizedSnapshotSpanCollection spans, CancellationToken cancellationToken) => GetAllTags(spans, cancellationToken); diff --git a/src/EditorFeatures/Core/Tagging/EfficientTagger.cs b/src/EditorFeatures/Core/Tagging/EfficientTagger.cs index 8349cb739bfa7..af187baaba78b 100644 --- a/src/EditorFeatures/Core/Tagging/EfficientTagger.cs +++ b/src/EditorFeatures/Core/Tagging/EfficientTagger.cs @@ -30,12 +30,20 @@ IEnumerable> ITagger.GetTags(NormalizedSnapshotSpanCollecti => GetTags(spans); /// - /// Default impl of the core interface. Forces an allocation. + /// Default impl of the core interface. /// - public IReadOnlyList> GetTags(NormalizedSnapshotSpanCollection spans) - => SegmentedListPool>.ComputeList( - static (args, tags) => args.@this.AddTags(args.spans, tags), - (@this: this, spans)); + public IEnumerable> GetTags(NormalizedSnapshotSpanCollection spans) + { + using var pooledObject = SegmentedListPool.GetPooledList>(out var list); + + AddTags(spans, list); + + // Use yield return mechanism to allow the segmented list to get returned back to the + // pool after usage. This does cause an allocation for the yield state machinery, but + // that is better than not freeing a potentially large segmented list back to the pool. + foreach (var item in list) + yield return item; + } public virtual event EventHandler? TagsChanged; diff --git a/src/Workspaces/Core/Portable/Utilities/SegmentedListPool.cs b/src/Workspaces/Core/Portable/Utilities/SegmentedListPool.cs index 08225e1b37496..d7def7e7395ce 100644 --- a/src/Workspaces/Core/Portable/Utilities/SegmentedListPool.cs +++ b/src/Workspaces/Core/Portable/Utilities/SegmentedListPool.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; using Microsoft.CodeAnalysis.Collections; namespace Microsoft.CodeAnalysis.Utilities; @@ -32,37 +30,4 @@ internal static PooledObject> GetPooledList(out SegmentedLis classifiedSpans = pooledObject.Object; return pooledObject; } - - /// - /// Computes a list of results based on a provided callback. The callback is passed - /// a to add results to, and additional args to assist the process. If no items - /// are added to the list, then the singleton will be returned. Otherwise the - /// instance will be returned. - /// - public static IReadOnlyList ComputeList( - Action> addItems, - TArgs args, - // Only used to allow type inference to work at callsite - T? _) - { - var pooledObject = GetPooledList(out var list); - - addItems(args, list); - - // If the result was empty, return it to the pool, and just pass back the empty array singleton. - if (pooledObject.Object.Count == 0) - { - pooledObject.Dispose(); - return []; - } - - // Otherwise, do not dispose. Caller needs this value to stay alive. - return list; - } -} - -internal static class SegmentedListPool -{ - public static IReadOnlyList ComputeList(Action> addItems, TArgs args) - => SegmentedListPool.ComputeList(addItems, args, _: default); } From 059e27249b43b1ac013ab4693980e7d13999aabf Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 13:14:43 -0800 Subject: [PATCH 285/508] Add embedded classification support for locals passed into an annotated api at a later point --- .../SemanticClassifierTests_Json.cs | 165 ++++++++++++++++++ .../CSharpEmbeddedLanguagesProvider.cs | 12 +- .../EmbeddedLanguageDetector.cs | 161 +++++++++++++---- .../EmbeddedLanguages/EmbeddedLanguageInfo.cs | 3 + .../VisualBasicEmbeddedLanguagesProvider.vb | 3 +- .../CSharpBlockFactsService.cs | 11 +- .../VisualBasicBlockFactsService.vb | 2 +- 7 files changed, 303 insertions(+), 54 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests_Json.cs b/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests_Json.cs index 44c63fe81ea29..f8e81bb763aca 100644 --- a/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests_Json.cs +++ b/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests_Json.cs @@ -222,6 +222,35 @@ void Goo() Json.Array("]")); } + [Theory, CombinatorialData] + public async Task TestJsonOnApiWithStringSyntaxAttribute_Field_FromLocal(TestHost testHost) + { + await TestAsync( + """ + using System.Diagnostics.CodeAnalysis; + + class Program + { + [StringSyntax(StringSyntaxAttribute.Json)] + private string field; + void Goo() + { + [|var v = @"[{ 'goo': 0}]";|] + this.field = v; + } + } + """ + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp, + testHost, + Keyword("var"), + Json.Array("["), + Json.Object("{"), + Json.PropertyName("'goo'"), + Json.Punctuation(":"), + Json.Number("0"), + Json.Object("}"), + Json.Array("]")); + } + [Theory, CombinatorialData] [WorkItem("https://github.com/dotnet/roslyn/issues/74020")] public async Task TestJsonOnApiWithStringSyntaxAttribute_OtherLanguage_Field(TestHost testHost) @@ -302,6 +331,37 @@ void Goo() Json.Array("]")); } + [Theory, CombinatorialData] + public async Task TestJsonOnApiWithStringSyntaxAttribute_Argument_FromLocal(TestHost testHost) + { + await TestAsync( + """ + using System.Diagnostics.CodeAnalysis; + + class Program + { + private void M([StringSyntax(StringSyntaxAttribute.Json)] string p) + { + } + + void Goo() + { + [|var v = @"[{ 'goo': 0}]";|] + M(v); + } + } + """ + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp, + testHost, + Keyword("var"), + Json.Array("["), + Json.Object("{"), + Json.PropertyName("'goo'"), + Json.Punctuation(":"), + Json.Number("0"), + Json.Object("}"), + Json.Array("]")); + } + [Theory, CombinatorialData] [WorkItem("https://github.com/dotnet/roslyn/issues/69237")] public async Task TestJsonOnApiWithStringSyntaxAttribute_PropertyInitializer(TestHost testHost) @@ -335,6 +395,40 @@ void Goo() Json.Array("]")); } + [Theory, CombinatorialData] + [WorkItem("https://github.com/dotnet/roslyn/issues/69237")] + public async Task TestJsonOnApiWithStringSyntaxAttribute_PropertyInitializer_FromLocal(TestHost testHost) + { + await TestAsync( + """" + using System.Diagnostics.CodeAnalysis; + + public sealed record Foo + { + [StringSyntax(StringSyntaxAttribute.Json)] + public required string Bar { get; set; } + } + + class Program + { + void Goo() + { + [|var v = """[1, 2, 3]""";|] + var f = new Foo { Bar = v }; + } + } + """" + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp, + testHost, + Keyword("var"), + Json.Array("["), + Json.Number("1"), + Json.Punctuation(","), + Json.Number("2"), + Json.Punctuation(","), + Json.Number("3"), + Json.Array("]")); + } + [Theory, CombinatorialData] [WorkItem("https://github.com/dotnet/roslyn/issues/69237")] public async Task TestJsonOnApiWithStringSyntaxAttribute_WithExpression(TestHost testHost) @@ -368,4 +462,75 @@ void Goo() Json.Number("3"), Json.Array("]")); } + + [Theory, CombinatorialData] + [WorkItem("https://github.com/dotnet/roslyn/issues/69237")] + public async Task TestJsonOnApiWithStringSyntaxAttribute_WithExpression_FromLocal(TestHost testHost) + { + await TestAsync( + """" + using System.Diagnostics.CodeAnalysis; + + public sealed record Foo + { + [StringSyntax(StringSyntaxAttribute.Json)] + public required string Bar { get; set; } + } + + class Program + { + void Goo() + { + var f = new Foo { Bar = "" }; + [|var v = """[1, 2, 3]""";|] + f = f with { Bar = v }; + } + } + """" + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp, + testHost, + Keyword("var"), + Json.Array("["), + Json.Number("1"), + Json.Punctuation(","), + Json.Number("2"), + Json.Punctuation(","), + Json.Number("3"), + Json.Array("]")); + } + + [Theory, CombinatorialData] + [WorkItem("https://github.com/dotnet/roslyn/issues/69237")] + public async Task TestJsonOnApiWithStringSyntaxAttribute_WithExpression_FromLocal2(TestHost testHost) + { + await TestAsync( + """" + using System.Diagnostics.CodeAnalysis; + + public sealed record Foo + { + [StringSyntax(StringSyntaxAttribute.Json)] + public required string Bar { get; set; } + } + + class Program + { + void Goo() + { + var f = new Foo { Bar = "" }; + string v; + [|v = """[1, 2, 3]""";|] + f = f with { Bar = v }; + } + } + """" + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp, + testHost, + Local("v"), + Json.Array("["), + Json.Number("1"), + Json.Punctuation(","), + Json.Number("2"), + Json.Punctuation(","), + Json.Number("3"), + Json.Array("]")); + } } diff --git a/src/Features/CSharp/Portable/EmbeddedLanguages/CSharpEmbeddedLanguagesProvider.cs b/src/Features/CSharp/Portable/EmbeddedLanguages/CSharpEmbeddedLanguagesProvider.cs index b9e463b2499b2..1b5fe90712f86 100644 --- a/src/Features/CSharp/Portable/EmbeddedLanguages/CSharpEmbeddedLanguagesProvider.cs +++ b/src/Features/CSharp/Portable/EmbeddedLanguages/CSharpEmbeddedLanguagesProvider.cs @@ -13,20 +13,16 @@ namespace Microsoft.CodeAnalysis.CSharp.EmbeddedLanguages.LanguageServices; [ExportLanguageService(typeof(IEmbeddedLanguagesProvider), LanguageNames.CSharp, ServiceLayer.Default), Shared] -internal class CSharpEmbeddedLanguagesProvider : AbstractEmbeddedLanguagesProvider +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CSharpEmbeddedLanguagesProvider() : AbstractEmbeddedLanguagesProvider(Info) { public static readonly EmbeddedLanguageInfo Info = new( + CSharpBlockFacts.Instance, CSharpSyntaxFacts.Instance, CSharpSemanticFactsService.Instance, CSharpVirtualCharService.Instance); - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpEmbeddedLanguagesProvider() - : base(Info) - { - } - public override string EscapeText(string text, SyntaxToken token) => EmbeddedLanguageUtilities.EscapeText(text, token); } diff --git a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs index 101611416f132..4c7b8fdd95e52 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs @@ -216,58 +216,147 @@ private bool IsEmbeddedLanguageStringLiteralToken( // If we're a string used in a collection initializer, treat this as a lang string if the collection itself // is properly annotated. This is for APIs that do things like DateTime.ParseExact(..., string[] formats, ...); - var container = TryFindContainer(token); - if (container is null) + var tokenParent = TryFindContainer(token); + if (tokenParent is null) return false; - if (syntaxFacts.IsArgument(container.Parent)) + // Check for direct usage of this token that indicates it's an embedded language string. Like passing it to an + // argument which has the StringSyntax attribute on it. + if (IsEmbeddedLanguageStringLiteralToken_Direct( + token, semanticModel, cancellationToken, out identifier)) { - if (IsArgumentWithMatchingStringSyntaxAttribute(semanticModel, container.Parent, cancellationToken, out identifier)) - return true; + return true; } - else if (syntaxFacts.IsAttributeArgument(container.Parent)) + + // Now check for if the literal was assigned to a local that we then see is passed along to something that + // indicates an embedded language string. + + var statement = tokenParent.FirstAncestorOrSelf(syntaxFacts.IsStatement); + if (syntaxFacts.IsSimpleAssignmentStatement(statement)) { - if (IsAttributeArgumentWithMatchingStringSyntaxAttribute(semanticModel, container.Parent, cancellationToken, out identifier)) - return true; + syntaxFacts.GetPartsOfAssignmentStatement(statement, out var left, out var right); + return tokenParent == right && + IsLocalConsumedByApiWithStringSyntaxAttribute( + semanticModel.GetSymbolInfo(left, cancellationToken).GetAnySymbol(), tokenParent, semanticModel, cancellationToken, out identifier); } - else if (syntaxFacts.IsNamedMemberInitializer(container.Parent)) + + if (syntaxFacts.IsEqualsValueClause(tokenParent.Parent) && + syntaxFacts.IsVariableDeclarator(tokenParent.Parent.Parent)) { - syntaxFacts.GetPartsOfNamedMemberInitializer(container.Parent, out var name, out _); - if (IsFieldOrPropertyWithMatchingStringSyntaxAttribute(semanticModel, name, cancellationToken, out identifier)) - return true; + var variableDeclarator = tokenParent.Parent.Parent; + var symbol = + semanticModel.GetDeclaredSymbol(variableDeclarator, cancellationToken) ?? + semanticModel.GetDeclaredSymbol(syntaxFacts.GetIdentifierOfVariableDeclarator(variableDeclarator).GetRequiredParent(), cancellationToken); + + return IsLocalConsumedByApiWithStringSyntaxAttribute(symbol, tokenParent, semanticModel, cancellationToken, out identifier); } - else + + return false; + } + + private bool IsEmbeddedLanguageStringLiteralToken_Direct( + SyntaxToken token, + SemanticModel semanticModel, + CancellationToken cancellationToken, + [NotNullWhen(true)] out string? identifier) + { + identifier = null; + var syntaxFacts = Info.SyntaxFacts; + + var tokenParent = TryFindContainer(token); + if (tokenParent is null) + return false; + + if (syntaxFacts.IsArgument(tokenParent.Parent)) + return IsArgumentWithMatchingStringSyntaxAttribute(semanticModel, tokenParent.Parent, cancellationToken, out identifier); + + if (syntaxFacts.IsAttributeArgument(tokenParent.Parent)) + return IsAttributeArgumentWithMatchingStringSyntaxAttribute(semanticModel, tokenParent.Parent, cancellationToken, out identifier); + + if (syntaxFacts.IsNamedMemberInitializer(tokenParent.Parent)) + { + syntaxFacts.GetPartsOfNamedMemberInitializer(tokenParent.Parent, out var name, out _); + return IsFieldOrPropertyWithMatchingStringSyntaxAttribute(semanticModel, name, cancellationToken, out identifier); + } + + var statement = tokenParent.FirstAncestorOrSelf(syntaxFacts.IsStatement); + if (syntaxFacts.IsSimpleAssignmentStatement(statement)) { - var statement = container.FirstAncestorOrSelf(syntaxFacts.IsStatement); - if (syntaxFacts.IsSimpleAssignmentStatement(statement)) + syntaxFacts.GetPartsOfAssignmentStatement(statement, out var left, out var right); + if (tokenParent == right) { - syntaxFacts.GetPartsOfAssignmentStatement(statement, out var left, out var right); - if (container == right && - IsFieldOrPropertyWithMatchingStringSyntaxAttribute( - semanticModel, left, cancellationToken, out identifier)) - { + if (IsFieldOrPropertyWithMatchingStringSyntaxAttribute(semanticModel, left, cancellationToken, out identifier)) return true; - } } - if (syntaxFacts.IsEqualsValueClause(container.Parent)) + return false; + } + + if (syntaxFacts.IsEqualsValueClause(tokenParent.Parent)) + { + if (syntaxFacts.IsVariableDeclarator(tokenParent.Parent.Parent)) { - if (syntaxFacts.IsVariableDeclarator(container.Parent.Parent)) - { - var variableDeclarator = container.Parent.Parent; - var symbol = - semanticModel.GetDeclaredSymbol(variableDeclarator, cancellationToken) ?? - semanticModel.GetDeclaredSymbol(syntaxFacts.GetIdentifierOfVariableDeclarator(variableDeclarator).GetRequiredParent(), cancellationToken); + var variableDeclarator = tokenParent.Parent.Parent; + var symbol = + semanticModel.GetDeclaredSymbol(variableDeclarator, cancellationToken) ?? + semanticModel.GetDeclaredSymbol(syntaxFacts.GetIdentifierOfVariableDeclarator(variableDeclarator).GetRequiredParent(), cancellationToken); - if (IsFieldOrPropertyWithMatchingStringSyntaxAttribute(symbol, out identifier)) - return true; - } - else if (syntaxFacts.IsEqualsValueOfPropertyDeclaration(container.Parent)) - { - var property = container.Parent.GetRequiredParent(); - var symbol = semanticModel.GetDeclaredSymbol(property, cancellationToken); + if (IsFieldOrPropertyWithMatchingStringSyntaxAttribute(symbol, out identifier)) + return true; + } + else if (syntaxFacts.IsEqualsValueOfPropertyDeclaration(tokenParent.Parent)) + { + var property = tokenParent.Parent.GetRequiredParent(); + var symbol = semanticModel.GetDeclaredSymbol(property, cancellationToken); + + if (IsFieldOrPropertyWithMatchingStringSyntaxAttribute(symbol, out identifier)) + return true; + } + } + + return false; + } + + private bool IsLocalConsumedByApiWithStringSyntaxAttribute( + ISymbol? symbol, + SyntaxNode tokenParent, + SemanticModel semanticModel, + CancellationToken cancellationToken, + [NotNullWhen(true)] out string? identifier) + { + identifier = null; + if (symbol is not ILocalSymbol localSymbol) + return false; + + var blockFacts = this.Info.BlockFacts; + var syntaxFacts = this.Info.SyntaxFacts; + + var block = tokenParent.AncestorsAndSelf().FirstOrDefault(blockFacts.IsExecutableBlock); + if (block is null) + return false; - if (IsFieldOrPropertyWithMatchingStringSyntaxAttribute(symbol, out identifier)) + var localName = localSymbol.Name; + if (localName == "") + return false; + + // Now look at the next statements that follow for usages of this local variable. + foreach (var statement in blockFacts.GetExecutableBlockStatements(block)) + { + cancellationToken.ThrowIfCancellationRequested(); + + foreach (var descendent in statement.DescendantNodesAndSelf()) + { + if (!syntaxFacts.IsIdentifierName(descendent)) + continue; + + var identifierToken = syntaxFacts.GetIdentifierOfIdentifierName(descendent); + if (identifierToken.ValueText != localName) + continue; + + var otherSymbol = semanticModel.GetSymbolInfo(descendent, cancellationToken).GetAnySymbol(); + if (localSymbol.Equals(otherSymbol)) + { + if (IsEmbeddedLanguageStringLiteralToken_Direct(identifierToken, semanticModel, cancellationToken, out identifier)) return true; } } diff --git a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageInfo.cs b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageInfo.cs index 3a820e78e81e0..41a9a81237ecc 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageInfo.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageInfo.cs @@ -11,6 +11,7 @@ namespace Microsoft.CodeAnalysis.EmbeddedLanguages; internal readonly struct EmbeddedLanguageInfo { + public readonly IBlockFacts BlockFacts; public readonly ISyntaxFacts SyntaxFacts; public readonly ISemanticFactsService SemanticFacts; public readonly IVirtualCharService VirtualCharService; @@ -18,10 +19,12 @@ internal readonly struct EmbeddedLanguageInfo public readonly ISyntaxKinds SyntaxKinds => SyntaxFacts.SyntaxKinds; public EmbeddedLanguageInfo( + IBlockFacts blockFacts, ISyntaxFacts syntaxFacts, ISemanticFactsService semanticFacts, IVirtualCharService virtualCharService) { + BlockFacts = blockFacts; SyntaxFacts = syntaxFacts; SemanticFacts = semanticFacts; VirtualCharService = virtualCharService; diff --git a/src/Features/VisualBasic/Portable/EmbeddedLanguages/VisualBasicEmbeddedLanguagesProvider.vb b/src/Features/VisualBasic/Portable/EmbeddedLanguages/VisualBasicEmbeddedLanguagesProvider.vb index 25524d719e0f4..9c5e868e15811 100644 --- a/src/Features/VisualBasic/Portable/EmbeddedLanguages/VisualBasicEmbeddedLanguagesProvider.vb +++ b/src/Features/VisualBasic/Portable/EmbeddedLanguages/VisualBasicEmbeddedLanguagesProvider.vb @@ -11,10 +11,11 @@ Imports Microsoft.CodeAnalysis.VisualBasic.LanguageService Namespace Microsoft.CodeAnalysis.VisualBasic.EmbeddedLanguages.LanguageServices - Friend Class VisualBasicEmbeddedLanguagesProvider + Friend NotInheritable Class VisualBasicEmbeddedLanguagesProvider Inherits AbstractEmbeddedLanguagesProvider Public Shared ReadOnly Info As New EmbeddedLanguageInfo( + VisualBasicBlockFacts.Instance, VisualBasicSyntaxFacts.Instance, VisualBasicSemanticFactsService.Instance, VisualBasicVirtualCharService.Instance) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpBlockFactsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpBlockFactsService.cs index 2d55c80232afa..75391d62201aa 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpBlockFactsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpBlockFactsService.cs @@ -11,11 +11,6 @@ namespace Microsoft.CodeAnalysis.CSharp; [ExportLanguageService(typeof(IBlockFactsService), LanguageNames.CSharp), Shared] -internal class CSharpBlockFactsService : CSharpBlockFacts, IBlockFactsService -{ - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpBlockFactsService() - { - } -} +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CSharpBlockFactsService() : CSharpBlockFacts, IBlockFactsService; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicBlockFactsService.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicBlockFactsService.vb index 296b52ba091cc..92ba91037f8f5 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicBlockFactsService.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicBlockFactsService.vb @@ -9,7 +9,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.LanguageService Namespace Microsoft.CodeAnalysis.VisualBasic - Friend Class VisualBasicBlockFactsService + Friend NotInheritable Class VisualBasicBlockFactsService Inherits VisualBasicBlockFacts Implements IBlockFactsService From b97775c7ea1f9a1261bfbd9d507f2758d22d8e37 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 13:40:08 -0800 Subject: [PATCH 286/508] Make even cleaner --- .../Solution/Solution_SemanticModelCaching.cs | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs index 526c4f9c80172..5b1d7032acbe1 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs @@ -18,7 +18,7 @@ public partial class Solution /// though as we simply want to settle on these semantic models settling into a stable state over time. We don't /// need to be perfect about it. /// - private ImmutableDictionary> _activeSemanticModels = ImmutableDictionary>.Empty; + private ImmutableArray<(DocumentId documentId, SemanticModel semanticModel)> _activeSemanticModels = []; internal void OnSemanticModelObtained( DocumentId documentId, SemanticModel semanticModel) @@ -36,20 +36,12 @@ internal void OnSemanticModelObtained( var relatedDocumentIds = this.GetRelatedDocumentIds(activeDocumentId); // Clear out any entries for cached documents that are no longer active. - foreach (var (existingDocId, _) in _activeSemanticModels) - { - if (!relatedDocumentIds.Contains(existingDocId)) - ImmutableInterlocked.TryRemove(ref _activeSemanticModels, existingDocId, out _); - } + _activeSemanticModels = _activeSemanticModels.RemoveAll( + tuple => !relatedDocumentIds.Contains(tuple.documentId)); - // If this is a semantic model for the active document (or any of its related documents), cache it. - if (relatedDocumentIds.Contains(documentId)) - { - ImmutableInterlocked.AddOrUpdate( - ref _activeSemanticModels, - documentId, - addValueFactory: documentId => [semanticModel], - updateValueFactory: (_, set) => set.Add(semanticModel)); - } + // If this is a semantic model for the active document (or any of its related documents), and we haven't already + // cached it, then do so. + if (!_activeSemanticModels.Contains((documentId, semanticModel))) + _activeSemanticModels = _activeSemanticModels.Add((documentId, semanticModel)); } } From 22583f0f2ecd88c847181c5277c63e2cfb3d813b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 13:48:11 -0800 Subject: [PATCH 287/508] Simplify --- .../Solution/Solution_SemanticModelCaching.cs | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs index 5b1d7032acbe1..2c31bedafc71b 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs @@ -25,23 +25,35 @@ internal void OnSemanticModelObtained( { var service = this.Services.GetRequiredService(); + var localArray = _activeSemanticModels; + + // No need to do anything if we're already caching this pair. + if (localArray.Contains((documentId, semanticModel))) + return; + var activeDocumentId = service.TryGetActiveDocument(); if (activeDocumentId is null) { // No known active document. Clear out any cached semantic models we have. - _activeSemanticModels = _activeSemanticModels.Clear(); - return; + localArray = localArray.Clear(); } + else + { + var relatedDocumentIds = this.GetRelatedDocumentIds(activeDocumentId); - var relatedDocumentIds = this.GetRelatedDocumentIds(activeDocumentId); + // Clear out any entries for cached documents that are no longer active. + localArray = localArray.RemoveAll( + tuple => !relatedDocumentIds.Contains(tuple.documentId)); - // Clear out any entries for cached documents that are no longer active. - _activeSemanticModels = _activeSemanticModels.RemoveAll( - tuple => !relatedDocumentIds.Contains(tuple.documentId)); + // If this is a semantic model for the active document (or any of its related documents), and we haven't already + // cached it, then do so. + if (relatedDocumentIds.Contains(documentId)) + localArray = localArray.Add((documentId, semanticModel)); + } - // If this is a semantic model for the active document (or any of its related documents), and we haven't already - // cached it, then do so. - if (!_activeSemanticModels.Contains((documentId, semanticModel))) - _activeSemanticModels = _activeSemanticModels.Add((documentId, semanticModel)); + // Note: this code is racy. We could have two threads executing the code above, while only one thread will win + // here. We accept that as this code is just intended to help just by making some strong references to semantic + // models to prevent them from being GC'ed. We don't need to be perfect about it. + _activeSemanticModels = localArray; } } From d0aa6395b659ad8e7d841d4c77cefd81745e183a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 13:51:16 -0800 Subject: [PATCH 288/508] Empty array --- .../Workspace/Solution/Solution_SemanticModelCaching.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs index 2c31bedafc71b..d2089855270ba 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs @@ -35,7 +35,7 @@ internal void OnSemanticModelObtained( if (activeDocumentId is null) { // No known active document. Clear out any cached semantic models we have. - localArray = localArray.Clear(); + localArray = []; } else { From 89097251add1fdd17ecc7d4af2e2185116d1089d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 13:52:18 -0800 Subject: [PATCH 289/508] simplify --- .../Solution/Solution_SemanticModelCaching.cs | 25 ++++++------------- 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs index d2089855270ba..242e98734c8a1 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs @@ -32,24 +32,13 @@ internal void OnSemanticModelObtained( return; var activeDocumentId = service.TryGetActiveDocument(); - if (activeDocumentId is null) - { - // No known active document. Clear out any cached semantic models we have. - localArray = []; - } - else - { - var relatedDocumentIds = this.GetRelatedDocumentIds(activeDocumentId); - - // Clear out any entries for cached documents that are no longer active. - localArray = localArray.RemoveAll( - tuple => !relatedDocumentIds.Contains(tuple.documentId)); - - // If this is a semantic model for the active document (or any of its related documents), and we haven't already - // cached it, then do so. - if (relatedDocumentIds.Contains(documentId)) - localArray = localArray.Add((documentId, semanticModel)); - } + var relatedDocumentIds = activeDocumentId is null ? [] : this.GetRelatedDocumentIds(activeDocumentId); + + localArray = localArray.RemoveAll( + tuple => !relatedDocumentIds.Contains(tuple.documentId)); + + if (relatedDocumentIds.Contains(documentId)) + localArray = localArray.Add((documentId, semanticModel)); // Note: this code is racy. We could have two threads executing the code above, while only one thread will win // here. We accept that as this code is just intended to help just by making some strong references to semantic From 518658afe9618dd1e0769dff735c8d997ec4cf45 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 13:53:02 -0800 Subject: [PATCH 290/508] Docs --- .../Workspace/Solution/Solution_SemanticModelCaching.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs index 242e98734c8a1..64e43cba5fea0 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs @@ -34,9 +34,11 @@ internal void OnSemanticModelObtained( var activeDocumentId = service.TryGetActiveDocument(); var relatedDocumentIds = activeDocumentId is null ? [] : this.GetRelatedDocumentIds(activeDocumentId); + // Remove any cached values for documents that are no longer the active document. localArray = localArray.RemoveAll( tuple => !relatedDocumentIds.Contains(tuple.documentId)); + // Now cache this doc/semantic model pair if it's in the related document set. if (relatedDocumentIds.Contains(documentId)) localArray = localArray.Add((documentId, semanticModel)); From cc806c42dc01bc64fb4ed8c1a141a3b7df1246b7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 13:53:34 -0800 Subject: [PATCH 291/508] Docs --- .../Portable/Workspace/Solution/Solution_SemanticModelCaching.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs index 64e43cba5fea0..c323a3c582ee6 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs @@ -25,6 +25,7 @@ internal void OnSemanticModelObtained( { var service = this.Services.GetRequiredService(); + // Operate on a local reference to the immutable array to ensure a consistent view of it. var localArray = _activeSemanticModels; // No need to do anything if we're already caching this pair. From d349374114d0191554f963d198fbfa5d7ce03a2f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 14:12:15 -0800 Subject: [PATCH 292/508] Fix --- src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs | 6 ++---- .../Core/Test.Next/Services/ServiceHubServicesTests.cs | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs index f88ba465bca6a..fea47f1ac5bee 100644 --- a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs +++ b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs @@ -155,11 +155,9 @@ private void OnWorkspaceChanged(object? sender, WorkspaceChangeEventArgs e) // to whatever the workspace is doing. lock (_gate) { - if (_isSynchronizeWorkspacePaused) - return; + if (!_isSynchronizeWorkspacePaused) + _synchronizeWorkspaceQueue.AddWork(); } - - _synchronizeWorkspaceQueue.AddWork(); } private void OnActiveDocumentChanged(object? sender, DocumentId? e) diff --git a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs index 512a6ab9edd6c..719309af3aa5c 100644 --- a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs @@ -91,7 +91,7 @@ public async Task TestRemoteHostTextSynchronize() // sync await client.TryInvokeAsync( - (service, cancellationToken) => service.SynchronizeTextAsync(oldDocument.Id, oldState.Text, newText.GetTextChanges(oldText).AsImmutable(), cancellationToken), + (service, cancellationToken) => service.SynchronizeTextChangesAsync([oldDocument.Id, oldState.Text, newText.GetTextChanges(oldText).AsImmutable()], cancellationToken), CancellationToken.None); // apply change to solution From eb999ce03c18eb6f1de807fc9a6d16e638f6c4d7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 14:12:35 -0800 Subject: [PATCH 293/508] Fix --- .../Core/Test.Next/Services/ServiceHubServicesTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs index 719309af3aa5c..fb57ce4999708 100644 --- a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs @@ -91,7 +91,7 @@ public async Task TestRemoteHostTextSynchronize() // sync await client.TryInvokeAsync( - (service, cancellationToken) => service.SynchronizeTextChangesAsync([oldDocument.Id, oldState.Text, newText.GetTextChanges(oldText).AsImmutable()], cancellationToken), + (service, cancellationToken) => service.SynchronizeTextChangesAsync([(oldDocument.Id, oldState.Text, newText.GetTextChanges(oldText).AsImmutable())], cancellationToken), CancellationToken.None); // apply change to solution From c622f132be1bd7c9e01ceef7cf027b999f84f5cb Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 14:19:51 -0800 Subject: [PATCH 294/508] Update src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs Co-authored-by: Todd Grunke --- .../Workspace/Solution/Solution_SemanticModelCaching.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs index c323a3c582ee6..92c49675d850a 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs @@ -11,7 +11,7 @@ public partial class Solution { /// /// Strongly held reference to the semantic models for the active document (and its related documents linked into - /// other projects). By strongly holding onto then, we ensure that they won't be GC'ed between feature requests + /// other projects). By strongly holding onto them, we ensure that they won't be GC'ed between feature requests /// from multiple features that care about it. As the active document has the most features running on it /// continuously, we definitely do not want to drop this. Note: this cached value is only to help with performance. /// Not with correctness. Importantly, the concept of 'active document' is itself fundamentally racy. That's ok From 78246be173acf54734d2ab1e83c13a61aa760e12 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 14:20:51 -0800 Subject: [PATCH 295/508] Add test --- .../Services/SolutionServiceTests.cs | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs index 7acae046c03eb..e74b1d5764919 100644 --- a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs @@ -1048,6 +1048,48 @@ class Program2 objectReference2.AssertReleased(); } + [Fact] + public void TestActiveAndRelatedDocumentSemanticModelCached() + { + using var workspace = TestWorkspace.Create(""" + + + + class Program1 + { + } + + + class Program2 + { + } + + + + + + + """, composition: s_compositionWithFirstDocumentIsActiveAndVisible); + using var remoteWorkspace = CreateRemoteWorkspace(); + + var solution = workspace.CurrentSolution; + + var project1 = solution.Projects.Single(p => p.AssemblyName == "Assembly1"); + var project2 = solution.Projects.Single(p => p.AssemblyName == "Assembly2"); + var document1 = project1.Documents.First(); + var document2 = project1.Documents.Last(); + var document3 = project2.Documents.Single(); + + // Only the semantic model for the active document should be cached. + var objectReference1 = ObjectReference.CreateFromFactory(() => document1.GetSemanticModelAsync().GetAwaiter().GetResult()); + var objectReference2 = ObjectReference.CreateFromFactory(() => document2.GetSemanticModelAsync().GetAwaiter().GetResult()); + var objectReference3 = ObjectReference.CreateFromFactory(() => document3.GetSemanticModelAsync().GetAwaiter().GetResult()); + + objectReference1.AssertHeld(); + objectReference2.AssertReleased(); + objectReference3.AssertHeld(); + } + [Fact] public async Task TestRemoteWorkspaceCachesNothingIfActiveDocumentNotSynced() { From 5c3dfadb3f4000e382711fe5a04aab3ca7470b53 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 14:29:13 -0800 Subject: [PATCH 296/508] Reorder --- .../EmbeddedLanguageDetector.cs | 116 +++++++++--------- 1 file changed, 60 insertions(+), 56 deletions(-) diff --git a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs index 4c7b8fdd95e52..75635b778f5f0 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs @@ -198,62 +198,10 @@ private bool IsEmbeddedLanguageInterpolatedStringTextToken( return HasMatchingStringSyntaxAttribute(method.Parameters[0], out identifier); } - private bool IsEmbeddedLanguageStringLiteralToken( - SyntaxToken token, - SemanticModel semanticModel, - CancellationToken cancellationToken, - [NotNullWhen(true)] out string? identifier, - out IEnumerable? options) - { - identifier = null; - options = null; - var syntaxFacts = Info.SyntaxFacts; - if (!syntaxFacts.IsLiteralExpression(token.Parent)) - return false; - - if (HasLanguageComment(token, syntaxFacts, out identifier, out options)) - return true; - - // If we're a string used in a collection initializer, treat this as a lang string if the collection itself - // is properly annotated. This is for APIs that do things like DateTime.ParseExact(..., string[] formats, ...); - var tokenParent = TryFindContainer(token); - if (tokenParent is null) - return false; - - // Check for direct usage of this token that indicates it's an embedded language string. Like passing it to an - // argument which has the StringSyntax attribute on it. - if (IsEmbeddedLanguageStringLiteralToken_Direct( - token, semanticModel, cancellationToken, out identifier)) - { - return true; - } - - // Now check for if the literal was assigned to a local that we then see is passed along to something that - // indicates an embedded language string. - - var statement = tokenParent.FirstAncestorOrSelf(syntaxFacts.IsStatement); - if (syntaxFacts.IsSimpleAssignmentStatement(statement)) - { - syntaxFacts.GetPartsOfAssignmentStatement(statement, out var left, out var right); - return tokenParent == right && - IsLocalConsumedByApiWithStringSyntaxAttribute( - semanticModel.GetSymbolInfo(left, cancellationToken).GetAnySymbol(), tokenParent, semanticModel, cancellationToken, out identifier); - } - - if (syntaxFacts.IsEqualsValueClause(tokenParent.Parent) && - syntaxFacts.IsVariableDeclarator(tokenParent.Parent.Parent)) - { - var variableDeclarator = tokenParent.Parent.Parent; - var symbol = - semanticModel.GetDeclaredSymbol(variableDeclarator, cancellationToken) ?? - semanticModel.GetDeclaredSymbol(syntaxFacts.GetIdentifierOfVariableDeclarator(variableDeclarator).GetRequiredParent(), cancellationToken); - - return IsLocalConsumedByApiWithStringSyntaxAttribute(symbol, tokenParent, semanticModel, cancellationToken, out identifier); - } - - return false; - } - + /// + /// Checks for a string literal directly used in a location we can tell is controlled by a + /// [StringSyntax] attribute. + /// private bool IsEmbeddedLanguageStringLiteralToken_Direct( SyntaxToken token, SemanticModel semanticModel, @@ -317,6 +265,62 @@ private bool IsEmbeddedLanguageStringLiteralToken_Direct( return false; } + private bool IsEmbeddedLanguageStringLiteralToken( + SyntaxToken token, + SemanticModel semanticModel, + CancellationToken cancellationToken, + [NotNullWhen(true)] out string? identifier, + out IEnumerable? options) + { + identifier = null; + options = null; + var syntaxFacts = Info.SyntaxFacts; + if (!syntaxFacts.IsLiteralExpression(token.Parent)) + return false; + + if (HasLanguageComment(token, syntaxFacts, out identifier, out options)) + return true; + + // If we're a string used in a collection initializer, treat this as a lang string if the collection itself + // is properly annotated. This is for APIs that do things like DateTime.ParseExact(..., string[] formats, ...); + var tokenParent = TryFindContainer(token); + if (tokenParent is null) + return false; + + // Check for direct usage of this token that indicates it's an embedded language string. Like passing it to an + // argument which has the StringSyntax attribute on it. + if (IsEmbeddedLanguageStringLiteralToken_Direct( + token, semanticModel, cancellationToken, out identifier)) + { + return true; + } + + // Now check for if the literal was assigned to a local that we then see is passed along to something that + // indicates an embedded language string at some later point. + + var statement = tokenParent.FirstAncestorOrSelf(syntaxFacts.IsStatement); + if (syntaxFacts.IsSimpleAssignmentStatement(statement)) + { + syntaxFacts.GetPartsOfAssignmentStatement(statement, out var left, out var right); + return tokenParent == right && + IsLocalConsumedByApiWithStringSyntaxAttribute( + semanticModel.GetSymbolInfo(left, cancellationToken).GetAnySymbol(), tokenParent, semanticModel, cancellationToken, out identifier); + } + + if (syntaxFacts.IsEqualsValueClause(tokenParent.Parent) && + syntaxFacts.IsVariableDeclarator(tokenParent.Parent.Parent)) + { + var variableDeclarator = tokenParent.Parent.Parent; + var symbol = + semanticModel.GetDeclaredSymbol(variableDeclarator, cancellationToken) ?? + semanticModel.GetDeclaredSymbol(syntaxFacts.GetIdentifierOfVariableDeclarator(variableDeclarator).GetRequiredParent(), cancellationToken); + + return IsLocalConsumedByApiWithStringSyntaxAttribute(symbol, tokenParent, semanticModel, cancellationToken, out identifier); + } + + return false; + } + private bool IsLocalConsumedByApiWithStringSyntaxAttribute( ISymbol? symbol, SyntaxNode tokenParent, From 7ba8ccf474eee1f777112c33cbc2380d6253a80f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 14:29:52 -0800 Subject: [PATCH 297/508] Docs --- .../Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs index 75635b778f5f0..d287c23a7c011 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs @@ -360,6 +360,9 @@ private bool IsLocalConsumedByApiWithStringSyntaxAttribute( var otherSymbol = semanticModel.GetSymbolInfo(descendent, cancellationToken).GetAnySymbol(); if (localSymbol.Equals(otherSymbol)) { + // Only do a direct check here. We don't want to continually do indirect checks where a string + // literal is assigned to one local, assigned to another local, assigned to another local, and so + // on. if (IsEmbeddedLanguageStringLiteralToken_Direct(identifierToken, semanticModel, cancellationToken, out identifier)) return true; } From d1e2705d8cd7f18b35292e8a0af85fe55427dba5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 14:30:26 -0800 Subject: [PATCH 298/508] Docs --- .../EmbeddedLanguageDetector.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs index d287c23a7c011..b7a4a1da3b20e 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs @@ -211,27 +211,27 @@ private bool IsEmbeddedLanguageStringLiteralToken_Direct( identifier = null; var syntaxFacts = Info.SyntaxFacts; - var tokenParent = TryFindContainer(token); - if (tokenParent is null) + var container = TryFindContainer(token); + if (container is null) return false; - if (syntaxFacts.IsArgument(tokenParent.Parent)) - return IsArgumentWithMatchingStringSyntaxAttribute(semanticModel, tokenParent.Parent, cancellationToken, out identifier); + if (syntaxFacts.IsArgument(container.Parent)) + return IsArgumentWithMatchingStringSyntaxAttribute(semanticModel, container.Parent, cancellationToken, out identifier); - if (syntaxFacts.IsAttributeArgument(tokenParent.Parent)) - return IsAttributeArgumentWithMatchingStringSyntaxAttribute(semanticModel, tokenParent.Parent, cancellationToken, out identifier); + if (syntaxFacts.IsAttributeArgument(container.Parent)) + return IsAttributeArgumentWithMatchingStringSyntaxAttribute(semanticModel, container.Parent, cancellationToken, out identifier); - if (syntaxFacts.IsNamedMemberInitializer(tokenParent.Parent)) + if (syntaxFacts.IsNamedMemberInitializer(container.Parent)) { - syntaxFacts.GetPartsOfNamedMemberInitializer(tokenParent.Parent, out var name, out _); + syntaxFacts.GetPartsOfNamedMemberInitializer(container.Parent, out var name, out _); return IsFieldOrPropertyWithMatchingStringSyntaxAttribute(semanticModel, name, cancellationToken, out identifier); } - var statement = tokenParent.FirstAncestorOrSelf(syntaxFacts.IsStatement); + var statement = container.FirstAncestorOrSelf(syntaxFacts.IsStatement); if (syntaxFacts.IsSimpleAssignmentStatement(statement)) { syntaxFacts.GetPartsOfAssignmentStatement(statement, out var left, out var right); - if (tokenParent == right) + if (container == right) { if (IsFieldOrPropertyWithMatchingStringSyntaxAttribute(semanticModel, left, cancellationToken, out identifier)) return true; @@ -240,11 +240,11 @@ private bool IsEmbeddedLanguageStringLiteralToken_Direct( return false; } - if (syntaxFacts.IsEqualsValueClause(tokenParent.Parent)) + if (syntaxFacts.IsEqualsValueClause(container.Parent)) { - if (syntaxFacts.IsVariableDeclarator(tokenParent.Parent.Parent)) + if (syntaxFacts.IsVariableDeclarator(container.Parent.Parent)) { - var variableDeclarator = tokenParent.Parent.Parent; + var variableDeclarator = container.Parent.Parent; var symbol = semanticModel.GetDeclaredSymbol(variableDeclarator, cancellationToken) ?? semanticModel.GetDeclaredSymbol(syntaxFacts.GetIdentifierOfVariableDeclarator(variableDeclarator).GetRequiredParent(), cancellationToken); @@ -252,9 +252,9 @@ private bool IsEmbeddedLanguageStringLiteralToken_Direct( if (IsFieldOrPropertyWithMatchingStringSyntaxAttribute(symbol, out identifier)) return true; } - else if (syntaxFacts.IsEqualsValueOfPropertyDeclaration(tokenParent.Parent)) + else if (syntaxFacts.IsEqualsValueOfPropertyDeclaration(container.Parent)) { - var property = tokenParent.Parent.GetRequiredParent(); + var property = container.Parent.GetRequiredParent(); var symbol = semanticModel.GetDeclaredSymbol(property, cancellationToken); if (IsFieldOrPropertyWithMatchingStringSyntaxAttribute(symbol, out identifier)) From a6ef2c95a1ffb236eefbff37c7374361d3ba22be Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 14:31:53 -0800 Subject: [PATCH 299/508] REvert --- .../EmbeddedLanguageDetector.cs | 73 ++++++++++--------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs index b7a4a1da3b20e..c97e4a802260c 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs @@ -216,49 +216,56 @@ private bool IsEmbeddedLanguageStringLiteralToken_Direct( return false; if (syntaxFacts.IsArgument(container.Parent)) - return IsArgumentWithMatchingStringSyntaxAttribute(semanticModel, container.Parent, cancellationToken, out identifier); - - if (syntaxFacts.IsAttributeArgument(container.Parent)) - return IsAttributeArgumentWithMatchingStringSyntaxAttribute(semanticModel, container.Parent, cancellationToken, out identifier); - - if (syntaxFacts.IsNamedMemberInitializer(container.Parent)) { - syntaxFacts.GetPartsOfNamedMemberInitializer(container.Parent, out var name, out _); - return IsFieldOrPropertyWithMatchingStringSyntaxAttribute(semanticModel, name, cancellationToken, out identifier); + if (IsArgumentWithMatchingStringSyntaxAttribute(semanticModel, container.Parent, cancellationToken, out identifier)) + return true; } - - var statement = container.FirstAncestorOrSelf(syntaxFacts.IsStatement); - if (syntaxFacts.IsSimpleAssignmentStatement(statement)) + else if (syntaxFacts.IsAttributeArgument(container.Parent)) { - syntaxFacts.GetPartsOfAssignmentStatement(statement, out var left, out var right); - if (container == right) - { - if (IsFieldOrPropertyWithMatchingStringSyntaxAttribute(semanticModel, left, cancellationToken, out identifier)) - return true; - } - - return false; + if (IsAttributeArgumentWithMatchingStringSyntaxAttribute(semanticModel, container.Parent, cancellationToken, out identifier)) + return true; } - - if (syntaxFacts.IsEqualsValueClause(container.Parent)) + else if (syntaxFacts.IsNamedMemberInitializer(container.Parent)) { - if (syntaxFacts.IsVariableDeclarator(container.Parent.Parent)) + syntaxFacts.GetPartsOfNamedMemberInitializer(container.Parent, out var name, out _); + if (IsFieldOrPropertyWithMatchingStringSyntaxAttribute(semanticModel, name, cancellationToken, out identifier)) + return true; + } + else + { + var statement = container.FirstAncestorOrSelf(syntaxFacts.IsStatement); + if (syntaxFacts.IsSimpleAssignmentStatement(statement)) { - var variableDeclarator = container.Parent.Parent; - var symbol = - semanticModel.GetDeclaredSymbol(variableDeclarator, cancellationToken) ?? - semanticModel.GetDeclaredSymbol(syntaxFacts.GetIdentifierOfVariableDeclarator(variableDeclarator).GetRequiredParent(), cancellationToken); + syntaxFacts.GetPartsOfAssignmentStatement(statement, out var left, out var right); + if (container == right) + { + if (IsFieldOrPropertyWithMatchingStringSyntaxAttribute(semanticModel, left, cancellationToken, out identifier)) + return true; + } - if (IsFieldOrPropertyWithMatchingStringSyntaxAttribute(symbol, out identifier)) - return true; + return false; } - else if (syntaxFacts.IsEqualsValueOfPropertyDeclaration(container.Parent)) + + if (syntaxFacts.IsEqualsValueClause(container.Parent)) { - var property = container.Parent.GetRequiredParent(); - var symbol = semanticModel.GetDeclaredSymbol(property, cancellationToken); + if (syntaxFacts.IsVariableDeclarator(container.Parent.Parent)) + { + var variableDeclarator = container.Parent.Parent; + var symbol = + semanticModel.GetDeclaredSymbol(variableDeclarator, cancellationToken) ?? + semanticModel.GetDeclaredSymbol(syntaxFacts.GetIdentifierOfVariableDeclarator(variableDeclarator).GetRequiredParent(), cancellationToken); - if (IsFieldOrPropertyWithMatchingStringSyntaxAttribute(symbol, out identifier)) - return true; + if (IsFieldOrPropertyWithMatchingStringSyntaxAttribute(symbol, out identifier)) + return true; + } + else if (syntaxFacts.IsEqualsValueOfPropertyDeclaration(container.Parent)) + { + var property = container.Parent.GetRequiredParent(); + var symbol = semanticModel.GetDeclaredSymbol(property, cancellationToken); + + if (IsFieldOrPropertyWithMatchingStringSyntaxAttribute(symbol, out identifier)) + return true; + } } } From 5588c0d5f18a105bfce22c1b38e21acba7dcd9c0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 14:32:45 -0800 Subject: [PATCH 300/508] REvert --- .../Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs index c97e4a802260c..55e81858cbe83 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; @@ -211,6 +212,10 @@ private bool IsEmbeddedLanguageStringLiteralToken_Direct( identifier = null; var syntaxFacts = Info.SyntaxFacts; + // Checked by caller + Debug.Assert(syntaxFacts.IsLiteralExpression(token.Parent)); + Debug.Assert(!HasLanguageComment(token, syntaxFacts, out identifier, out _)); + var container = TryFindContainer(token); if (container is null) return false; From 97c93933034f357ecf566dad849374852a791ab8 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 14:33:34 -0800 Subject: [PATCH 301/508] REvert --- .../EmbeddedLanguages/EmbeddedLanguageDetector.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs index 55e81858cbe83..343e888e1cfc7 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs @@ -242,13 +242,11 @@ private bool IsEmbeddedLanguageStringLiteralToken_Direct( if (syntaxFacts.IsSimpleAssignmentStatement(statement)) { syntaxFacts.GetPartsOfAssignmentStatement(statement, out var left, out var right); - if (container == right) + if (container == right && + IsFieldOrPropertyWithMatchingStringSyntaxAttribute(semanticModel, left, cancellationToken, out identifier)) { - if (IsFieldOrPropertyWithMatchingStringSyntaxAttribute(semanticModel, left, cancellationToken, out identifier)) - return true; + return true; } - - return false; } if (syntaxFacts.IsEqualsValueClause(container.Parent)) From b539019249f04676cf38497aca41437c84fc36a7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 14:35:11 -0800 Subject: [PATCH 302/508] REvert --- .../EmbeddedLanguageDetector.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs index 343e888e1cfc7..ea27b08c1ef66 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs @@ -216,6 +216,8 @@ private bool IsEmbeddedLanguageStringLiteralToken_Direct( Debug.Assert(syntaxFacts.IsLiteralExpression(token.Parent)); Debug.Assert(!HasLanguageComment(token, syntaxFacts, out identifier, out _)); + // If we're a string used in a collection initializer, treat this as a lang string if the collection itself is + // properly annotated. This is for APIs that do things like DateTime.ParseExact(..., string[] formats, ...); var container = TryFindContainer(token); if (container is null) return false; @@ -291,14 +293,8 @@ private bool IsEmbeddedLanguageStringLiteralToken( if (HasLanguageComment(token, syntaxFacts, out identifier, out options)) return true; - // If we're a string used in a collection initializer, treat this as a lang string if the collection itself - // is properly annotated. This is for APIs that do things like DateTime.ParseExact(..., string[] formats, ...); - var tokenParent = TryFindContainer(token); - if (tokenParent is null) - return false; - - // Check for direct usage of this token that indicates it's an embedded language string. Like passing it to an - // argument which has the StringSyntax attribute on it. + // Check for *direct* usage of this token that indicates it's an embedded language string. Like passing it to + // an argument which has the StringSyntax attribute on it. if (IsEmbeddedLanguageStringLiteralToken_Direct( token, semanticModel, cancellationToken, out identifier)) { @@ -308,24 +304,28 @@ private bool IsEmbeddedLanguageStringLiteralToken( // Now check for if the literal was assigned to a local that we then see is passed along to something that // indicates an embedded language string at some later point. - var statement = tokenParent.FirstAncestorOrSelf(syntaxFacts.IsStatement); + var container = TryFindContainer(token); + if (container is null) + return false; + + var statement = container.FirstAncestorOrSelf(syntaxFacts.IsStatement); if (syntaxFacts.IsSimpleAssignmentStatement(statement)) { syntaxFacts.GetPartsOfAssignmentStatement(statement, out var left, out var right); - return tokenParent == right && + return container == right && IsLocalConsumedByApiWithStringSyntaxAttribute( - semanticModel.GetSymbolInfo(left, cancellationToken).GetAnySymbol(), tokenParent, semanticModel, cancellationToken, out identifier); + semanticModel.GetSymbolInfo(left, cancellationToken).GetAnySymbol(), container, semanticModel, cancellationToken, out identifier); } - if (syntaxFacts.IsEqualsValueClause(tokenParent.Parent) && - syntaxFacts.IsVariableDeclarator(tokenParent.Parent.Parent)) + if (syntaxFacts.IsEqualsValueClause(container.Parent) && + syntaxFacts.IsVariableDeclarator(container.Parent.Parent)) { - var variableDeclarator = tokenParent.Parent.Parent; + var variableDeclarator = container.Parent.Parent; var symbol = semanticModel.GetDeclaredSymbol(variableDeclarator, cancellationToken) ?? semanticModel.GetDeclaredSymbol(syntaxFacts.GetIdentifierOfVariableDeclarator(variableDeclarator).GetRequiredParent(), cancellationToken); - return IsLocalConsumedByApiWithStringSyntaxAttribute(symbol, tokenParent, semanticModel, cancellationToken, out identifier); + return IsLocalConsumedByApiWithStringSyntaxAttribute(symbol, container, semanticModel, cancellationToken, out identifier); } return false; From 03650a792efba4e5469e07e6390b1823d2609c95 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 14:35:59 -0800 Subject: [PATCH 303/508] REvert --- .../Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs index ea27b08c1ef66..9058bdb6a34eb 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs @@ -216,8 +216,8 @@ private bool IsEmbeddedLanguageStringLiteralToken_Direct( Debug.Assert(syntaxFacts.IsLiteralExpression(token.Parent)); Debug.Assert(!HasLanguageComment(token, syntaxFacts, out identifier, out _)); - // If we're a string used in a collection initializer, treat this as a lang string if the collection itself is - // properly annotated. This is for APIs that do things like DateTime.ParseExact(..., string[] formats, ...); + // If we're a string used in a collection initializer, treat this as a lang string if the collection itself + // is properly annotated. This is for APIs that do things like DateTime.ParseExact(..., string[] formats, ...); var container = TryFindContainer(token); if (container is null) return false; From 35b1bbcc4c0edfd63faad579026c5c58ff7fe062 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 14:36:24 -0800 Subject: [PATCH 304/508] Update src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs --- .../Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs index 9058bdb6a34eb..278d7741e45b4 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs @@ -245,7 +245,8 @@ private bool IsEmbeddedLanguageStringLiteralToken_Direct( { syntaxFacts.GetPartsOfAssignmentStatement(statement, out var left, out var right); if (container == right && - IsFieldOrPropertyWithMatchingStringSyntaxAttribute(semanticModel, left, cancellationToken, out identifier)) + IsFieldOrPropertyWithMatchingStringSyntaxAttribute( + semanticModel, left, cancellationToken, out identifier)) { return true; } From 4401048be7abead08a16b500e9781079bb8ed9a6 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 14:37:39 -0800 Subject: [PATCH 305/508] merge --- .../EmbeddedLanguages/EmbeddedLanguageDetector.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs index 9058bdb6a34eb..580c3d4760be7 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs @@ -356,10 +356,11 @@ private bool IsLocalConsumedByApiWithStringSyntaxAttribute( // Now look at the next statements that follow for usages of this local variable. foreach (var statement in blockFacts.GetExecutableBlockStatements(block)) { - cancellationToken.ThrowIfCancellationRequested(); foreach (var descendent in statement.DescendantNodesAndSelf()) { + cancellationToken.ThrowIfCancellationRequested(); + if (!syntaxFacts.IsIdentifierName(descendent)) continue; @@ -368,13 +369,13 @@ private bool IsLocalConsumedByApiWithStringSyntaxAttribute( continue; var otherSymbol = semanticModel.GetSymbolInfo(descendent, cancellationToken).GetAnySymbol(); - if (localSymbol.Equals(otherSymbol)) + + // Only do a direct check here. We don't want to continually do indirect checks where a string literal + // is assigned to one local, assigned to another local, assigned to another local, and so on. + if (localSymbol.Equals(otherSymbol) && + IsEmbeddedLanguageStringLiteralToken_Direct(identifierToken, semanticModel, cancellationToken, out identifier)) { - // Only do a direct check here. We don't want to continually do indirect checks where a string - // literal is assigned to one local, assigned to another local, assigned to another local, and so - // on. - if (IsEmbeddedLanguageStringLiteralToken_Direct(identifierToken, semanticModel, cancellationToken, out identifier)) - return true; + return true; } } } From 205f73ee8a6b74cdb098520bdfe475026b5dcc57 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 14:38:31 -0800 Subject: [PATCH 306/508] cleanup --- .../Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs index 337ad0071eb7c..c2988d64b365a 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs @@ -357,7 +357,6 @@ private bool IsLocalConsumedByApiWithStringSyntaxAttribute( // Now look at the next statements that follow for usages of this local variable. foreach (var statement in blockFacts.GetExecutableBlockStatements(block)) { - foreach (var descendent in statement.DescendantNodesAndSelf()) { cancellationToken.ThrowIfCancellationRequested(); From 37a70d2ae92071f88aebed29d6bc607e8f873221 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Tue, 12 Nov 2024 15:16:23 -0800 Subject: [PATCH 307/508] =?UTF-8?q?Struct=20layout=20check=20=E2=80=93=20b?= =?UTF-8?q?reak=20an=20infinite=20generic=20expansion=20cycle=20that=20doe?= =?UTF-8?q?sn=E2=80=99t=20include=20the=20target=20struct=20(a=20cycle=20a?= =?UTF-8?q?t=20the=20end=20of=20the=20path)=20(#75702)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #66844 --- .../Portable/Symbols/BaseTypeAnalysis.cs | 34 +- .../Test/Emit3/FlowAnalysis/StructTests.cs | 310 ++++++++++++++++++ .../Source/SourceMemberContainerTypeSymbol.vb | 64 ++-- .../Symbol/SymbolsTests/Source/TypeTests.vb | 233 +++++++++++++ 4 files changed, 612 insertions(+), 29 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs b/src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs index 7d5e29491e665..ca24025fe4390 100644 --- a/src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs +++ b/src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs @@ -66,30 +66,50 @@ internal static bool StructDependsOn(NamedTypeSymbol depends, NamedTypeSymbol on Debug.Assert((object)on != null); Debug.Assert(on.IsDefinition); - var hs = PooledHashSet.GetInstance(); - StructDependsClosure(depends, hs, on); + var hs = PooledHashSet.GetInstance(); + var typesWithCycle = PooledHashSet.GetInstance(); + StructDependsClosure(depends, hs, typesWithCycle, ConsList.Empty.Prepend(on)); - var result = hs.Contains(on); + var result = typesWithCycle.Contains(on); + typesWithCycle.Free(); hs.Free(); return result; } - private static void StructDependsClosure(NamedTypeSymbol type, HashSet partialClosure, NamedTypeSymbol on) + private static void StructDependsClosure(NamedTypeSymbol type, HashSet partialClosure, HashSet typesWithCycle, ConsList on) { Debug.Assert((object)type != null); - if ((object)type.OriginalDefinition == on) + if (typesWithCycle.Contains(type.OriginalDefinition)) + { + return; + } + + if (on.ContainsReference(type.OriginalDefinition)) { // found a possibly expanding cycle, for example // struct X { public T t; } // struct W { X>> x; } // while not explicitly forbidden by the spec, it should be. - partialClosure.Add(on); + typesWithCycle.Add(type.OriginalDefinition); return; } if (partialClosure.Add(type)) + { + if (!type.IsDefinition) + { + // First, visit type as a definition in order to detect the fact that it itself has a cycle. + // This prevents us from going into an infinite generic expansion while visiting constructed form + // of the type below. + visitFields(type.OriginalDefinition, partialClosure, typesWithCycle, on.Prepend(type.OriginalDefinition)); + } + + visitFields(type, partialClosure, typesWithCycle, on); + } + + static void visitFields(NamedTypeSymbol type, HashSet partialClosure, HashSet typesWithCycle, ConsList on) { foreach (var member in type.GetMembersUnordered()) { @@ -100,7 +120,7 @@ private static void StructDependsClosure(NamedTypeSymbol type, HashSet p continue; } - StructDependsClosure((NamedTypeSymbol)fieldType, partialClosure, on); + StructDependsClosure((NamedTypeSymbol)fieldType, partialClosure, typesWithCycle, on); } } } diff --git a/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/StructTests.cs b/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/StructTests.cs index 32248a37617e9..44c5f04148875 100644 --- a/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/StructTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/StructTests.cs @@ -219,6 +219,316 @@ public void GenericStructWithPropertyUsingStruct() Diagnostic(ErrorCode.ERR_StructLayoutCycle, "P").WithArguments("S.P", "S?").WithLocation(3, 13)); } + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/66844")] + public void InstanceMemberExplosion_01() + { + string program = @"#pragma warning disable CS0169 // The field is never used +struct A +{ + A> x; +} + +struct B +{ + A> x; +} + +struct C +{ + D x; +} +struct D +{ + C> x; +} +"; + CreateCompilation(program).VerifyDiagnostics( + // (4,13): error CS0523: Struct member 'A.x' of type 'A>' causes a cycle in the struct layout + // A> x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("A.x", "A>").WithLocation(4, 13), + // (14,10): error CS0523: Struct member 'C.x' of type 'D' causes a cycle in the struct layout + // D x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("C.x", "D").WithLocation(14, 10), + // (18,13): error CS0523: Struct member 'D.x' of type 'C>' causes a cycle in the struct layout + // C> x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("D.x", "C>").WithLocation(18, 13) + ); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/66844")] + public void InstanceMemberExplosion_02() + { + string program = @"#pragma warning disable CS0169 // The field is never used +struct A +{ + A> x; +} + +struct B +{ + A>> x; +} + +struct C +{ +} +"; + CreateCompilation(program).VerifyDiagnostics( + // (4,13): error CS0523: Struct member 'A.x' of type 'A>' causes a cycle in the struct layout + // A> x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("A.x", "A>").WithLocation(4, 13) + ); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/66844")] + public void InstanceMemberExplosion_03() + { + string program = @"#pragma warning disable CS0169 // The field is never used +struct E +{} + +struct X +{ + T _t; +} + +struct Y +{ + X xz; +} + +struct Z +{ + X xe; + X xy; +} +"; + CreateCompilation(program).VerifyDiagnostics( + // (12,10): error CS0523: Struct member 'Y.xz' of type 'X' causes a cycle in the struct layout + // X xz; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "xz").WithArguments("Y.xz", "X").WithLocation(12, 10), + // (18,10): error CS0523: Struct member 'Z.xy' of type 'X' causes a cycle in the struct layout + // X xy; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "xy").WithArguments("Z.xy", "X").WithLocation(18, 10) + ); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/66844")] + public void InstanceMemberExplosion_04() + { + string program = @"#pragma warning disable CS0169 // The field is never used +struct A +{ + A> x; +} + +struct C +{ + C> x; +} + +struct B +{ + A> x; + C> y; + B z; +} + +struct D +{ + B x; +} +"; + CreateCompilation(program).VerifyDiagnostics( + // (4,13): error CS0523: Struct member 'A.x' of type 'A>' causes a cycle in the struct layout + // A> x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("A.x", "A>").WithLocation(4, 13), + // (9,13): error CS0523: Struct member 'C.x' of type 'C>' causes a cycle in the struct layout + // C> x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("C.x", "C>").WithLocation(9, 13), + // (16,10): error CS0523: Struct member 'B.z' of type 'B' causes a cycle in the struct layout + // B z; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "z").WithArguments("B.z", "B").WithLocation(16, 10) + ); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/66844")] + public void InstanceMemberExplosion_05() + { + string program = @"#pragma warning disable CS0169 // The field is never used +struct A +{ + A> x; +} + +struct C +{ + C> x; +} + +struct B +{ + B z; + A> x; + C> y; +} + +struct D +{ + B x; +} +"; + CreateCompilation(program).VerifyDiagnostics( + // (4,13): error CS0523: Struct member 'A.x' of type 'A>' causes a cycle in the struct layout + // A> x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("A.x", "A>").WithLocation(4, 13), + // (9,13): error CS0523: Struct member 'C.x' of type 'C>' causes a cycle in the struct layout + // C> x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("C.x", "C>").WithLocation(9, 13), + // (14,10): error CS0523: Struct member 'B.z' of type 'B' causes a cycle in the struct layout + // B z; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "z").WithArguments("B.z", "B").WithLocation(14, 10) + ); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/66844")] + public void InstanceMemberExplosion_06() + { + string program = @"#pragma warning disable CS0169 // The field is never used +struct A +{ + A> x; +} + +struct C +{ + C> x; +} + +struct B +{ + A> x; + B z; + C> y; +} + +struct D +{ + B x; +} +"; + CreateCompilation(program).VerifyDiagnostics( + // (4,13): error CS0523: Struct member 'A.x' of type 'A>' causes a cycle in the struct layout + // A> x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("A.x", "A>").WithLocation(4, 13), + // (9,13): error CS0523: Struct member 'C.x' of type 'C>' causes a cycle in the struct layout + // C> x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("C.x", "C>").WithLocation(9, 13), + // (15,10): error CS0523: Struct member 'B.z' of type 'B' causes a cycle in the struct layout + // B z; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "z").WithArguments("B.z", "B").WithLocation(15, 10) + ); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/66844")] + public void StaticMemberExplosion_01() + { + string program = @"#pragma warning disable CS0169 // The field is never used +struct A +{ + static A> x; +} + +struct B +{ + static A> x; +} + +struct C +{ + static D x; +} +struct D +{ + static C> x; +} +"; + CreateCompilation(program).VerifyDiagnostics( + // (4,20): error CS0523: Struct member 'A.x' of type 'A>' causes a cycle in the struct layout + // static A> x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("A.x", "A>").WithLocation(4, 20), + // (14,17): error CS0523: Struct member 'C.x' of type 'D' causes a cycle in the struct layout + // static D x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("C.x", "D").WithLocation(14, 17), + // (18,20): error CS0523: Struct member 'D.x' of type 'C>' causes a cycle in the struct layout + // static C> x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("D.x", "C>").WithLocation(18, 20) + ); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/66844")] + public void StaticMemberExplosion_02() + { + string program = @"#pragma warning disable CS0169 // The field is never used +struct A +{ + static A> x; +} + +struct B +{ + static A>> x; +} + +struct C +{ +} +"; + CreateCompilation(program).VerifyDiagnostics( + // (4,20): error CS0523: Struct member 'A.x' of type 'A>' causes a cycle in the struct layout + // static A> x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("A.x", "A>").WithLocation(4, 20) + ); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/66844")] + [WorkItem("https://github.com/dotnet/roslyn/issues/75701")] + public void StaticMemberExplosion_03() + { + string program = @"#pragma warning disable CS0169 // The field is never used +struct E +{} + +struct X +{ + static T _t; +} + +struct Y +{ + static X xz; +} + +struct Z +{ + static X xe; + static X xy; +} +"; + CreateCompilation(program).VerifyDiagnostics( + // Errors are expected here, see https://github.com/dotnet/roslyn/issues/75701. + ); + } + [Fact, WorkItem(1017887, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1017887")] public void EmptyStructsFromMetadata() { diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMemberContainerTypeSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMemberContainerTypeSymbol.vb index 867e7eabf15a8..5119595df0745 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMemberContainerTypeSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMemberContainerTypeSymbol.vb @@ -2022,16 +2022,23 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols ''' Set of processed structure types Public ReadOnly ProcessedTypes As HashSet(Of NamedTypeSymbol) + Public ReadOnly TypesWithCycle As HashSet(Of NamedTypeSymbol) + ''' Queue element structure Public Structure QueueElement Public ReadOnly Type As NamedTypeSymbol - Public ReadOnly Path As ConsList(Of FieldSymbol) + Public ReadOnly FieldPath As ConsList(Of FieldSymbol) + Public ReadOnly ContainingDefinitionsPath As ConsList(Of NamedTypeSymbol) + Public ReadOnly Report As Boolean - Public Sub New(type As NamedTypeSymbol, path As ConsList(Of FieldSymbol)) + Public Sub New(type As NamedTypeSymbol, fieldPath As ConsList(Of FieldSymbol), containingDefinitionsPath As ConsList(Of NamedTypeSymbol), report As Boolean) Debug.Assert(type IsNot Nothing) - Debug.Assert(path IsNot Nothing) + Debug.Assert(fieldPath IsNot Nothing) + Debug.Assert(containingDefinitionsPath IsNot Nothing) Me.Type = type - Me.Path = path + Me.FieldPath = fieldPath + Me.ContainingDefinitionsPath = containingDefinitionsPath + Me.Report = report End Sub End Structure @@ -2040,6 +2047,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Private Sub New() ProcessedTypes = New HashSet(Of NamedTypeSymbol)() + TypesWithCycle = New HashSet(Of NamedTypeSymbol)() Queue = New Queue(Of QueueElement) End Sub @@ -2050,6 +2058,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public Sub Free() Me.Queue.Clear() Me.ProcessedTypes.Clear() + Me.TypesWithCycle.Clear() s_pool.Free(Me) End Sub @@ -2091,7 +2100,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols ' Allocate data set Dim data = StructureCircularityDetectionDataSet.GetInstance() - data.Queue.Enqueue(New StructureCircularityDetectionDataSet.QueueElement(Me, ConsList(Of FieldSymbol).Empty)) + data.Queue.Enqueue(New StructureCircularityDetectionDataSet.QueueElement(Me, ConsList(Of FieldSymbol).Empty, ConsList(Of NamedTypeSymbol).Empty.Prepend(Me), report:=True)) Dim hasCycle = False @@ -2104,6 +2113,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Continue While End If + If data.TypesWithCycle.Contains(current.Type.OriginalDefinition) Then + Continue While + End If + Dim cycleReportedForCurrentType As Boolean = False ' iterate over non-static fields of structure data type @@ -2121,13 +2134,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Continue For End If - If fieldType.OriginalDefinition.Equals(Me) Then + If current.ContainingDefinitionsPath.ContainsReference(fieldType.OriginalDefinition) Then ' a cycle detected - If Not cycleReportedForCurrentType Then + data.TypesWithCycle.Add(fieldType.OriginalDefinition) + + If current.Report AndAlso Not cycleReportedForCurrentType AndAlso fieldType.OriginalDefinition.Equals(Me) Then ' the cycle includes 'current.Path' and ends with 'field'; the order is reversed in the list - Dim cycleFields = New ConsList(Of FieldSymbol)(field, current.Path) + Dim cycleFields = New ConsList(Of FieldSymbol)(field, current.FieldPath) ' generate a message info Dim diagnosticInfos = ArrayBuilder(Of DiagnosticInfo).GetInstance() @@ -2158,19 +2173,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols hasCycle = True End If - ElseIf Not data.ProcessedTypes.Contains(fieldType) Then + ElseIf Not data.ProcessedTypes.Contains(fieldType) AndAlso Not data.TypesWithCycle.Contains(fieldType.OriginalDefinition) Then ' Add to the queue if we don't know yet if it was processed - If Not fieldType.IsDefinition Then - ' Types constructed from generic types are considered to be a separate types. We never report - ' errors on such types. We also process only fields actually changed compared to original generic type. - data.Queue.Enqueue(New StructureCircularityDetectionDataSet.QueueElement( - fieldType, New ConsList(Of FieldSymbol)(field, current.Path))) - - ' The original Generic type is added using regular rules (see next note). - fieldType = fieldType.OriginalDefinition - End If - ' NOTE: we want to make sure we report the same error for the same types ' consistently and don't depend on the call order; this solution uses ' the following approach: @@ -2180,15 +2185,30 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols ' (b) thus, this analysis only considers the cycles consisting of the ' types which are 'bigger' than 'structBeingAnalyzed' because we will not ' report the error regarding this cycle for this type anyway - Dim stepIntoType As Boolean = DetectTypeCircularity_ShouldStepIntoType(fieldType) - If stepIntoType Then + Dim stepIntoType As Boolean = DetectTypeCircularity_ShouldStepIntoType(fieldType.OriginalDefinition) + + If stepIntoType OrElse Not fieldType.IsDefinition Then ' enqueue to be processed + ' First, visit type as a definition in order to detect the fact that it itself has a cycle. + ' This prevents us from going into an infinite generic expansion while visiting constructed form + ' of the type below. data.Queue.Enqueue(New StructureCircularityDetectionDataSet.QueueElement( - fieldType, New ConsList(Of FieldSymbol)(field, current.Path))) + fieldType.OriginalDefinition, New ConsList(Of FieldSymbol)(field, current.FieldPath), + current.ContainingDefinitionsPath.Prepend(fieldType.OriginalDefinition), + report:=stepIntoType)) Else ' should not process data.ProcessedTypes.Add(fieldType) End If + + If Not fieldType.IsDefinition Then + ' Types constructed from generic types are considered to be a separate types. We never report + ' errors on such types. We also process only fields actually changed compared to original generic type. + data.Queue.Enqueue(New StructureCircularityDetectionDataSet.QueueElement( + fieldType, New ConsList(Of FieldSymbol)(field, current.FieldPath), + current.ContainingDefinitionsPath, + report:=True)) + End If End If End If End If diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Source/TypeTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Source/TypeTests.vb index 047f6779857aa..02c20c77f0dfc 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Source/TypeTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Source/TypeTests.vb @@ -1538,6 +1538,239 @@ BC30294: Structure 'SI_1' cannot contain an instance of itself: ) End Sub + + + Public Sub InstanceMemberExplosion_01() + Dim compilation = CompilationUtils.CreateCompilation(" +Structure A(Of T) + Dim x As A(Of A(Of T)) +End Structure + +Structure B(Of T) + Dim x As A(Of B(Of T)) +End Structure + +Structure C(Of T) + Dim x As D(Of T) +End Structure + +Structure D(Of T) + Dim x As C(Of D(Of T)) +End Structure +") + + CompilationUtils.AssertTheseDiagnostics(compilation, + +BC30294: Structure 'A' cannot contain an instance of itself: + 'A(Of T)' contains 'A(Of A(Of T))' (variable 'x'). + Dim x As A(Of A(Of T)) + ~ +BC30294: Structure 'C' cannot contain an instance of itself: + 'C(Of T)' contains 'D(Of T)' (variable 'x'). + 'D(Of T)' contains 'C(Of D(Of T))' (variable 'x'). + Dim x As D(Of T) + ~ +BC30294: Structure 'C' cannot contain an instance of itself: + 'C(Of T)' contains 'D(Of T)' (variable 'x'). + 'D(Of T)' contains 'C(Of D(Of T))' (variable 'x'). + Dim x As D(Of T) + ~ +BC30294: Structure 'D' cannot contain an instance of itself: + 'D(Of T)' contains 'C(Of D(Of T))' (variable 'x'). + 'C(Of D(Of T))' contains 'D(Of D(Of T))' (variable 'x'). + Dim x As C(Of D(Of T)) + ~ +) + End Sub + + + + Public Sub InstanceMemberExplosion_02() + Dim compilation = CompilationUtils.CreateCompilation(" +Structure A(Of T) + Dim x As A(Of A(Of T)) +End Structure + +Structure B(Of T) + Dim x As A(Of C(Of B(Of T))) +End Structure + +Structure C(Of T) +End Structure +") + + CompilationUtils.AssertTheseDiagnostics(compilation, + +BC30294: Structure 'A' cannot contain an instance of itself: + 'A(Of T)' contains 'A(Of A(Of T))' (variable 'x'). + Dim x As A(Of A(Of T)) + ~ +) + End Sub + + + + Public Sub InstanceMemberExplosion_04() + Dim compilation = CompilationUtils.CreateCompilation(" +Structure A(Of T) + Dim x As A(Of A(Of T)) +End Structure + +Structure C(Of T) + Dim x As C(Of C(Of T)) +End Structure + +Structure B(Of T) + Dim x As A(Of B(Of T)) + Dim y As C(Of C(Of T)) + Dim z As B(Of T) +End Structure + +Structure D + Dim x As B(Of Integer) +End Structure +") + + CompilationUtils.AssertTheseDiagnostics(compilation, + +BC30294: Structure 'A' cannot contain an instance of itself: + 'A(Of T)' contains 'A(Of A(Of T))' (variable 'x'). + Dim x As A(Of A(Of T)) + ~ +BC30294: Structure 'C' cannot contain an instance of itself: + 'C(Of T)' contains 'C(Of C(Of T))' (variable 'x'). + Dim x As C(Of C(Of T)) + ~ +BC30294: Structure 'B' cannot contain an instance of itself: + 'B(Of T)' contains 'B(Of T)' (variable 'z'). + Dim z As B(Of T) + ~ +) + End Sub + + + + Public Sub InstanceMemberExplosion_05() + Dim compilation = CompilationUtils.CreateCompilation(" +Structure A(Of T) + Dim x As A(Of A(Of T)) +End Structure + +Structure C(Of T) + Dim x As C(Of C(Of T)) +End Structure + +Structure B(Of T) + Dim z As B(Of T) + Dim x As A(Of B(Of T)) + Dim y As C(Of C(Of T)) +End Structure + +Structure D + Dim x As B(Of Integer) +End Structure +") + + CompilationUtils.AssertTheseDiagnostics(compilation, + +BC30294: Structure 'A' cannot contain an instance of itself: + 'A(Of T)' contains 'A(Of A(Of T))' (variable 'x'). + Dim x As A(Of A(Of T)) + ~ +BC30294: Structure 'C' cannot contain an instance of itself: + 'C(Of T)' contains 'C(Of C(Of T))' (variable 'x'). + Dim x As C(Of C(Of T)) + ~ +BC30294: Structure 'B' cannot contain an instance of itself: + 'B(Of T)' contains 'B(Of T)' (variable 'z'). + Dim z As B(Of T) + ~ +) + End Sub + + + + Public Sub InstanceMemberExplosion_06() + Dim compilation = CompilationUtils.CreateCompilation(" +Structure A(Of T) + Dim x As A(Of A(Of T)) +End Structure + +Structure C(Of T) + Dim x As C(Of C(Of T)) +End Structure + +Structure B(Of T) + Dim x As A(Of B(Of T)) + Dim z As B(Of T) + Dim y As C(Of C(Of T)) +End Structure + +Structure D + Dim x As B(Of Integer) +End Structure +") + + CompilationUtils.AssertTheseDiagnostics(compilation, + +BC30294: Structure 'A' cannot contain an instance of itself: + 'A(Of T)' contains 'A(Of A(Of T))' (variable 'x'). + Dim x As A(Of A(Of T)) + ~ +BC30294: Structure 'C' cannot contain an instance of itself: + 'C(Of T)' contains 'C(Of C(Of T))' (variable 'x'). + Dim x As C(Of C(Of T)) + ~ +BC30294: Structure 'B' cannot contain an instance of itself: + 'B(Of T)' contains 'B(Of T)' (variable 'z'). + Dim z As B(Of T) + ~ +) + End Sub + + + + Public Sub StaticMemberExplosion_01() + Dim compilation = CompilationUtils.CreateCompilation(" +Structure A(Of T) + Shared x As A(Of A(Of T)) +End Structure + +Structure B(Of T) + Shared x As A(Of B(Of T)) +End Structure + +Structure C(Of T) + Shared x As D(Of T) +End Structure + +Structure D(Of T) + Shared x As C(Of D(Of T)) +End Structure +") + + CompileAndVerify(compilation, verify:=Verification.Skipped).VerifyDiagnostics() + End Sub + + + + Public Sub StaticMemberExplosion_02() + Dim compilation = CompilationUtils.CreateCompilation(" +Structure A(Of T) + Shared x As A(Of A(Of T)) +End Structure + +Structure B(Of T) + Shared x As A(Of C(Of B(Of T))) +End Structure + +Structure C(Of T) +End Structure +") + + CompileAndVerify(compilation, verify:=Verification.Skipped).VerifyDiagnostics() + End Sub + Public Sub SynthesizedConstructorLocation() Dim compilation = CompilationUtils.CreateCompilationWithMscorlib40( From 4935399ac43de9f0fc562d4123204d7418951fdd Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 16:11:06 -0800 Subject: [PATCH 308/508] Add fluent case for 'use collection expressoin' --- ...onExpressionForFluentDiagnosticAnalyzer.cs | 11 ++- .../UseCollectionExpressionForFluentTests.cs | 89 ++++++++++++++++++- 2 files changed, 94 insertions(+), 6 deletions(-) diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForFluentDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForFluentDiagnosticAnalyzer.cs index 42f9408bd91ca..9202b1ad3d802 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForFluentDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForFluentDiagnosticAnalyzer.cs @@ -341,8 +341,8 @@ bool IsListLike(ExpressionSyntax expression) return false; return - Implements(type, compilation.IListOfTType()) || - Implements(type, compilation.IReadOnlyListOfTType()); + EqualsOrImplements(type, compilation.IListOfTType()) || + EqualsOrImplements(type, compilation.IReadOnlyListOfTType()); } bool IsIterable(ExpressionSyntax expression) @@ -354,15 +354,18 @@ bool IsIterable(ExpressionSyntax expression) if (s_bannedTypes.Contains(type.Name)) return false; - return Implements(type, compilation.IEnumerableOfTType()) || + return EqualsOrImplements(type, compilation.IEnumerableOfTType()) || type.Equals(compilation.SpanOfTType()) || type.Equals(compilation.ReadOnlySpanOfTType()); } - static bool Implements(ITypeSymbol type, INamedTypeSymbol? interfaceType) + static bool EqualsOrImplements(ITypeSymbol type, INamedTypeSymbol? interfaceType) { if (interfaceType != null) { + if (type.OriginalDefinition.Equals(interfaceType)) + return true; + foreach (var baseInterface in type.AllInterfaces) { if (interfaceType.Equals(baseInterface.OriginalDefinition)) diff --git a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForFluentTests.cs b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForFluentTests.cs index b3413c13b4dba..cc4097d2d84f3 100644 --- a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForFluentTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForFluentTests.cs @@ -2700,7 +2700,7 @@ void M() } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71145")] - public async Task TestNotInParallelEnumerable() + public async Task TestNotInParallelEnumerable1() { await new VerifyCS.Test { @@ -2711,6 +2711,55 @@ public async Task TestNotInParallelEnumerable() using System.Linq; using System.Linq.Expressions; + class C + { + void M() + { + const bool shouldParallelize = false; + + IEnumerable sequence = null!; + + var result = sequence.AsParallel().ToArray(); + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + }.RunAsync(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71145")] + public async Task TestNotInParallelEnumerable2() + { + await new VerifyCS.Test + { + TestCode = + """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Linq.Expressions; + + class C + { + void M() + { + const bool shouldParallelize = false; + + IEnumerable sequence = null!; + + var result = shouldParallelize + ? sequence.AsParallel().ToArray() + : sequence.[|ToArray|](); + } + } + """, + FixedCode = + """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Linq.Expressions; + class C { void M() @@ -2721,7 +2770,7 @@ void M() var result = shouldParallelize ? sequence.AsParallel().ToArray() - : sequence.ToArray(); + : [.. sequence]; } } """, @@ -3066,4 +3115,40 @@ void M(int[] values, int[] x) ReferenceAssemblies = ReferenceAssemblies.Net.Net80, }.RunAsync(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75870")] + public async Task TestSelectToImmutableArray() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Linq; + using System.Collections.Generic; + using System.Collections.Immutable; + + class C + { + ImmutableArray GetFormattedNumbers(ImmutableArray numbers) + { + return numbers.Select(n => $"Number: {n}").[|ToImmutableArray|](); + } + } + """, + FixedCode = """ + using System.Linq; + using System.Collections.Generic; + using System.Collections.Immutable; + + class C + { + ImmutableArray GetFormattedNumbers(ImmutableArray numbers) + { + return [.. numbers.Select(n => $"Number: {n}")]; + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + }.RunAsync(); + } } From b64c161b1b2da1ca14486c3651ecc6f8e52d827f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 16:45:07 -0800 Subject: [PATCH 309/508] Add create case for 'use collection expression' --- ...onExpressionForCreateDiagnosticAnalyzer.cs | 15 +++-- ...onExpressionForFluentDiagnosticAnalyzer.cs | 52 ++------------ .../UseCollectionExpressionHelpers.cs | 59 +++++++++++++++- ...ctionExpressionForCreateCodeFixProvider.cs | 3 +- .../UseCollectionExpressionForCreateTests.cs | 67 ++++++++++++++++++- .../UseCollectionExpressionForFluentTests.cs | 36 ++++++++++ 6 files changed, 174 insertions(+), 58 deletions(-) diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForCreateDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForCreateDiagnosticAnalyzer.cs index de116646207c7..2cfdf81b3243a 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForCreateDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForCreateDiagnosticAnalyzer.cs @@ -21,9 +21,7 @@ internal sealed partial class CSharpUseCollectionExpressionForCreateDiagnosticAn EnforceOnBuildValues.UseCollectionExpressionForCreate) { public const string UnwrapArgument = nameof(UnwrapArgument); - - private static readonly ImmutableDictionary s_unwrapArgumentProperties = - ImmutableDictionary.Empty.Add(UnwrapArgument, UnwrapArgument); + public const string UseSpread = nameof(UseSpread); protected override void InitializeWorker(CodeBlockStartAnalysisContext context, INamedTypeSymbol? expressionType) => context.RegisterSyntaxNodeAction(context => AnalyzeInvocationExpression(context, expressionType), SyntaxKind.InvocationExpression); @@ -40,7 +38,7 @@ private void AnalyzeInvocationExpression(SyntaxNodeAnalysisContext context, INam return; var invocationExpression = (InvocationExpressionSyntax)context.Node; - if (!IsCollectionFactoryCreate(semanticModel, invocationExpression, out var memberAccess, out var unwrapArgument, cancellationToken)) + if (!IsCollectionFactoryCreate(semanticModel, invocationExpression, out var memberAccess, out var unwrapArgument, out var useSpread, cancellationToken)) return; // Make sure we can actually use a collection expression in place of the full invocation. @@ -52,7 +50,14 @@ private void AnalyzeInvocationExpression(SyntaxNodeAnalysisContext context, INam } var locations = ImmutableArray.Create(invocationExpression.GetLocation()); - var properties = unwrapArgument ? s_unwrapArgumentProperties : ImmutableDictionary.Empty; + var properties = ImmutableDictionary.Empty; + + if (unwrapArgument) + properties = properties.Add(UnwrapArgument, ""); + + if (useSpread) + properties = properties.Add(UseSpread, ""); + if (changesSemantics) properties = properties.Add(UseCollectionInitializerHelpers.ChangesSemanticsName, ""); diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForFluentDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForFluentDiagnosticAnalyzer.cs index 9202b1ad3d802..50292ab3f5721 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForFluentDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForFluentDiagnosticAnalyzer.cs @@ -63,17 +63,6 @@ internal sealed partial class CSharpUseCollectionExpressionForFluentDiagnosticAn nameof(System.Collections.Immutable), ]; - /// - /// Set of type-names that are blocked from moving over to collection expressions because the semantics of them are - /// known to be specialized, and thus could change semantics in undesirable ways if the compiler emitted its own - /// code as an replacement. - /// - private static readonly ImmutableHashSet s_bannedTypes = [ - nameof(ParallelEnumerable), - nameof(ParallelQuery), - // Special internal runtime interface that is optimized for fast path conversions of collections. - "IIListProvider"]; - protected override void InitializeWorker(CodeBlockStartAnalysisContext context, INamedTypeSymbol? expressionType) => context.RegisterSyntaxNodeAction(context => AnalyzeMemberAccess(context, expressionType), SyntaxKind.SimpleMemberAccessExpression); @@ -272,12 +261,12 @@ private static bool AnalyzeInvocation( // Forms like `ImmutableArray.Create(...)` or `ImmutableArray.CreateRange(...)` are fine base cases. if (current is InvocationExpressionSyntax currentInvocationExpression && - IsCollectionFactoryCreate(semanticModel, currentInvocationExpression, out var factoryMemberAccess, out var unwrapArgument, cancellationToken)) + IsCollectionFactoryCreate(semanticModel, currentInvocationExpression, out var factoryMemberAccess, out var unwrapArgument, out var useSpread, cancellationToken)) { if (!IsListLike(current)) return false; - AddArgumentsInReverse(postMatchesInReverse, GetArguments(currentInvocationExpression, unwrapArgument), useSpread: false); + AddArgumentsInReverse(postMatchesInReverse, GetArguments(currentInvocationExpression, unwrapArgument), useSpread); return true; } @@ -292,7 +281,7 @@ private static bool AnalyzeInvocation( // Down to some final collection. Like `x` in `x.Concat(y).ToArray()`. If `x` is itself is something that // can be iterated, we can convert this to `[.. x, .. y]`. Note: we only want to do this if ending with one // of the ToXXX Methods. If we just have `x.AddRange(y)` it's preference to keep that, versus `[.. x, ..y]` - if (!isAdditionMatch && IsIterable(current)) + if (!isAdditionMatch && IsIterable(semanticModel, current, cancellationToken)) { AddFinalMatch(current); return true; @@ -345,37 +334,6 @@ bool IsListLike(ExpressionSyntax expression) EqualsOrImplements(type, compilation.IReadOnlyListOfTType()); } - bool IsIterable(ExpressionSyntax expression) - { - var type = semanticModel.GetTypeInfo(expression, cancellationToken).Type; - if (type is null or IErrorTypeSymbol) - return false; - - if (s_bannedTypes.Contains(type.Name)) - return false; - - return EqualsOrImplements(type, compilation.IEnumerableOfTType()) || - type.Equals(compilation.SpanOfTType()) || - type.Equals(compilation.ReadOnlySpanOfTType()); - } - - static bool EqualsOrImplements(ITypeSymbol type, INamedTypeSymbol? interfaceType) - { - if (interfaceType != null) - { - if (type.OriginalDefinition.Equals(interfaceType)) - return true; - - foreach (var baseInterface in type.AllInterfaces) - { - if (interfaceType.Equals(baseInterface.OriginalDefinition)) - return true; - } - } - - return false; - } - static bool IsLegalInitializer(InitializerExpressionSyntax? initializer) { // We can't convert any initializer that contains an initializer in it. For example `new SomeType() { { 1, @@ -429,12 +387,12 @@ private static bool IsMatch( // Check to make sure we're not calling something banned because it would change semantics. First check if the // method itself comes from a banned type (like with an extension method). var member = state.SemanticModel.GetSymbolInfo(memberAccess, cancellationToken).Symbol; - if (s_bannedTypes.Contains(member?.ContainingType.Name)) + if (BannedTypes.Contains(member?.ContainingType.Name)) return false; // Next, check if we're invoking this on a banned type. var type = state.SemanticModel.GetTypeInfo(memberAccess.Expression, cancellationToken).Type; - if (s_bannedTypes.Contains(type?.Name)) + if (BannedTypes.Contains(type?.Name)) return false; return true; diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs index f7fab29e21d2e..ed7a5246fcc9f 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs @@ -29,6 +29,17 @@ internal static class UseCollectionExpressionHelpers { private static readonly CollectionExpressionSyntax s_emptyCollectionExpression = CollectionExpression(); + /// + /// Set of type-names that are blocked from moving over to collection expressions because the semantics of them are + /// known to be specialized, and thus could change semantics in undesirable ways if the compiler emitted its own + /// code as an replacement. + /// + public static readonly ImmutableHashSet BannedTypes = [ + nameof(ParallelEnumerable), + nameof(ParallelQuery), + // Special internal runtime interface that is optimized for fast path conversions of collections. + "IIListProvider"]; + private static readonly SymbolEquivalenceComparer s_tupleNamesCanDifferComparer = SymbolEquivalenceComparer.Create( // Not relevant. We are not comparing method signatures. distinguishRefFromOut: true, @@ -941,12 +952,14 @@ public static bool IsCollectionFactoryCreate( InvocationExpressionSyntax invocationExpression, [NotNullWhen(true)] out MemberAccessExpressionSyntax? memberAccess, out bool unwrapArgument, + out bool useSpread, CancellationToken cancellationToken) { const string CreateName = nameof(ImmutableArray.Create); const string CreateRangeName = nameof(ImmutableArray.CreateRange); unwrapArgument = false; + useSpread = false; memberAccess = null; // Looking for `XXX.Create(...)` @@ -988,16 +1001,18 @@ public static bool IsCollectionFactoryCreate( // `Create(params T[])` (passing as individual elements, or an array with an initializer) // `Create(ReadOnlySpan)` (passing as a stack-alloc with an initializer) // `Create(IEnumerable)` (passing as something with an initializer. - if (!IsCompatibleSignatureAndArguments(createMethod.OriginalDefinition, out unwrapArgument)) + if (!IsCompatibleSignatureAndArguments(createMethod.OriginalDefinition, out unwrapArgument, out useSpread)) return false; return true; bool IsCompatibleSignatureAndArguments( IMethodSymbol originalCreateMethod, - out bool unwrapArgument) + out bool unwrapArgument, + out bool useSpread) { unwrapArgument = false; + useSpread = false; var arguments = invocationExpression.ArgumentList.Arguments; @@ -1040,6 +1055,14 @@ bool IsCompatibleSignatureAndArguments( unwrapArgument = true; return true; } + + if (IsIterable(semanticModel, argExpression, cancellationToken)) + { + // Convert `ImmutableArray.Create(someEnumerable)` to `[.. someEnumerable]` + unwrapArgument = false; + useSpread = true; + return true; + } } } else if (originalCreateMethod.Name is CreateName) @@ -1102,6 +1125,38 @@ originalCreateMethod.Parameters is [ } } + public static bool IsIterable(SemanticModel semanticModel, ExpressionSyntax expression, CancellationToken cancellationToken) + { + var type = semanticModel.GetTypeInfo(expression, cancellationToken).Type; + if (type is null or IErrorTypeSymbol) + return false; + + if (BannedTypes.Contains(type.Name)) + return false; + + var compilation = semanticModel.Compilation; + return EqualsOrImplements(type, compilation.IEnumerableOfTType()) || + type.Equals(compilation.SpanOfTType()) || + type.Equals(compilation.ReadOnlySpanOfTType()); + } + + public static bool EqualsOrImplements(ITypeSymbol type, INamedTypeSymbol? interfaceType) + { + if (interfaceType != null) + { + if (type.OriginalDefinition.Equals(interfaceType)) + return true; + + foreach (var baseInterface in type.AllInterfaces) + { + if (interfaceType.Equals(baseInterface.OriginalDefinition)) + return true; + } + } + + return false; + } + public static bool IsCollectionEmptyAccess( SemanticModel semanticModel, ExpressionSyntax expression, diff --git a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForCreateCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForCreateCodeFixProvider.cs index ee052d56fe70e..1270da02da892 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForCreateCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForCreateCodeFixProvider.cs @@ -40,6 +40,7 @@ protected override async Task FixAsync( CancellationToken cancellationToken) { var unwrapArgument = properties.ContainsKey(CSharpUseCollectionExpressionForCreateDiagnosticAnalyzer.UnwrapArgument); + var useSpread = properties.ContainsKey(CSharpUseCollectionExpressionForCreateDiagnosticAnalyzer.UseSpread); // We want to replace `XXX.Create(...)` with the new collection expression. To do this, we go through the // following steps. First, we replace `XXX.Create(a, b, c)` with `new(a, b, c)` (a dummy object creation @@ -62,7 +63,7 @@ protected override async Task FixAsync( semanticDocument.Root.ReplaceNode(invocationExpression, dummyObjectCreation), cancellationToken).ConfigureAwait(false); dummyObjectCreation = (ImplicitObjectCreationExpressionSyntax)newSemanticDocument.Root.GetAnnotatedNodes(dummyObjectAnnotation).Single(); var expressions = dummyObjectCreation.ArgumentList.Arguments.Select(a => a.Expression); - var matches = expressions.SelectAsArray(static e => new CollectionMatch(e, UseSpread: false)); + var matches = expressions.SelectAsArray(e => new CollectionMatch(e, useSpread)); var collectionExpression = await CreateCollectionExpressionAsync( newSemanticDocument.Document, diff --git a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForCreateTests.cs b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForCreateTests.cs index 5c90dc6dd2fe0..14810cc0d08e3 100644 --- a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForCreateTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForCreateTests.cs @@ -489,7 +489,18 @@ public async Task TestCreateRange_ComputedExpression() class C { - MyCollection i = MyCollection.CreateRange(GetValues()); + MyCollection i = [|MyCollection.[|CreateRange|](|]GetValues()); + + static IEnumerable GetValues() => default; + } + """ + s_collectionBuilderApi + s_basicCollectionApi, + FixedCode = """ + using System; + using System.Collections.Generic; + + class C + { + MyCollection i = [.. GetValues()]; static IEnumerable GetValues() => default; } @@ -507,7 +518,13 @@ public async Task TestCreateRange_ExplicitArray1() TestCode = """ class C { - MyCollection i = MyCollection.CreateRange(new int [5]); + MyCollection i = [|MyCollection.[|CreateRange|](|]new int [5]); + } + """ + s_collectionBuilderApi + s_basicCollectionApi, + FixedCode = """ + class C + { + MyCollection i = [.. new int [5]]; } """ + s_collectionBuilderApi + s_basicCollectionApi, LanguageVersion = LanguageVersion.CSharp12, @@ -757,7 +774,15 @@ public async Task TestCreateRange_NewImplicitObject() class C { - MyCollection i = MyCollection.CreateRange({|CS0144:new() { }|]); + MyCollection i = [|MyCollection.[|CreateRange|](|]{|CS0144:new() { }|}); + } + """ + s_collectionBuilderApi + s_basicCollectionApi, + FixedCode = """ + using System.Collections.Generic; + + class C + { + MyCollection i = [.. {|CS8754:new() { }|}]; } """ + s_collectionBuilderApi + s_basicCollectionApi, LanguageVersion = LanguageVersion.CSharp12, @@ -1264,4 +1289,40 @@ class C """ }.RunAsync(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75870")] + public async Task TestIEnumerablePassedToCreateRange() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Linq; + using System.Collections.Generic; + using System.Collections.Immutable; + + class C + { + ImmutableArray GetFormattedRange() + { + return [|ImmutableArray.[|CreateRange|](|]Enumerable.Range(1, 10).Select(n => $"Item {n}")); + } + } + """, + FixedCode = """ + using System.Linq; + using System.Collections.Generic; + using System.Collections.Immutable; + + class C + { + ImmutableArray GetFormattedRange() + { + return [.. Enumerable.Range(1, 10).Select(n => $"Item {n}")]; + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + }.RunAsync(); + } } diff --git a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForFluentTests.cs b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForFluentTests.cs index cc4097d2d84f3..5dc98897c820a 100644 --- a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForFluentTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForFluentTests.cs @@ -3151,4 +3151,40 @@ ImmutableArray GetFormattedNumbers(ImmutableArray numbers) ReferenceAssemblies = ReferenceAssemblies.Net.Net80, }.RunAsync(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75870")] + public async Task TestIEnumerablePassedToListConstructor() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Linq; + using System.Collections.Generic; + using System.Collections.Immutable; + + class C + { + List GetNumbers() + { + return new List(Enumerable.Range(1, 10)); + } + } + """, + FixedCode = """ + using System.Linq; + using System.Collections.Generic; + using System.Collections.Immutable; + + class C + { + List GetNumbers() + { + return [.. Enumerable.Range(1, 10)]; + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + }.RunAsync(); + } } From 5c9d6f6abbaf519ced535708c176836fa34600cb Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 17:03:50 -0800 Subject: [PATCH 310/508] Add analyzers --- .../Analyzers/CSharpAnalyzers.projitems | 1 + ...onExpressionForCreateDiagnosticAnalyzer.cs | 14 +-- ...ctionExpressionForNewDiagnosticAnalyzer.cs | 86 +++++++++++++ .../UseCollectionExpressionHelpers.cs | 116 ++++++++++++------ .../CodeFixes/CSharpCodeFixes.projitems | 1 + ...llectionExpressionForNewCodeFixProvider.cs | 79 ++++++++++++ .../Core/Analyzers/EnforceOnBuildValues.cs | 1 + .../Core/Analyzers/IDEDiagnosticIds.cs | 1 + .../PredefinedCodeFixProviderNames.cs | 1 + 9 files changed, 249 insertions(+), 51 deletions(-) create mode 100644 src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForNewDiagnosticAnalyzer.cs create mode 100644 src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForNewCodeFixProvider.cs diff --git a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems index 5f550c427c2b5..ba405268c06a2 100644 --- a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems +++ b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems @@ -83,6 +83,7 @@ + diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForCreateDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForCreateDiagnosticAnalyzer.cs index 2cfdf81b3243a..8ce1cd87c339a 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForCreateDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForCreateDiagnosticAnalyzer.cs @@ -20,9 +20,6 @@ internal sealed partial class CSharpUseCollectionExpressionForCreateDiagnosticAn IDEDiagnosticIds.UseCollectionExpressionForCreateDiagnosticId, EnforceOnBuildValues.UseCollectionExpressionForCreate) { - public const string UnwrapArgument = nameof(UnwrapArgument); - public const string UseSpread = nameof(UseSpread); - protected override void InitializeWorker(CodeBlockStartAnalysisContext context, INamedTypeSymbol? expressionType) => context.RegisterSyntaxNodeAction(context => AnalyzeInvocationExpression(context, expressionType), SyntaxKind.InvocationExpression); @@ -50,16 +47,7 @@ private void AnalyzeInvocationExpression(SyntaxNodeAnalysisContext context, INam } var locations = ImmutableArray.Create(invocationExpression.GetLocation()); - var properties = ImmutableDictionary.Empty; - - if (unwrapArgument) - properties = properties.Add(UnwrapArgument, ""); - - if (useSpread) - properties = properties.Add(UseSpread, ""); - - if (changesSemantics) - properties = properties.Add(UseCollectionInitializerHelpers.ChangesSemanticsName, ""); + var properties = GetDiagnosticProperties(unwrapArgument, useSpread, changesSemantics); context.ReportDiagnostic(DiagnosticHelper.Create( Descriptor, diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForNewDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForNewDiagnosticAnalyzer.cs new file mode 100644 index 0000000000000..4a44e3a226916 --- /dev/null +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForNewDiagnosticAnalyzer.cs @@ -0,0 +1,86 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Shared.CodeStyle; +using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.UseCollectionInitializer; + +namespace Microsoft.CodeAnalysis.CSharp.UseCollectionExpression; + +using static UseCollectionExpressionHelpers; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +internal sealed partial class CSharpUseCollectionExpressionForNewDiagnosticAnalyzer() + : AbstractCSharpUseCollectionExpressionDiagnosticAnalyzer( + IDEDiagnosticIds.UseCollectionExpressionForNewDiagnosticId, + EnforceOnBuildValues.UseCollectionExpressionForNew) +{ + protected override void InitializeWorker(CodeBlockStartAnalysisContext context, INamedTypeSymbol? expressionType) + => context.RegisterSyntaxNodeAction(context => AnalyzeObjectCcreationExpression(context, expressionType), SyntaxKind.ObjectCreationExpression); + + private void AnalyzeObjectCcreationExpression(SyntaxNodeAnalysisContext context, INamedTypeSymbol? expressionType) + { + var semanticModel = context.SemanticModel; + var compilation = semanticModel.Compilation; + var syntaxTree = semanticModel.SyntaxTree; + var cancellationToken = context.CancellationToken; + + // no point in analyzing if the option is off. + var option = context.GetAnalyzerOptions().PreferCollectionExpression; + if (option.Value is CollectionExpressionPreference.Never || ShouldSkipAnalysis(context, option.Notification)) + return; + + var objectCreationExpression = (ObjectCreationExpressionSyntax)context.Node; + if (objectCreationExpression is not { ArgumentList.Arguments: [var argument] }) + return; + + var symbol = semanticModel.GetSymbolInfo(objectCreationExpression, cancellationToken).Symbol; + if (symbol is not IMethodSymbol { MethodKind: MethodKind.Constructor, Parameters: [var parameter] } || + !IsIEnumerableOfTParameter(compilation, parameter)) + { + return; + } + + if (!IsArgumentCompatibleWithIEnumerableOfT(semanticModel, argument, out var unwrapArgument, out var useSpread, cancellationToken)) + return; + + // Make sure we can actually use a collection expression in place of the full invocation. + var allowSemanticsChange = option.Value is CollectionExpressionPreference.WhenTypesLooselyMatch; + if (!CanReplaceWithCollectionExpression( + semanticModel, objectCreationExpression, expressionType, isSingletonInstance: false, allowSemanticsChange, skipVerificationForReplacedNode: true, cancellationToken, out var changesSemantics)) + { + return; + } + + var locations = ImmutableArray.Create(objectCreationExpression.GetLocation()); + var properties = GetDiagnosticProperties(unwrapArgument, useSpread, changesSemantics); + + context.ReportDiagnostic(DiagnosticHelper.Create( + Descriptor, + objectCreationExpression.NewKeyword.GetLocation(), + option.Notification, + context.Options, + additionalLocations: locations, + properties)); + + var additionalUnnecessaryLocations = ImmutableArray.Create( + syntaxTree.GetLocation(TextSpan.FromBounds( + objectCreationExpression.SpanStart, + objectCreationExpression.ArgumentList.OpenParenToken.Span.End)), + objectCreationExpression.ArgumentList.CloseParenToken.GetLocation()); + + context.ReportDiagnostic(DiagnosticHelper.CreateWithLocationTags( + UnnecessaryCodeDescriptor, + additionalUnnecessaryLocations[0], + NotificationOption2.ForSeverity(UnnecessaryCodeDescriptor.DefaultSeverity), + context.Options, + additionalLocations: locations, + additionalUnnecessaryLocations: additionalUnnecessaryLocations, + properties)); + } +} diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs index ed7a5246fcc9f..2ddc6dc810cb3 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs @@ -18,6 +18,7 @@ using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.UseCollectionExpression; +using Microsoft.CodeAnalysis.UseCollectionInitializer; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UseCollectionExpression; @@ -27,6 +28,9 @@ namespace Microsoft.CodeAnalysis.CSharp.UseCollectionExpression; internal static class UseCollectionExpressionHelpers { + public const string UnwrapArgument = nameof(UnwrapArgument); + public const string UseSpread = nameof(UseSpread); + private static readonly CollectionExpressionSyntax s_emptyCollectionExpression = CollectionExpression(); /// @@ -1024,45 +1028,11 @@ bool IsCompatibleSignatureAndArguments( if (originalCreateMethod.Name is CreateRangeName) { // If we have `CreateRange(IEnumerable values)` this is legal if we have an array, or no-arg object creation. - if (originalCreateMethod.Parameters is [ - { - Type: INamedTypeSymbol - { - Name: nameof(IEnumerable), - TypeArguments: [ITypeParameterSymbol { TypeParameterKind: TypeParameterKind.Method }] - } enumerableType - }] && enumerableType.OriginalDefinition.Equals(compilation.IEnumerableOfTType())) + if (originalCreateMethod.Parameters is [var parameter] && + IsIEnumerableOfTParameter(compilation, parameter) && + arguments.Count == 1) { - var argExpression = arguments[0].Expression; - if (argExpression - is ArrayCreationExpressionSyntax { Initializer: not null } - or ImplicitArrayCreationExpressionSyntax) - { - unwrapArgument = true; - return true; - } - - if (argExpression is ObjectCreationExpressionSyntax objectCreation) - { - // Can't have any arguments, as we cannot preserve them once we grab out all the elements. - if (objectCreation.ArgumentList != null && objectCreation.ArgumentList.Arguments.Count > 0) - return false; - - // If it's got an initializer, it has to be a collection initializer (or an empty object initializer); - if (objectCreation.Initializer.IsKind(SyntaxKind.ObjectCreationExpression) && objectCreation.Initializer.Expressions.Count > 0) - return false; - - unwrapArgument = true; - return true; - } - - if (IsIterable(semanticModel, argExpression, cancellationToken)) - { - // Convert `ImmutableArray.Create(someEnumerable)` to `[.. someEnumerable]` - unwrapArgument = false; - useSpread = true; - return true; - } + return IsArgumentCompatibleWithIEnumerableOfT(semanticModel, arguments[0], out unwrapArgument, out useSpread, cancellationToken); } } else if (originalCreateMethod.Name is CreateName) @@ -1125,6 +1095,60 @@ originalCreateMethod.Parameters is [ } } + public static bool IsArgumentCompatibleWithIEnumerableOfT( + SemanticModel semanticModel, ArgumentSyntax argument, out bool unwrapArgument, out bool useSpread, CancellationToken cancellationToken) + { + unwrapArgument = false; + useSpread = false; + + var argExpression = argument.Expression; + if (argExpression + is ArrayCreationExpressionSyntax { Initializer: not null } + or ImplicitArrayCreationExpressionSyntax) + { + unwrapArgument = true; + return true; + } + + if (argExpression is ObjectCreationExpressionSyntax objectCreation) + { + // Can't have any arguments, as we cannot preserve them once we grab out all the elements. + if (objectCreation.ArgumentList != null && objectCreation.ArgumentList.Arguments.Count > 0) + return false; + + // If it's got an initializer, it has to be a collection initializer (or an empty object initializer); + if (objectCreation.Initializer.IsKind(SyntaxKind.ObjectCreationExpression) && objectCreation.Initializer.Expressions.Count > 0) + return false; + + unwrapArgument = true; + return true; + } + + if (IsIterable(semanticModel, argExpression, cancellationToken)) + { + // Convert `ImmutableArray.Create(someEnumerable)` to `[.. someEnumerable]` + unwrapArgument = false; + useSpread = true; + return true; + } + + return false; + } + + public static bool IsIEnumerableOfTParameter( + Compilation compilation, IParameterSymbol parameter) + { + return parameter is + { + Type: INamedTypeSymbol + { + Name: nameof(IEnumerable), + TypeArguments: [ITypeParameterSymbol { TypeParameterKind: TypeParameterKind.Method }] + } + enumerableType + } && enumerableType.OriginalDefinition.Equals(compilation.IEnumerableOfTType()); + } + public static bool IsIterable(SemanticModel semanticModel, ExpressionSyntax expression, CancellationToken cancellationToken) { var type = semanticModel.GetTypeInfo(expression, cancellationToken).Type; @@ -1294,4 +1318,20 @@ public static SeparatedSyntaxList GetArguments(InvocationExpress public static CollectionExpressionSyntax CreateReplacementCollectionExpressionForAnalysis(InitializerExpressionSyntax? initializer) => initializer is null ? s_emptyCollectionExpression : CollectionExpression([.. initializer.Expressions.Select(ExpressionElement)]); + + public static ImmutableDictionary GetDiagnosticProperties(bool unwrapArgument, bool useSpread, bool changesSemantics) + { + var properties = ImmutableDictionary.Empty; + + if (unwrapArgument) + properties = properties.Add(UnwrapArgument, ""); + + if (useSpread) + properties = properties.Add(UseSpread, ""); + + if (changesSemantics) + properties = properties.Add(UseCollectionInitializerHelpers.ChangesSemanticsName, ""); + + return properties; + } } diff --git a/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems b/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems index 0dcf3b255450c..74ad3aea9f2a2 100644 --- a/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems +++ b/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems @@ -98,6 +98,7 @@ + diff --git a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForNewCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForNewCodeFixProvider.cs new file mode 100644 index 0000000000000..f92df9915b0fe --- /dev/null +++ b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForNewCodeFixProvider.cs @@ -0,0 +1,79 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.UseCollectionExpression; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.UseCollectionExpression; + +using static CSharpCollectionExpressionRewriter; +using static SyntaxFactory; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.UseCollectionExpressionForNew), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed partial class CSharpUseCollectionExpressionForNewCodeFixProvider() + : AbstractUseCollectionExpressionCodeFixProvider( + CSharpCodeFixesResources.Use_collection_expression, + IDEDiagnosticIds.UseCollectionExpressionForCreateDiagnosticId) +{ + public override ImmutableArray FixableDiagnosticIds { get; } = [IDEDiagnosticIds.UseCollectionExpressionForCreateDiagnosticId]; + + protected override async Task FixAsync( + Document document, + SyntaxEditor editor, + InvocationExpressionSyntax invocationExpression, + ImmutableDictionary properties, + CancellationToken cancellationToken) + { + var unwrapArgument = properties.ContainsKey(CSharpUseCollectionExpressionForCreateDiagnosticAnalyzer.UnwrapArgument); + var useSpread = properties.ContainsKey(CSharpUseCollectionExpressionForCreateDiagnosticAnalyzer.UseSpread); + + // We want to replace `XXX.Create(...)` with the new collection expression. To do this, we go through the + // following steps. First, we replace `XXX.Create(a, b, c)` with `new(a, b, c)` (a dummy object creation + // expression). We then call into our helper which replaces expressions with collection expressions. The reason + // for the dummy object creation expression is that it serves as an actual node the rewriting code can attach an + // initializer to, by which it can figure out appropriate wrapping and indentation for the collection expression + // elements. + + var semanticDocument = await SemanticDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false); + + // Get the expressions that we're going to fill the new collection expression with. + var arguments = UseCollectionExpressionHelpers.GetArguments(invocationExpression, unwrapArgument); + + var dummyObjectAnnotation = new SyntaxAnnotation(); + var dummyObjectCreation = ImplicitObjectCreationExpression(ArgumentList(arguments), initializer: null) + .WithTriviaFrom(invocationExpression) + .WithAdditionalAnnotations(dummyObjectAnnotation); + + var newSemanticDocument = await semanticDocument.WithSyntaxRootAsync( + semanticDocument.Root.ReplaceNode(invocationExpression, dummyObjectCreation), cancellationToken).ConfigureAwait(false); + dummyObjectCreation = (ImplicitObjectCreationExpressionSyntax)newSemanticDocument.Root.GetAnnotatedNodes(dummyObjectAnnotation).Single(); + var expressions = dummyObjectCreation.ArgumentList.Arguments.Select(a => a.Expression); + var matches = expressions.SelectAsArray(e => new CollectionMatch(e, useSpread)); + + var collectionExpression = await CreateCollectionExpressionAsync( + newSemanticDocument.Document, + dummyObjectCreation, + preMatches: [], + matches, + static o => o.Initializer, + static (o, i) => o.WithInitializer(i), + cancellationToken).ConfigureAwait(false); + + editor.ReplaceNode(invocationExpression, collectionExpression); + } +} diff --git a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs index ec58d8982660a..16bc901f9533a 100644 --- a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs +++ b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs @@ -96,6 +96,7 @@ internal static class EnforceOnBuildValues public const EnforceOnBuild UseCollectionExpressionForCreate = /*IDE0303*/ EnforceOnBuild.Recommended; public const EnforceOnBuild UseCollectionExpressionForBuilder = /*IDE0304*/ EnforceOnBuild.Recommended; public const EnforceOnBuild UseCollectionExpressionForFluent = /*IDE0305*/ EnforceOnBuild.Recommended; + public const EnforceOnBuild UseCollectionExpressionForNew = /*IDE0306*/ EnforceOnBuild.Recommended; public const EnforceOnBuild MakeAnonymousFunctionStatic = /*IDE0320*/ EnforceOnBuild.Recommended; public const EnforceOnBuild UseSystemThreadingLock = /*IDE0330*/ EnforceOnBuild.Recommended; diff --git a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs index 91856fd7d7468..abb14572b56d6 100644 --- a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs +++ b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs @@ -198,6 +198,7 @@ internal static class IDEDiagnosticIds public const string UseCollectionExpressionForCreateDiagnosticId = "IDE0303"; public const string UseCollectionExpressionForBuilderDiagnosticId = "IDE0304"; public const string UseCollectionExpressionForFluentDiagnosticId = "IDE0305"; + public const string UseCollectionExpressionForNewDiagnosticId = "IDE0306"; public const string MakeAnonymousFunctionStaticDiagnosticId = "IDE0320"; diff --git a/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs b/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs index e6e5e1a492e32..9331fb48b1947 100644 --- a/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs +++ b/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs @@ -137,6 +137,7 @@ internal static class PredefinedCodeFixProviderNames public const string UseCollectionExpressionForCreate = nameof(UseCollectionExpressionForCreate); public const string UseCollectionExpressionForEmpty = nameof(UseCollectionExpressionForEmpty); public const string UseCollectionExpressionForFluent = nameof(UseCollectionExpressionForFluent); + public const string UseCollectionExpressionForNew = nameof(UseCollectionExpressionForNew); public const string UseCollectionExpressionForStackAlloc = nameof(UseCollectionExpressionForStackAlloc); public const string UseCollectionInitializer = nameof(UseCollectionInitializer); public const string UseCompoundAssignment = nameof(UseCompoundAssignment); From 4e859a3938d8338aa62d02afdc075cb7495b1a43 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 17:19:54 -0800 Subject: [PATCH 311/508] Add fixer --- ...onExpressionForFluentDiagnosticAnalyzer.cs | 2 +- ...ctionExpressionForNewDiagnosticAnalyzer.cs | 11 ++- .../UseCollectionExpressionHelpers.cs | 43 ++++----- ...ctionExpressionForCreateCodeFixProvider.cs | 7 +- ...llectionExpressionForNewCodeFixProvider.cs | 26 +++--- .../Tests/CSharpAnalyzers.UnitTests.projitems | 1 + .../UseCollectionExpressionForArrayTests.cs | 2 +- .../UseCollectionExpressionForFluentTests.cs | 36 -------- .../UseCollectionExpressionForNewTests.cs | 92 +++++++++++++++++++ 9 files changed, 138 insertions(+), 82 deletions(-) create mode 100644 src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForNewTests.cs diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForFluentDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForFluentDiagnosticAnalyzer.cs index 50292ab3f5721..721d79add95a5 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForFluentDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForFluentDiagnosticAnalyzer.cs @@ -266,7 +266,7 @@ private static bool AnalyzeInvocation( if (!IsListLike(current)) return false; - AddArgumentsInReverse(postMatchesInReverse, GetArguments(currentInvocationExpression, unwrapArgument), useSpread); + AddArgumentsInReverse(postMatchesInReverse, GetArguments(currentInvocationExpression.ArgumentList, unwrapArgument), useSpread); return true; } diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForNewDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForNewDiagnosticAnalyzer.cs index 4a44e3a226916..a2dce85ef7739 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForNewDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForNewDiagnosticAnalyzer.cs @@ -2,11 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Shared.CodeStyle; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.UseCollectionInitializer; @@ -21,9 +23,9 @@ internal sealed partial class CSharpUseCollectionExpressionForNewDiagnosticAnaly EnforceOnBuildValues.UseCollectionExpressionForNew) { protected override void InitializeWorker(CodeBlockStartAnalysisContext context, INamedTypeSymbol? expressionType) - => context.RegisterSyntaxNodeAction(context => AnalyzeObjectCcreationExpression(context, expressionType), SyntaxKind.ObjectCreationExpression); + => context.RegisterSyntaxNodeAction(context => AnalyzeObjectCreationExpression(context, expressionType), SyntaxKind.ObjectCreationExpression); - private void AnalyzeObjectCcreationExpression(SyntaxNodeAnalysisContext context, INamedTypeSymbol? expressionType) + private void AnalyzeObjectCreationExpression(SyntaxNodeAnalysisContext context, INamedTypeSymbol? expressionType) { var semanticModel = context.SemanticModel; var compilation = semanticModel.Compilation; @@ -41,11 +43,14 @@ private void AnalyzeObjectCcreationExpression(SyntaxNodeAnalysisContext context, var symbol = semanticModel.GetSymbolInfo(objectCreationExpression, cancellationToken).Symbol; if (symbol is not IMethodSymbol { MethodKind: MethodKind.Constructor, Parameters: [var parameter] } || - !IsIEnumerableOfTParameter(compilation, parameter)) + parameter.Type.Name != nameof(IEnumerable)) { return; } + if (!Equals(compilation.IEnumerableOfTType(), parameter.Type.OriginalDefinition)) + return; + if (!IsArgumentCompatibleWithIEnumerableOfT(semanticModel, argument, out var unwrapArgument, out var useSpread, cancellationToken)) return; diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs index 2ddc6dc810cb3..5d3e50199e4f7 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs @@ -1028,9 +1028,16 @@ bool IsCompatibleSignatureAndArguments( if (originalCreateMethod.Name is CreateRangeName) { // If we have `CreateRange(IEnumerable values)` this is legal if we have an array, or no-arg object creation. - if (originalCreateMethod.Parameters is [var parameter] && - IsIEnumerableOfTParameter(compilation, parameter) && - arguments.Count == 1) + if (arguments.Count == 1 && + originalCreateMethod.Parameters is [var parameter] && + parameter is + { + Type: INamedTypeSymbol + { + Name: nameof(IEnumerable), + TypeArguments: [ITypeParameterSymbol { TypeParameterKind: TypeParameterKind.Method }] + } enumerableType + } && enumerableType.OriginalDefinition.Equals(compilation.IEnumerableOfTType())) { return IsArgumentCompatibleWithIEnumerableOfT(semanticModel, arguments[0], out unwrapArgument, out useSpread, cancellationToken); } @@ -1069,13 +1076,13 @@ bool IsCompatibleSignatureAndArguments( if (arguments.Count == 1 && compilation.SupportsRuntimeCapability(RuntimeCapability.InlineArrayTypes) && originalCreateMethod.Parameters is [ + { + Type: INamedTypeSymbol { - Type: INamedTypeSymbol - { - Name: nameof(Span) or nameof(ReadOnlySpan), - TypeArguments: [ITypeParameterSymbol { TypeParameterKind: TypeParameterKind.Method }] - } spanType - }]) + Name: nameof(Span) or nameof(ReadOnlySpan), + TypeArguments: [ITypeParameterSymbol { TypeParameterKind: TypeParameterKind.Method }] + } spanType + }]) { if (spanType.OriginalDefinition.Equals(compilation.SpanOfTType()) || spanType.OriginalDefinition.Equals(compilation.ReadOnlySpanOfTType())) @@ -1135,20 +1142,6 @@ public static bool IsArgumentCompatibleWithIEnumerableOfT( return false; } - public static bool IsIEnumerableOfTParameter( - Compilation compilation, IParameterSymbol parameter) - { - return parameter is - { - Type: INamedTypeSymbol - { - Name: nameof(IEnumerable), - TypeArguments: [ITypeParameterSymbol { TypeParameterKind: TypeParameterKind.Method }] - } - enumerableType - } && enumerableType.OriginalDefinition.Equals(compilation.IEnumerableOfTType()); - } - public static bool IsIterable(SemanticModel semanticModel, ExpressionSyntax expression, CancellationToken cancellationToken) { var type = semanticModel.GetTypeInfo(expression, cancellationToken).Type; @@ -1287,9 +1280,9 @@ static bool IsPossiblyDottedName(ExpressionSyntax name) } } - public static SeparatedSyntaxList GetArguments(InvocationExpressionSyntax invocationExpression, bool unwrapArgument) + public static SeparatedSyntaxList GetArguments(ArgumentListSyntax argumentList, bool unwrapArgument) { - var arguments = invocationExpression.ArgumentList.Arguments; + var arguments = argumentList.Arguments; // If we're not unwrapping a singular argument expression, then just pass back all the explicit argument // expressions the user wrote out. diff --git a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForCreateCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForCreateCodeFixProvider.cs index 1270da02da892..d29cecd68c36e 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForCreateCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForCreateCodeFixProvider.cs @@ -21,6 +21,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseCollectionExpression; using static CSharpCollectionExpressionRewriter; using static SyntaxFactory; +using static UseCollectionExpressionHelpers; [ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.UseCollectionExpressionForCreate), Shared] [method: ImportingConstructor] @@ -39,8 +40,8 @@ protected override async Task FixAsync( ImmutableDictionary properties, CancellationToken cancellationToken) { - var unwrapArgument = properties.ContainsKey(CSharpUseCollectionExpressionForCreateDiagnosticAnalyzer.UnwrapArgument); - var useSpread = properties.ContainsKey(CSharpUseCollectionExpressionForCreateDiagnosticAnalyzer.UseSpread); + var unwrapArgument = properties.ContainsKey(UnwrapArgument); + var useSpread = properties.ContainsKey(UseSpread); // We want to replace `XXX.Create(...)` with the new collection expression. To do this, we go through the // following steps. First, we replace `XXX.Create(a, b, c)` with `new(a, b, c)` (a dummy object creation @@ -52,7 +53,7 @@ protected override async Task FixAsync( var semanticDocument = await SemanticDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false); // Get the expressions that we're going to fill the new collection expression with. - var arguments = UseCollectionExpressionHelpers.GetArguments(invocationExpression, unwrapArgument); + var arguments = GetArguments(invocationExpression.ArgumentList, unwrapArgument); var dummyObjectAnnotation = new SyntaxAnnotation(); var dummyObjectCreation = ImplicitObjectCreationExpression(ArgumentList(arguments), initializer: null) diff --git a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForNewCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForNewCodeFixProvider.cs index f92df9915b0fe..464ef81fb6a90 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForNewCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForNewCodeFixProvider.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; @@ -21,29 +20,30 @@ namespace Microsoft.CodeAnalysis.CSharp.UseCollectionExpression; using static CSharpCollectionExpressionRewriter; using static SyntaxFactory; +using static UseCollectionExpressionHelpers; [ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.UseCollectionExpressionForNew), Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] internal sealed partial class CSharpUseCollectionExpressionForNewCodeFixProvider() - : AbstractUseCollectionExpressionCodeFixProvider( + : AbstractUseCollectionExpressionCodeFixProvider( CSharpCodeFixesResources.Use_collection_expression, - IDEDiagnosticIds.UseCollectionExpressionForCreateDiagnosticId) + IDEDiagnosticIds.UseCollectionExpressionForNewDiagnosticId) { - public override ImmutableArray FixableDiagnosticIds { get; } = [IDEDiagnosticIds.UseCollectionExpressionForCreateDiagnosticId]; + public override ImmutableArray FixableDiagnosticIds { get; } = [IDEDiagnosticIds.UseCollectionExpressionForNewDiagnosticId]; protected override async Task FixAsync( Document document, SyntaxEditor editor, - InvocationExpressionSyntax invocationExpression, + ObjectCreationExpressionSyntax objectCreationExpression, ImmutableDictionary properties, CancellationToken cancellationToken) { - var unwrapArgument = properties.ContainsKey(CSharpUseCollectionExpressionForCreateDiagnosticAnalyzer.UnwrapArgument); - var useSpread = properties.ContainsKey(CSharpUseCollectionExpressionForCreateDiagnosticAnalyzer.UseSpread); + var unwrapArgument = properties.ContainsKey(UnwrapArgument); + var useSpread = properties.ContainsKey(UseSpread); - // We want to replace `XXX.Create(...)` with the new collection expression. To do this, we go through the - // following steps. First, we replace `XXX.Create(a, b, c)` with `new(a, b, c)` (a dummy object creation + // We want to replace `new ...(...)` with the new collection expression. To do this, we go through the + // following steps. First, we replace `new XXX(a, b, c)` with `new(a, b, c)` (a dummy object creation // expression). We then call into our helper which replaces expressions with collection expressions. The reason // for the dummy object creation expression is that it serves as an actual node the rewriting code can attach an // initializer to, by which it can figure out appropriate wrapping and indentation for the collection expression @@ -52,15 +52,15 @@ protected override async Task FixAsync( var semanticDocument = await SemanticDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false); // Get the expressions that we're going to fill the new collection expression with. - var arguments = UseCollectionExpressionHelpers.GetArguments(invocationExpression, unwrapArgument); + var arguments = GetArguments(objectCreationExpression.ArgumentList!, unwrapArgument); var dummyObjectAnnotation = new SyntaxAnnotation(); var dummyObjectCreation = ImplicitObjectCreationExpression(ArgumentList(arguments), initializer: null) - .WithTriviaFrom(invocationExpression) + .WithTriviaFrom(objectCreationExpression) .WithAdditionalAnnotations(dummyObjectAnnotation); var newSemanticDocument = await semanticDocument.WithSyntaxRootAsync( - semanticDocument.Root.ReplaceNode(invocationExpression, dummyObjectCreation), cancellationToken).ConfigureAwait(false); + semanticDocument.Root.ReplaceNode(objectCreationExpression, dummyObjectCreation), cancellationToken).ConfigureAwait(false); dummyObjectCreation = (ImplicitObjectCreationExpressionSyntax)newSemanticDocument.Root.GetAnnotatedNodes(dummyObjectAnnotation).Single(); var expressions = dummyObjectCreation.ArgumentList.Arguments.Select(a => a.Expression); var matches = expressions.SelectAsArray(e => new CollectionMatch(e, useSpread)); @@ -74,6 +74,6 @@ protected override async Task FixAsync( static (o, i) => o.WithInitializer(i), cancellationToken).ConfigureAwait(false); - editor.ReplaceNode(invocationExpression, collectionExpression); + editor.ReplaceNode(objectCreationExpression, collectionExpression); } } diff --git a/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems b/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems index cb7713cf037fb..4151a269c71ff 100644 --- a/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems +++ b/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems @@ -115,6 +115,7 @@ + diff --git a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForArrayTests.cs b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForArrayTests.cs index 863c202e575e3..8e8bf8c71c873 100644 --- a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForArrayTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForArrayTests.cs @@ -19,7 +19,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Analyzers.UnitTests.UseCollectionExpress CSharpUseCollectionExpressionForArrayCodeFixProvider>; [Trait(Traits.Feature, Traits.Features.CodeActionsUseCollectionExpression)] -public class UseCollectionExpressionForArrayTests +public sealed class UseCollectionExpressionForArrayTests { [Fact] public async Task TestNotInCSharp11() diff --git a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForFluentTests.cs b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForFluentTests.cs index 5dc98897c820a..cc4097d2d84f3 100644 --- a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForFluentTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForFluentTests.cs @@ -3151,40 +3151,4 @@ ImmutableArray GetFormattedNumbers(ImmutableArray numbers) ReferenceAssemblies = ReferenceAssemblies.Net.Net80, }.RunAsync(); } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75870")] - public async Task TestIEnumerablePassedToListConstructor() - { - await new VerifyCS.Test - { - TestCode = """ - using System.Linq; - using System.Collections.Generic; - using System.Collections.Immutable; - - class C - { - List GetNumbers() - { - return new List(Enumerable.Range(1, 10)); - } - } - """, - FixedCode = """ - using System.Linq; - using System.Collections.Generic; - using System.Collections.Immutable; - - class C - { - List GetNumbers() - { - return [.. Enumerable.Range(1, 10)]; - } - } - """, - LanguageVersion = LanguageVersion.CSharp12, - ReferenceAssemblies = ReferenceAssemblies.Net.Net80, - }.RunAsync(); - } } diff --git a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForNewTests.cs b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForNewTests.cs new file mode 100644 index 0000000000000..250bee8dfda87 --- /dev/null +++ b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForNewTests.cs @@ -0,0 +1,92 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.UseCollectionExpression; +using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Testing; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.Analyzers.UnitTests.UseCollectionExpression; + +using VerifyCS = CSharpCodeFixVerifier< + CSharpUseCollectionExpressionForNewDiagnosticAnalyzer, + CSharpUseCollectionExpressionForNewCodeFixProvider>; + +[Trait(Traits.Feature, Traits.Features.CodeActionsUseCollectionExpression)] +public sealed class UseCollectionExpressionForNewTests +{ + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75870")] + public async Task TestIEnumerablePassedToListConstructor() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Linq; + using System.Collections.Generic; + using System.Collections.Immutable; + + class C + { + List GetNumbers() + { + return [|[|new|] List(|]Enumerable.Range(1, 10)); + } + } + """, + FixedCode = """ + using System.Linq; + using System.Collections.Generic; + using System.Collections.Immutable; + + class C + { + List GetNumbers() + { + return [.. Enumerable.Range(1, 10)]; + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + }.RunAsync(); + } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75870")] + public async Task TestArrayPassedToListConstructor() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Linq; + using System.Collections.Generic; + using System.Collections.Immutable; + + class C + { + List GetNumbers() + { + return [|[|new|] List(|]new[] { 1, 2, 3 }); + } + } + """, + FixedCode = """ + using System.Linq; + using System.Collections.Generic; + using System.Collections.Immutable; + + class C + { + List GetNumbers() + { + return [1, 2, 3]; + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + }.RunAsync(); + } +} From 94879b70c78761ea6f45a92300ac341bd2fb6def Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 17:27:24 -0800 Subject: [PATCH 312/508] Fixup --- ...ctionExpressionForNewDiagnosticAnalyzer.cs | 19 ++++++++++++----- ...llectionExpressionForNewCodeFixProvider.cs | 4 ++-- .../UseCollectionExpressionForNewTests.cs | 21 ++++++++++++------- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForNewDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForNewDiagnosticAnalyzer.cs index a2dce85ef7739..a0896323b7fcd 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForNewDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForNewDiagnosticAnalyzer.cs @@ -23,24 +23,33 @@ internal sealed partial class CSharpUseCollectionExpressionForNewDiagnosticAnaly EnforceOnBuildValues.UseCollectionExpressionForNew) { protected override void InitializeWorker(CodeBlockStartAnalysisContext context, INamedTypeSymbol? expressionType) - => context.RegisterSyntaxNodeAction(context => AnalyzeObjectCreationExpression(context, expressionType), SyntaxKind.ObjectCreationExpression); + { + context.RegisterSyntaxNodeAction(context => AnalyzeObjectCreationExpression(context, expressionType), SyntaxKind.ObjectCreationExpression); + context.RegisterSyntaxNodeAction(context => AnalyzeImplicitObjectCreationExpression(context, expressionType), SyntaxKind.ImplicitObjectCreationExpression); + } private void AnalyzeObjectCreationExpression(SyntaxNodeAnalysisContext context, INamedTypeSymbol? expressionType) + => AnalyzeBaseObjectCreationExpression(context, (BaseObjectCreationExpressionSyntax)context.Node, expressionType); + + private void AnalyzeImplicitObjectCreationExpression(SyntaxNodeAnalysisContext context, INamedTypeSymbol? expressionType) + => AnalyzeBaseObjectCreationExpression(context, (BaseObjectCreationExpressionSyntax)context.Node, expressionType); + + private void AnalyzeBaseObjectCreationExpression( + SyntaxNodeAnalysisContext context, BaseObjectCreationExpressionSyntax objectCreationExpression, INamedTypeSymbol? expressionType) { var semanticModel = context.SemanticModel; var compilation = semanticModel.Compilation; var syntaxTree = semanticModel.SyntaxTree; var cancellationToken = context.CancellationToken; + if (objectCreationExpression is not { ArgumentList.Arguments: [var argument], Initializer: null }) + return; + // no point in analyzing if the option is off. var option = context.GetAnalyzerOptions().PreferCollectionExpression; if (option.Value is CollectionExpressionPreference.Never || ShouldSkipAnalysis(context, option.Notification)) return; - var objectCreationExpression = (ObjectCreationExpressionSyntax)context.Node; - if (objectCreationExpression is not { ArgumentList.Arguments: [var argument] }) - return; - var symbol = semanticModel.GetSymbolInfo(objectCreationExpression, cancellationToken).Symbol; if (symbol is not IMethodSymbol { MethodKind: MethodKind.Constructor, Parameters: [var parameter] } || parameter.Type.Name != nameof(IEnumerable)) diff --git a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForNewCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForNewCodeFixProvider.cs index 464ef81fb6a90..4111e3e2db128 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForNewCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForNewCodeFixProvider.cs @@ -26,7 +26,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseCollectionExpression; [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] internal sealed partial class CSharpUseCollectionExpressionForNewCodeFixProvider() - : AbstractUseCollectionExpressionCodeFixProvider( + : AbstractUseCollectionExpressionCodeFixProvider( CSharpCodeFixesResources.Use_collection_expression, IDEDiagnosticIds.UseCollectionExpressionForNewDiagnosticId) { @@ -35,7 +35,7 @@ internal sealed partial class CSharpUseCollectionExpressionForNewCodeFixProvider protected override async Task FixAsync( Document document, SyntaxEditor editor, - ObjectCreationExpressionSyntax objectCreationExpression, + BaseObjectCreationExpressionSyntax objectCreationExpression, ImmutableDictionary properties, CancellationToken cancellationToken) { diff --git a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForNewTests.cs b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForNewTests.cs index 250bee8dfda87..a5fc8fb3683ca 100644 --- a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForNewTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForNewTests.cs @@ -19,12 +19,14 @@ namespace Microsoft.CodeAnalysis.CSharp.Analyzers.UnitTests.UseCollectionExpress [Trait(Traits.Feature, Traits.Features.CodeActionsUseCollectionExpression)] public sealed class UseCollectionExpressionForNewTests { - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75870")] - public async Task TestIEnumerablePassedToListConstructor() + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/75870")] + [InlineData("List")] + [InlineData("")] + public async Task TestIEnumerablePassedToListConstructor(string typeName) { await new VerifyCS.Test { - TestCode = """ + TestCode = $$""" using System.Linq; using System.Collections.Generic; using System.Collections.Immutable; @@ -33,7 +35,7 @@ class C { List GetNumbers() { - return [|[|new|] List(|]Enumerable.Range(1, 10)); + return [|[|new|] {{typeName}}(|]Enumerable.Range(1, 10)); } } """, @@ -54,12 +56,15 @@ List GetNumbers() ReferenceAssemblies = ReferenceAssemblies.Net.Net80, }.RunAsync(); } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75870")] - public async Task TestArrayPassedToListConstructor() + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/75870")] + [InlineData("List")] + [InlineData("")] + public async Task TestArrayPassedToListConstructor(string typeName) { await new VerifyCS.Test { - TestCode = """ + TestCode = $$""" using System.Linq; using System.Collections.Generic; using System.Collections.Immutable; @@ -68,7 +73,7 @@ class C { List GetNumbers() { - return [|[|new|] List(|]new[] { 1, 2, 3 }); + return [|[|new|] {{typeName}}(|]new[] { 1, 2, 3 }); } } """, From 428a4223c7a7c4cf320d2955557cc8febccc581f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 17:32:12 -0800 Subject: [PATCH 313/508] REvert --- .../UseCollectionExpressionHelpers.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs index 5d3e50199e4f7..392599f22780e 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs @@ -1029,15 +1029,14 @@ bool IsCompatibleSignatureAndArguments( { // If we have `CreateRange(IEnumerable values)` this is legal if we have an array, or no-arg object creation. if (arguments.Count == 1 && - originalCreateMethod.Parameters is [var parameter] && - parameter is - { - Type: INamedTypeSymbol + originalCreateMethod.Parameters is [ { - Name: nameof(IEnumerable), - TypeArguments: [ITypeParameterSymbol { TypeParameterKind: TypeParameterKind.Method }] - } enumerableType - } && enumerableType.OriginalDefinition.Equals(compilation.IEnumerableOfTType())) + Type: INamedTypeSymbol + { + Name: nameof(IEnumerable), + TypeArguments: [ITypeParameterSymbol { TypeParameterKind: TypeParameterKind.Method }] + } enumerableType + }] && enumerableType.OriginalDefinition.Equals(compilation.IEnumerableOfTType())) { return IsArgumentCompatibleWithIEnumerableOfT(semanticModel, arguments[0], out unwrapArgument, out useSpread, cancellationToken); } From 05e03df04d415060e92dadef1c8c06d97dacf125 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 17:35:55 -0800 Subject: [PATCH 314/508] check --- .../UseCollectionExpressionHelpers.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs index 392599f22780e..d2445b059bb6e 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs @@ -1028,15 +1028,16 @@ bool IsCompatibleSignatureAndArguments( if (originalCreateMethod.Name is CreateRangeName) { // If we have `CreateRange(IEnumerable values)` this is legal if we have an array, or no-arg object creation. - if (arguments.Count == 1 && - originalCreateMethod.Parameters is [ + if (originalCreateMethod.Parameters is [ + { + Type: INamedTypeSymbol { - Type: INamedTypeSymbol - { - Name: nameof(IEnumerable), - TypeArguments: [ITypeParameterSymbol { TypeParameterKind: TypeParameterKind.Method }] - } enumerableType - }] && enumerableType.OriginalDefinition.Equals(compilation.IEnumerableOfTType())) + Name: nameof(IEnumerable), + TypeArguments: [ITypeParameterSymbol { TypeParameterKind: TypeParameterKind.Method }] + } enumerableType + }] && + enumerableType.OriginalDefinition.Equals(compilation.IEnumerableOfTType()) && + arguments.Count == 1) { return IsArgumentCompatibleWithIEnumerableOfT(semanticModel, arguments[0], out unwrapArgument, out useSpread, cancellationToken); } From 2f1a4d26c1e209d81675c583c6ec7c9b88be1bf1 Mon Sep 17 00:00:00 2001 From: Fred Silberberg Date: Tue, 12 Nov 2024 17:45:31 -0800 Subject: [PATCH 315/508] Don't let inapplicable members participate in ORPA filtering (#75878) Fixes https://github.com/dotnet/roslyn/issues/75871. --- .../Operators/BinaryOperatorAnalysisResult.cs | 1 + .../Operators/UnaryOperatorAnalysisResult.cs | 1 + .../IMemberResolutionResultWithPriority.cs | 1 + .../OverloadResolution/OverloadResolution.cs | 12 +- .../Emit3/OverloadResolutionPriorityTests.cs | 180 ++++++++++++++++++ 5 files changed, 193 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorAnalysisResult.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorAnalysisResult.cs index d96fd06e7a0d4..35d044d2b9714 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorAnalysisResult.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorAnalysisResult.cs @@ -36,6 +36,7 @@ public bool HasValue get { return this.Kind != OperatorAnalysisResultKind.Undefined; } } + bool IMemberResolutionResultWithPriority.IsApplicable => IsValid; MethodSymbol IMemberResolutionResultWithPriority.MemberWithPriority => Signature.Method; public override bool Equals(object obj) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorAnalysisResult.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorAnalysisResult.cs index 46773fa51a037..c5ac83e989313 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorAnalysisResult.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorAnalysisResult.cs @@ -31,6 +31,7 @@ public bool HasValue get { return this.Kind != OperatorAnalysisResultKind.Undefined; } } + bool IMemberResolutionResultWithPriority.IsApplicable => IsValid; MethodSymbol IMemberResolutionResultWithPriority.MemberWithPriority => Signature.Method; public static UnaryOperatorAnalysisResult Applicable(UnaryOperatorSignature signature, Conversion conversion) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/IMemberResolutionResultWithPriority.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/IMemberResolutionResultWithPriority.cs index 747c26b2c5228..6c2176157c6d2 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/IMemberResolutionResultWithPriority.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/IMemberResolutionResultWithPriority.cs @@ -11,4 +11,5 @@ namespace Microsoft.CodeAnalysis.CSharp; internal interface IMemberResolutionResultWithPriority where TMember : Symbol { TMember? MemberWithPriority { get; } + bool IsApplicable { get; } } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs index 48d5f0de526f6..1b0f4d68ed4eb 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs @@ -1860,12 +1860,17 @@ private void RemoveLowerPriorityMembers(ArrayBuilder bool removedMembers = false; var resultsByContainingType = PooledDictionary>.GetInstance(); + var inapplicableMembers = ArrayBuilder.GetInstance(); foreach (var result in results) { - if (result.MemberWithPriority is null) + Debug.Assert(result.MemberWithPriority is not null); + + // We don't filter out inapplicable members here, as we want to keep them in the list for diagnostics + // However, we don't want to take them into account for the priority filtering + if (!result.IsApplicable) { - // Can happen for things like built-in binary operators + inapplicableMembers.Add(result); continue; } @@ -1900,6 +1905,7 @@ private void RemoveLowerPriorityMembers(ArrayBuilder { // No changes, so we can just return resultsByContainingType.Free(); + inapplicableMembers.Free(); return; } @@ -1908,7 +1914,9 @@ private void RemoveLowerPriorityMembers(ArrayBuilder { results.AddRange(resultsForType); } + results.AddRange(inapplicableMembers); resultsByContainingType.Free(); + inapplicableMembers.Free(); } private void RemoveWorseMembers(ArrayBuilder> results, AnalyzedArguments arguments, ref CompoundUseSiteInfo useSiteInfo) diff --git a/src/Compilers/CSharp/Test/Emit3/OverloadResolutionPriorityTests.cs b/src/Compilers/CSharp/Test/Emit3/OverloadResolutionPriorityTests.cs index 1eaf022d7af20..ea94309830cf6 100644 --- a/src/Compilers/CSharp/Test/Emit3/OverloadResolutionPriorityTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/OverloadResolutionPriorityTests.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.CSharp.UnitTests; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; +using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.Test; @@ -2600,4 +2601,183 @@ static void Main() CompileAndVerify([source, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "1234"); } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/75871")] + [InlineData(new[] { 1, 2, 3 })] + [InlineData(new[] { 1, 3, 2 })] + [InlineData(new[] { 2, 1, 3 })] + [InlineData(new[] { 2, 3, 1 })] + [InlineData(new[] { 3, 1, 2 })] + [InlineData(new[] { 3, 2, 1 })] + public void ExtensionsOnlyFilteredByApplicability_01(int[] methodOrder) + { + var e2Methods = ""; + + foreach (var method in methodOrder) + { + e2Methods += method switch + { + 1 => """ + [OverloadResolutionPriority(-1)] + public static void R(this int x) => Console.WriteLine("E2.R(int)"); + """, + 2 => """ + public static void R(this string x) => Console.WriteLine("E2.R(string)"); + """, + 3 => """ + public static void R(this bool o) => Console.WriteLine("E2.R(bool)"); + """, + _ => throw ExceptionUtilities.Unreachable(), + }; + } + + var source = $$""" + using System; + using System.Runtime.CompilerServices; + + internal class Program + { + private static void Main(string[] args) + { + int x = 5; + x.R(); // E1.R(int) + } + } + + public static class E1 + { + public static void R(this int x) => Console.WriteLine("E1.R(int)"); + } + + public static class E2 + { + {{e2Methods}} + } + """; + + var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); + comp.VerifyDiagnostics( + // (9,11): error CS0121: The call is ambiguous between the following methods or properties: 'E1.R(int)' and 'E2.R(int)' + // x.R(); // E1.R(int) + Diagnostic(ErrorCode.ERR_AmbigCall, "R").WithArguments("E1.R(int)", "E2.R(int)").WithLocation(9, 11) + ); + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/75871")] + [InlineData(new[] { 1, 2, 3 })] + [InlineData(new[] { 1, 3, 2 })] + [InlineData(new[] { 2, 1, 3 })] + [InlineData(new[] { 2, 3, 1 })] + [InlineData(new[] { 3, 1, 2 })] + [InlineData(new[] { 3, 2, 1 })] + public void ExtensionsOnlyFilteredByApplicability_02(int[] methodOrder) + { + var e2Methods = ""; + + foreach (var method in methodOrder) + { + e2Methods += method switch + { + 1 => """ + [OverloadResolutionPriority(-1)] + public static void R(this int x) => Console.WriteLine("E2.R(int)"); + """, + 2 => """ + public static void R(this string x) => Console.WriteLine("E2.R(string)"); + """, + 3 => """ + [OverloadResolutionPriority(-1)] + public static void R(this long o) => Console.WriteLine("E2.R(bool)"); + """, + _ => throw ExceptionUtilities.Unreachable(), + }; + } + + var source = $$""" + using System; + using System.Runtime.CompilerServices; + + internal class Program + { + private static void Main(string[] args) + { + int x = 5; + x.R(); // E1.R(int) + } + } + + public static class E1 + { + public static void R(this int x) => Console.WriteLine("E1.R(int)"); + } + + public static class E2 + { + {{e2Methods}} + } + """; + + var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); + comp.VerifyDiagnostics( + // (9,11): error CS0121: The call is ambiguous between the following methods or properties: 'E1.R(int)' and 'E2.R(int)' + // x.R(); // E1.R(int) + Diagnostic(ErrorCode.ERR_AmbigCall, "R").WithArguments("E1.R(int)", "E2.R(int)").WithLocation(9, 11) + ); + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/75871")] + [InlineData(new[] { 1, 2, 3 })] + [InlineData(new[] { 1, 3, 2 })] + [InlineData(new[] { 2, 1, 3 })] + [InlineData(new[] { 2, 3, 1 })] + [InlineData(new[] { 3, 1, 2 })] + [InlineData(new[] { 3, 2, 1 })] + public void ExtensionsOnlyFilteredByApplicability_03(int[] methodOrder) + { + var e2Methods = ""; + + foreach (var method in methodOrder) + { + e2Methods += method switch + { + 1 => """ + [OverloadResolutionPriority(-1)] + public static void R(this int x) => Console.WriteLine("E2.R(int)"); + """, + 2 => """ + public static void R(this string x) => Console.WriteLine("E2.R(string)"); + """, + 3 => """ + public static void R(this object o) => Console.WriteLine("E2.R(object)"); + """, + _ => throw ExceptionUtilities.Unreachable(), + }; + } + + var source = $$""" + using System; + using System.Runtime.CompilerServices; + + internal class Program + { + private static void Main(string[] args) + { + int x = 5; + x.R(); // E1.R(int) + } + } + + public static class E1 + { + public static void R(this int x) => Console.WriteLine("E1.R(int)"); + } + + public static class E2 + { + {{e2Methods}} + } + """; + + CompileAndVerify([source, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "E1.R(int)").VerifyDiagnostics(); + } } From 305f868d01da8b295e25395cd8ffdaca375f5b9b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 18:08:05 -0800 Subject: [PATCH 316/508] lint --- .../UseCollectionExpressionHelpers.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs index d2445b059bb6e..e3f25377f485f 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs @@ -1076,13 +1076,13 @@ bool IsCompatibleSignatureAndArguments( if (arguments.Count == 1 && compilation.SupportsRuntimeCapability(RuntimeCapability.InlineArrayTypes) && originalCreateMethod.Parameters is [ - { - Type: INamedTypeSymbol { - Name: nameof(Span) or nameof(ReadOnlySpan), - TypeArguments: [ITypeParameterSymbol { TypeParameterKind: TypeParameterKind.Method }] - } spanType - }]) + Type: INamedTypeSymbol + { + Name: nameof(Span) or nameof(ReadOnlySpan), + TypeArguments: [ITypeParameterSymbol { TypeParameterKind: TypeParameterKind.Method }] + } spanType + }]) { if (spanType.OriginalDefinition.Equals(compilation.SpanOfTType()) || spanType.OriginalDefinition.Equals(compilation.ReadOnlySpanOfTType())) From 8a94cdbd75d4b9670cd66fffe496793c909792a5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 21:17:49 -0800 Subject: [PATCH 317/508] Update test --- .../Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs b/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs index fb37145993f67..04c391b5490ac 100644 --- a/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs @@ -478,6 +478,9 @@ public void CSharp_VerifyIDEDiagnosticSeveritiesAreConfigurable() # IDE0305 dotnet_diagnostic.IDE0305.severity = %value% + + # IDE0306 + dotnet_diagnostic.IDE0306.severity = %value% # IDE0320 dotnet_diagnostic.IDE0320.severity = %value% @@ -898,6 +901,7 @@ public void CSharp_VerifyIDECodeStyleOptionsAreConfigurable() ("IDE0303", "dotnet_style_prefer_collection_expression", "when_types_loosely_match"), ("IDE0304", "dotnet_style_prefer_collection_expression", "when_types_loosely_match"), ("IDE0305", "dotnet_style_prefer_collection_expression", "when_types_loosely_match"), + ("IDE0306", "dotnet_style_prefer_collection_expression", "when_types_loosely_match"), ("IDE0320", "csharp_prefer_static_anonymous_function", "true"), ("IDE0330", "csharp_prefer_system_threading_lock", "true"), ("IDE1005", "csharp_style_conditional_delegate_call", "true"), From 580377b60f784a502df2505100409d29c9def538 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 21:21:01 -0800 Subject: [PATCH 318/508] Update test --- src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs b/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs index e17ddcc4dd94c..aebbe79ee16bc 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs @@ -627,8 +627,8 @@ private void Method() } [Theory] - [InlineData(LanguageNames.CSharp, 50)] - [InlineData(LanguageNames.VisualBasic, 87)] + [InlineData(LanguageNames.CSharp, 51)] + [InlineData(LanguageNames.VisualBasic, 88)] public void VerifyAllCodeStyleFixersAreSupportedByCodeCleanup(string language, int numberOfUnsupportedDiagnosticIds) { var supportedDiagnostics = GetSupportedDiagnosticIdsForCodeCleanupService(language); From 24fc1f350fa5bbd1cf33581541f34a466d33a00f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Nov 2024 21:51:11 -0800 Subject: [PATCH 319/508] Update src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems --- src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems b/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems index 4151a269c71ff..4930c8dd698b1 100644 --- a/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems +++ b/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems @@ -115,7 +115,7 @@ - + From 1cf8c57320f97dda07e28cb454ba80dc7c252ed7 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 13 Nov 2024 17:03:50 +0100 Subject: [PATCH 320/508] [main] Update dependencies from dotnet/source-build-reference-packages (#75867) * Update dependencies from https://github.com/dotnet/source-build-reference-packages build 20241111.2 Microsoft.SourceBuild.Intermediate.source-build-reference-packages From Version 10.0.0-alpha.1.24558.1 -> To Version 10.0.0-alpha.1.24561.2 * Failed to perform coherency update for one or more dependencies. --------- Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 68bf8a5f05609..98a4ac410992b 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -8,9 +8,9 @@ - + https://github.com/dotnet/source-build-reference-packages - 1ebd9ce245112164207d961c0d2faea741c7c489 + 92a51d1379daa1fa7892a9d06840ba833fcd6298 From 78759a281f53b34ac213588778db7cb3aa14cb29 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 13 Nov 2024 09:54:35 -0800 Subject: [PATCH 321/508] Switch to primary constructor --- .../New.IntegrationTests/CSharp/CSharpCodeActions.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs index 08bd9fc336185..039163c07229a 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs @@ -25,13 +25,8 @@ namespace Roslyn.VisualStudio.NewIntegrationTests.CSharp; -public class CSharpCodeActions : AbstractEditorTest +public sealed class CSharpCodeActions() : AbstractEditorTest(nameof(CSharpCodeActions)) { - public CSharpCodeActions() - : base(nameof(CSharpCodeActions)) - { - } - protected override string LanguageName => LanguageNames.CSharp; [IdeFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)] From 8abea8b76b6aa5c17303507bfbacf8d2d9175549 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 13 Nov 2024 09:55:47 -0800 Subject: [PATCH 322/508] Switch to raw strings --- .../CSharp/CSharpCodeActions.cs | 1315 +++++++++-------- 1 file changed, 680 insertions(+), 635 deletions(-) diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs index 039163c07229a..e3b7b6dee553e 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs @@ -33,38 +33,38 @@ public sealed class CSharpCodeActions() : AbstractEditorTest(nameof(CSharpCodeAc public async Task GenerateMethodInClosedFile() { var project = ProjectName; - await TestServices.SolutionExplorer.AddFileAsync(project, "Foo.cs", contents: @" -public class Foo -{ -} -", cancellationToken: HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.AddFileAsync(project, "Foo.cs", contents: """ + public class Foo + { + } + """, cancellationToken: HangMitigatingCancellationToken); - await SetUpEditorAsync(@" -using System; + await SetUpEditorAsync(""" + using System; -public class Program -{ - public static void Main(string[] args) - { - Foo f = new Foo(); - f.Bar()$$ - } -} -", HangMitigatingCancellationToken); + public class Program + { + public static void Main(string[] args) + { + Foo f = new Foo(); + f.Bar()$$ + } + } + """, HangMitigatingCancellationToken); await TestServices.Editor.InvokeCodeActionListAsync(HangMitigatingCancellationToken); await TestServices.EditorVerifier.CodeActionAsync("Generate method 'Bar'", applyFix: true, cancellationToken: HangMitigatingCancellationToken); - await TestServices.SolutionVerifier.FileContentsAsync(project, "Foo.cs", @" -using System; + await TestServices.SolutionVerifier.FileContentsAsync(project, "Foo.cs", """ + using System; -public class Foo -{ - internal void Bar() - { - throw new NotImplementedException(); - } -} -", HangMitigatingCancellationToken); + public class Foo + { + internal void Bar() + { + throw new NotImplementedException(); + } + } + """, HangMitigatingCancellationToken); } [IdeFact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)] @@ -72,12 +72,12 @@ public async Task AddUsingOnIncompleteMember() { // Need to ensure that incomplete member diagnostics run at high pri so that add-using can be // triggered by them. - await SetUpEditorAsync(@" -class Program -{ - DateTime$$ -} -", HangMitigatingCancellationToken); + await SetUpEditorAsync(""" + class Program + { + DateTime$$ + } + """, HangMitigatingCancellationToken); await TestServices.Editor.InvokeCodeActionListAsync(HangMitigatingCancellationToken); await TestServices.EditorVerifier.CodeActionAsync("using System;", cancellationToken: HangMitigatingCancellationToken); } @@ -90,15 +90,15 @@ public async Task FastDoubleInvoke() // to get it to invoke without any sort of waiting to happen. This helps address a bug // we had where our asynchronous smart tags interfered with asynchrony in VS, which caused // the second smart tag to not expand if you tried invoking it too quickly - await SetUpEditorAsync(@" -class Program -{ - static void Main(string[] args) - { - Exception $$ex = new System.ArgumentException(); - } -} -", HangMitigatingCancellationToken); + await SetUpEditorAsync(""" + class Program + { + static void Main(string[] args) + { + Exception $$ex = new System.ArgumentException(); + } + } + """, HangMitigatingCancellationToken); // Suspend file change notification during code action application, since spurious file change notifications // can cause silent failure to apply the code action if they occur within this block. @@ -117,44 +117,46 @@ static void Main(string[] args) } await TestServices.EditorVerifier.TextContainsAsync( - @" -using System; + """ + using System; -class Program -{ - static void Main(string[] args) - { - Exception ex = new ArgumentException(); - } -}", cancellationToken: HangMitigatingCancellationToken); + class Program + { + static void Main(string[] args) + { + Exception ex = new ArgumentException(); + } + } + """, cancellationToken: HangMitigatingCancellationToken); } [IdeFact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)] public async Task InvokeDelegateWithConditionalAccessMultipleTimes() { - var markup = @" -using System; -class C -{ - public event EventHandler First; - public event EventHandler Second; - void RaiseFirst() - { - var temp1 = First; - if (temp1 != null) - { - temp1$$(this, EventArgs.Empty); - } - } - void RaiseSecond() - { - var temp2 = Second; - if (temp2 != null) - { - temp2(this, EventArgs.Empty); - } - } -}"; + var markup = """ + using System; + class C + { + public event EventHandler First; + public event EventHandler Second; + void RaiseFirst() + { + var temp1 = First; + if (temp1 != null) + { + temp1$$(this, EventArgs.Empty); + } + } + void RaiseSecond() + { + var temp2 = Second; + if (temp2 != null) + { + temp2(this, EventArgs.Empty); + } + } + } + """; MarkupTestFile.GetSpans(markup, out _, out var _); await SetUpEditorAsync(markup, HangMitigatingCancellationToken); @@ -174,40 +176,42 @@ void RaiseSecond() [WorkItem("https://github.com/dotnet/roslyn/issues/19089")] public async Task ApplyEditorConfigAndFixAllOccurrences() { - var markup = @" -class C -{ - public int X1 - { - get - { - $$return 3; - } - } - - public int Y1 => 5; - - public int X2 - { - get - { - return 3; - } - } - - public int Y2 => 5; -}"; - var expectedText = @" -class C -{ - public int X1 => 3; + var markup = """ + class C + { + public int X1 + { + get + { + $$return 3; + } + } + + public int Y1 => 5; + + public int X2 + { + get + { + return 3; + } + } + + public int Y2 => 5; + } + """; + var expectedText = """ + class C + { + public int X1 => 3; - public int Y1 => 5; + public int Y1 => 5; - public int X2 => 3; + public int X2 => 3; - public int Y2 => 5; -}"; + public int Y2 => 5; + } + """; await TestServices.SolutionExplorer.OpenFileAsync(ProjectName, "Class1.cs", HangMitigatingCancellationToken); @@ -229,11 +233,12 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( HangMitigatingCancellationToken); await TestServices.EditorVerifier.CodeActionsNotShowingAsync(HangMitigatingCancellationToken); - var editorConfig = @"root = true + var editorConfig = """ + root = true -[*.cs] -csharp_style_expression_bodied_properties = true:warning -"; + [*.cs] + csharp_style_expression_bodied_properties = true:warning + """; await TestServices.SolutionExplorer.AddFileAsync(ProjectName, ".editorconfig", editorConfig, open: false, HangMitigatingCancellationToken); @@ -278,41 +283,42 @@ await TestServices.EditorVerifier.CodeActionAsync( fixAllScope: FixAllScope.Project, cancellationToken: HangMitigatingCancellationToken); - expectedText = @" -class C -{ - public int X1 - { - get - { - return 3; - } - } - - public int Y1 - { - get - { - return 5; - } - } - - public int X2 - { - get - { - return 3; - } - } - - public int Y2 - { - get - { - return 5; - } - } -}"; + expectedText = """ + class C + { + public int X1 + { + get + { + return 3; + } + } + + public int Y1 + { + get + { + return 5; + } + } + + public int X2 + { + get + { + return 3; + } + } + + public int Y2 + { + get + { + return 5; + } + } + } + """; AssertEx.EqualOrDiff(expectedText, await TestServices.Editor.GetTextAsync(HangMitigatingCancellationToken)); } @@ -324,36 +330,42 @@ public int Y2 [WorkItem("https://github.com/dotnet/roslyn/issues/33507")] public async Task FixAllOccurrencesIgnoresGeneratedCode(FixAllScope scope) { - var markup = @" -using System; -using $$System.Threading; + var markup = """ + using System; + using $$System.Threading; -class C -{ - public IntPtr X1 { get; set; } -}"; - var expectedText = @" -using System; + class C + { + public IntPtr X1 { get; set; } + } + """; + var expectedText = """ + using System; -class C -{ - public IntPtr X1 { get; set; } -}"; - var generatedSourceMarkup = @"// -using System; -using $$System.Threading; + class C + { + public IntPtr X1 { get; set; } + } + """; + var generatedSourceMarkup = """ + // + using System; + using $$System.Threading; -class D -{ - public IntPtr X1 { get; set; } -}"; - var expectedGeneratedSource = @"// -using System; + class D + { + public IntPtr X1 { get; set; } + } + """; + var expectedGeneratedSource = """ + // + using System; -class D -{ - public IntPtr X1 { get; set; } -}"; + class D + { + public IntPtr X1 { get; set; } + } + """; MarkupTestFile.GetPosition(generatedSourceMarkup, out var generatedSource, out int generatedSourcePosition); @@ -411,29 +423,33 @@ await TestServices.EditorVerifier.CodeActionAsync( [WorkItem("https://github.com/dotnet/roslyn/issues/33507")] public async Task FixAllOccurrencesTriggeredFromGeneratedCode(FixAllScope scope) { - var markup = @"// -using System; -using $$System.Threading; + var markup = """ + // + using System; + using $$System.Threading; -class C -{ - public IntPtr X1 { get; set; } -}"; - var secondFile = @" -using System; -using System.Threading; + class C + { + public IntPtr X1 { get; set; } + } + """; + var secondFile = """ + using System; + using System.Threading; -class D -{ - public IntPtr X1 { get; set; } -}"; - var expectedSecondFile = @" -using System; + class D + { + public IntPtr X1 { get; set; } + } + """; + var expectedSecondFile = """ + using System; -class D -{ - public IntPtr X1 { get; set; } -}"; + class D + { + public IntPtr X1 { get; set; } + } + """; await TestServices.SolutionExplorer.AddFileAsync(ProjectName, "D.cs", secondFile, open: false, HangMitigatingCancellationToken); @@ -462,14 +478,15 @@ await TestServices.EditorVerifier.CodeActionAsync( [IdeFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)] public async Task ClassificationInPreviewPane() { - await SetUpEditorAsync(@" -class Program -{ - int Main() - { - Foo$$(); - } -}", HangMitigatingCancellationToken); + await SetUpEditorAsync(""" + class Program + { + int Main() + { + Foo$$(); + } + } + """, HangMitigatingCancellationToken); await TestServices.Editor.InvokeCodeActionListAsync(HangMitigatingCancellationToken); var classifiedTokens = await TestServices.Editor.GetLightBulbPreviewClassificationsAsync("Generate method 'Foo'", HangMitigatingCancellationToken); Assert.True(classifiedTokens.Any(c => c.Span.GetText().ToString() == "void" && c.ClassificationType.Classification == "keyword")); @@ -478,16 +495,17 @@ int Main() [IdeFact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)] public async Task AddUsingExactMatchBeforeRenameTracking() { - await SetUpEditorAsync(@" -public class Program -{ - static void Main(string[] args) - { - P2$$ p; - } -} + await SetUpEditorAsync(""" + public class Program + { + static void Main(string[] args) + { + P2$$ p; + } + } -public class P2 { }", HangMitigatingCancellationToken); + public class P2 { } + """, HangMitigatingCancellationToken); await TestServices.Input.SendAsync([VirtualKeyCode.BACK, VirtualKeyCode.BACK, "Stream"], HangMitigatingCancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync( @@ -538,22 +556,23 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( [IdeFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateType)] public async Task GFUFuzzyMatchAfterRenameTrackingAndAfterGenerateType() { - await SetUpEditorAsync(@" -namespace N -{ - class Goober { } -} + await SetUpEditorAsync(""" + namespace N + { + class Goober { } + } -namespace NS -{ - public class P2 - { - static void Main(string[] args) - { - P2$$ p; - } - } -}", HangMitigatingCancellationToken); + namespace NS + { + public class P2 + { + static void Main(string[] args) + { + P2$$ p; + } + } + } + """, HangMitigatingCancellationToken); await TestServices.Input.SendAsync([VirtualKeyCode.BACK, VirtualKeyCode.BACK, "Foober"], HangMitigatingCancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync( [ @@ -594,18 +613,19 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( [IdeFact, Trait(Traits.Feature, Traits.Features.CodeGeneration)] public async Task SuppressionAfterRefactorings() { - await SetUpEditorAsync(@" -[System.Obsolete] -class C -{ -} -class Program -{ - static void Main(string[] args) - { - C p = $$2; - } -}", HangMitigatingCancellationToken); + await SetUpEditorAsync(""" + [System.Obsolete] + class C + { + } + class Program + { + static void Main(string[] args) + { + C p = $$2; + } + } + """, HangMitigatingCancellationToken); await TestServices.Editor.SelectTextInCurrentDocumentAsync("2", HangMitigatingCancellationToken); await TestServices.Editor.InvokeCodeActionListAsync(HangMitigatingCancellationToken); @@ -637,16 +657,17 @@ static void Main(string[] args) [IdeFact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)] public async Task OrderFixesByCursorProximityLeft() { - await SetUpEditorAsync(@" -using System; -public class Program -{ - static void Main(string[] args) - { - Byte[] bytes = null; - GCHandle$$ handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); - } -}", HangMitigatingCancellationToken); + await SetUpEditorAsync(""" + using System; + public class Program + { + static void Main(string[] args) + { + Byte[] bytes = null; + GCHandle$$ handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); + } + } + """, HangMitigatingCancellationToken); await TestServices.Editor.InvokeCodeActionListAsync(HangMitigatingCancellationToken); var expectedItems = new[] { @@ -661,16 +682,17 @@ static void Main(string[] args) [IdeFact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)] public async Task OrderFixesByCursorProximityRight() { - await SetUpEditorAsync(@" -using System; -public class Program -{ - static void Main(string[] args) - { - Byte[] bytes = null; - GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.$$Pinned); - } -}", HangMitigatingCancellationToken); + await SetUpEditorAsync(""" + using System; + public class Program + { + static void Main(string[] args) + { + Byte[] bytes = null; + GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.$$Pinned); + } + } + """, HangMitigatingCancellationToken); await TestServices.Editor.InvokeCodeActionListAsync(HangMitigatingCancellationToken); var expectedItems = new[] { @@ -685,15 +707,16 @@ static void Main(string[] args) [IdeFact, Trait(Traits.Feature, Traits.Features.CodeActionsConfiguration)] public async Task ConfigureCodeStyleOptionValueAndSeverity() { - await SetUpEditorAsync(@" -using System; -public class Program -{ - static void Main(string[] args) - { - var $$x = new Program(); - } -}", HangMitigatingCancellationToken); + await SetUpEditorAsync(""" + using System; + public class Program + { + static void Main(string[] args) + { + var $$x = new Program(); + } + } + """, HangMitigatingCancellationToken); await TestServices.Editor.InvokeCodeActionListAsync(HangMitigatingCancellationToken); await TestServices.EditorVerifier.CodeActionsAsync([ @@ -709,15 +732,16 @@ await TestServices.EditorVerifier.CodeActionsAsync([ [WorkItem("https://github.com/dotnet/roslyn/issues/46784")] public async Task ConfigureSeverity() { - var markup = @" -class C -{ - public static void Main() - { - // CS0168: The variable 'x' is declared but never used - int $$x; - } -}"; + var markup = """ + class C + { + public static void Main() + { + // CS0168: The variable 'x' is declared but never used + int $$x; + } + } + """; await SetUpEditorAsync(markup, HangMitigatingCancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync( @@ -797,15 +821,16 @@ static async Task VerifyDiagnosticInErrorListAsync(string expectedSeverity, Test [WorkItem("https://github.com/dotnet/roslyn/issues/46784")] public async Task ConfigureSeverityWithManualEditsToEditorconfig() { - var markup = @" -class C -{ - public static void Main() - { - // CS0168: The variable 'x' is declared but never used - int $$x; - } -}"; + var markup = """ + class C + { + public static void Main() + { + // CS0168: The variable 'x' is declared but never used + int $$x; + } + } + """; await SetUpEditorAsync(markup, HangMitigatingCancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync( @@ -822,9 +847,10 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( // Add an .editorconfig file to the project to change severity to error. await TestServices.SolutionExplorer.AddFileAsync(ProjectName, ".editorconfig", open: true, cancellationToken: HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(@" -[*.cs] -dotnet_diagnostic.CS0168.severity = ", HangMitigatingCancellationToken); + await TestServices.Input.SendAsync(""" + [*.cs] + dotnet_diagnostic.CS0168.severity = + """, HangMitigatingCancellationToken); // NOTE: Below wait is a critical step in repro-ing the original regression. await TestServices.Workspace.WaitForAllAsyncOperationsAsync( @@ -879,27 +905,29 @@ static async Task VerifyDiagnosticInErrorListAsync(string expectedSeverity, Test [InlineData(BackgroundAnalysisScope.FullSolution, CompilerDiagnosticsScope.FullSolution)] internal async Task ConfigureSeverityWithManualEditsToEditorconfig_CurrentDocumentScope(BackgroundAnalysisScope analyzerScope, CompilerDiagnosticsScope compilerScope) { - var markup1 = @" -class C -{ - public static void Main() - { - // CS0219: The variable 'x' is assigned but its value is never used - // IDE0059: Unnecessary assignment of a value to 'x' - int x = 0; - } -}"; + var markup1 = """ + class C + { + public static void Main() + { + // CS0219: The variable 'x' is assigned but its value is never used + // IDE0059: Unnecessary assignment of a value to 'x' + int x = 0; + } + } + """; - var markup2 = @" -class C2 -{ - public static void M() - { - // CS0219: The variable 'y' is assigned but its value is never used - // IDE0059: Unnecessary assignment of a value to 'y' - int $$y = 0; - } -}"; + var markup2 = """ + class C2 + { + public static void M() + { + // CS0219: The variable 'y' is assigned but its value is never used + // IDE0059: Unnecessary assignment of a value to 'y' + int $$y = 0; + } + } + """; await TestServices.Workspace.SetBackgroundAnalysisOptionsAsync(analyzerScope, compilerScope, HangMitigatingCancellationToken); await SetUpEditorAsync(markup2, HangMitigatingCancellationToken); @@ -929,10 +957,11 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( // Add an .editorconfig file to the project to change severities to error. await TestServices.SolutionExplorer.AddFileAsync(ProjectName, ".editorconfig", open: true, cancellationToken: HangMitigatingCancellationToken); - await TestServices.Editor.SetTextAsync(@" -[*.cs] -dotnet_diagnostic.CS0219.severity = error -dotnet_diagnostic.IDE0059.severity = error", HangMitigatingCancellationToken); + await TestServices.Editor.SetTextAsync(""" + [*.cs] + dotnet_diagnostic.CS0219.severity = error + dotnet_diagnostic.IDE0059.severity = error + """, HangMitigatingCancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync( [ @@ -947,10 +976,11 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( await VerifyDiagnosticsInErrorListAsync("error", "error", TestServices, HangMitigatingCancellationToken); // Edit editorconfig file to disable both compiler and analyzer diagnostics. - await TestServices.Editor.SetTextAsync(@" -[*.cs] -dotnet_diagnostic.CS0219.severity = none -dotnet_diagnostic.IDE0059.severity = none", HangMitigatingCancellationToken); + await TestServices.Editor.SetTextAsync(""" + [*.cs] + dotnet_diagnostic.CS0219.severity = none + dotnet_diagnostic.IDE0059.severity = none + """, HangMitigatingCancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync( [ @@ -1005,64 +1035,66 @@ static async Task VerifyDiagnosticsInErrorListAsync(string expectedCompilerDiagn [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] public async Task TestFixAllOccurrences_CodeFix_ContainingMember() { - var markup = @" -class Program1 -{ - static void Main() - { - $$if (true) if (true) return; + var markup = """ + class Program1 + { + static void Main() + { + $$if (true) if (true) return; - if (false) if (false) return; - } + if (false) if (false) return; + } - void OtherMethod() - { - if (true) if (true) return; - } -} + void OtherMethod() + { + if (true) if (true) return; + } + } -class OtherType -{ - void OtherMethod() - { - if (true) if (true) return; - } -}"; - var expectedText = @" -class Program1 -{ - static void Main() - { - if (true) - { - if (true) + class OtherType { - return; + void OtherMethod() + { + if (true) if (true) return; + } } - } - - if (false) - { - if (false) + """; + var expectedText = """ + class Program1 { - return; + static void Main() + { + if (true) + { + if (true) + { + return; + } + } + + if (false) + { + if (false) + { + return; + } + } + } + + void OtherMethod() + { + if (true) if (true) return; + } } - } - } - - void OtherMethod() - { - if (true) if (true) return; - } -} -class OtherType -{ - void OtherMethod() - { - if (true) if (true) return; - } -}"; + class OtherType + { + void OtherMethod() + { + if (true) if (true) return; + } + } + """; await TestServices.SolutionExplorer.OpenFileAsync(ProjectName, "Class1.cs", HangMitigatingCancellationToken); @@ -1092,141 +1124,145 @@ await TestServices.EditorVerifier.CodeActionAsync( [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] public async Task TestFixAllOccurrences_CodeFix_ContainingType() { - var markup1 = @" -partial class Program1 -{ - static void Main() - { - $$if (true) if (true) return; - - if (false) if (false) return; - } + var markup1 = """ + partial class Program1 + { + static void Main() + { + $$if (true) if (true) return; - void M1() - { - if (true) if (true) return; - } -} + if (false) if (false) return; + } -class OtherType1 -{ - void OtherMethod() - { - if (true) if (true) return; - } -} + void M1() + { + if (true) if (true) return; + } + } -partial class Program1 -{ - void M2() - { - if (true) if (true) return; - } -}"; - var expectedText1 = @" -partial class Program1 -{ - static void Main() - { - if (true) - { - if (true) + class OtherType1 { - return; + void OtherMethod() + { + if (true) if (true) return; + } } - } - if (false) - { - if (false) + partial class Program1 { - return; + void M2() + { + if (true) if (true) return; + } } - } - } - - void M1() - { - if (true) - { - if (true) + """; + var expectedText1 = """ + partial class Program1 { - return; + static void Main() + { + if (true) + { + if (true) + { + return; + } + } + + if (false) + { + if (false) + { + return; + } + } + } + + void M1() + { + if (true) + { + if (true) + { + return; + } + } + } } - } - } -} -class OtherType1 -{ - void OtherMethod() - { - if (true) if (true) return; - } -} + class OtherType1 + { + void OtherMethod() + { + if (true) if (true) return; + } + } -partial class Program1 -{ - void M2() - { - if (true) - { - if (true) + partial class Program1 { - return; + void M2() + { + if (true) + { + if (true) + { + return; + } + } + } } - } - } -}"; + """; - var markup2 = @" -partial class Program1 -{ - void OtherFileMethod() - { - if (true) if (true) return; + var markup2 = """ + partial class Program1 + { + void OtherFileMethod() + { + if (true) if (true) return; - if (false) if (false) return; - } -} + if (false) if (false) return; + } + } -class OtherType2 -{ - void OtherMethod() - { - if (true) if (true) return; - } -}"; - var expectedText2 = @" -partial class Program1 -{ - void OtherFileMethod() - { - if (true) - { - if (true) + class OtherType2 { - return; + void OtherMethod() + { + if (true) if (true) return; + } } - } - - if (false) - { - if (false) + """; + var expectedText2 = """ + partial class Program1 { - return; + void OtherFileMethod() + { + if (true) + { + if (true) + { + return; + } + } + + if (false) + { + if (false) + { + return; + } + } + } } - } - } -} -class OtherType2 -{ - void OtherMethod() - { - if (true) if (true) return; - } -}"; + class OtherType2 + { + void OtherMethod() + { + if (true) if (true) return; + } + } + """; await TestServices.SolutionExplorer.AddFileAsync(ProjectName, "Class2.cs", markup2, cancellationToken: HangMitigatingCancellationToken); await TestServices.SolutionExplorer.OpenFileAsync(ProjectName, "Class1.cs", HangMitigatingCancellationToken); @@ -1259,54 +1295,56 @@ await TestServices.EditorVerifier.CodeActionAsync( [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] public async Task TestFixAllOccurrences_CodeRefactoring_ContainingMember() { - var markup = @" -class C1 -{ - void M() - { - var singleLine1 = $$""a""; - var singleLine2 = @""goo""""bar""; - } - - void M2() - { - var singleLine1 = ""a""; - var singleLine2 = @""goo""""bar""; - } -} - -class C2 -{ - void M3() - { - var singleLine1 = ""a""; - var singleLine2 = @""goo""""bar""; - } -}"; - var expectedText = @" -class C1 -{ - void M() - { - var singleLine1 = """"""a""""""; - var singleLine2 = """"""goo""bar""""""; - } + var markup = """ + class C1 + { + void M() + { + var singleLine1 = $$"a"; + var singleLine2 = @"goo""bar"; + } + + void M2() + { + var singleLine1 = "a"; + var singleLine2 = @"goo""bar"; + } + } - void M2() - { - var singleLine1 = ""a""; - var singleLine2 = @""goo""""bar""; - } -} + class C2 + { + void M3() + { + var singleLine1 = "a"; + var singleLine2 = @"goo""bar"; + } + } + """; + var expectedText = """" + class C1 + { + void M() + { + var singleLine1 = """a"""; + var singleLine2 = """goo"bar"""; + } + + void M2() + { + var singleLine1 = "a"; + var singleLine2 = @"goo""bar"; + } + } -class C2 -{ - void M3() - { - var singleLine1 = ""a""; - var singleLine2 = @""goo""""bar""; - } -}"; + class C2 + { + void M3() + { + var singleLine1 = "a"; + var singleLine2 = @"goo""bar"; + } + } + """"; await TestServices.SolutionExplorer.OpenFileAsync(ProjectName, "Class1.cs", HangMitigatingCancellationToken); @@ -1336,109 +1374,113 @@ await TestServices.EditorVerifier.CodeActionAsync( [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] public async Task TestFixAllOccurrences_CodeRefactoring_ContainingType() { - var markup1 = @" -partial class C1 -{ - void M() - { - var singleLine1 = $$""a""; - var singleLine2 = @""goo""""bar""; - } - - void M2() - { - var singleLine1 = ""a""; - var singleLine2 = @""goo""""bar""; - } -} - -class C2 -{ - void M3() - { - var singleLine1 = ""a""; - var singleLine2 = @""goo""""bar""; - } -} + var markup1 = """ + partial class C1 + { + void M() + { + var singleLine1 = $$"a"; + var singleLine2 = @"goo""bar"; + } + + void M2() + { + var singleLine1 = "a"; + var singleLine2 = @"goo""bar"; + } + } -partial class C1 -{ - void M4() - { - var singleLine1 = ""a""; - var singleLine2 = @""goo""""bar""; - } -}"; - var expectedText1 = @" -partial class C1 -{ - void M() - { - var singleLine1 = """"""a""""""; - var singleLine2 = """"""goo""bar""""""; - } + class C2 + { + void M3() + { + var singleLine1 = "a"; + var singleLine2 = @"goo""bar"; + } + } - void M2() - { - var singleLine1 = """"""a""""""; - var singleLine2 = """"""goo""bar""""""; - } -} + partial class C1 + { + void M4() + { + var singleLine1 = "a"; + var singleLine2 = @"goo""bar"; + } + } + """; + var expectedText1 = """" + partial class C1 + { + void M() + { + var singleLine1 = """a"""; + var singleLine2 = """goo"bar"""; + } + + void M2() + { + var singleLine1 = """a"""; + var singleLine2 = """goo"bar"""; + } + } -class C2 -{ - void M3() - { - var singleLine1 = ""a""; - var singleLine2 = @""goo""""bar""; - } -} + class C2 + { + void M3() + { + var singleLine1 = "a"; + var singleLine2 = @"goo""bar"; + } + } -partial class C1 -{ - void M4() - { - var singleLine1 = """"""a""""""; - var singleLine2 = """"""goo""bar""""""; - } -}"; + partial class C1 + { + void M4() + { + var singleLine1 = """a"""; + var singleLine2 = """goo"bar"""; + } + } + """"; - var markup2 = @" -partial class C1 -{ - void M5() - { - var singleLine1 = ""a""; - var singleLine2 = @""goo""""bar""; - } -} + var markup2 = """ + partial class C1 + { + void M5() + { + var singleLine1 = "a"; + var singleLine2 = @"goo""bar"; + } + } -class C2 -{ - void M6() - { - var singleLine1 = ""a""; - var singleLine2 = @""goo""""bar""; - } -}"; - var expectedText2 = @" -partial class C1 -{ - void M5() - { - var singleLine1 = """"""a""""""; - var singleLine2 = """"""goo""bar""""""; - } -} + class C2 + { + void M6() + { + var singleLine1 = "a"; + var singleLine2 = @"goo""bar"; + } + } + """; + var expectedText2 = """" + partial class C1 + { + void M5() + { + var singleLine1 = """a"""; + var singleLine2 = """goo"bar"""; + } + } -class C2 -{ - void M6() - { - var singleLine1 = ""a""; - var singleLine2 = @""goo""""bar""; - } -}"; + class C2 + { + void M6() + { + var singleLine1 = "a"; + var singleLine2 = @"goo""bar"; + } + } + """"; await TestServices.SolutionExplorer.AddFileAsync(ProjectName, "Class2.cs", markup2, cancellationToken: HangMitigatingCancellationToken); await TestServices.SolutionExplorer.OpenFileAsync(ProjectName, "Class1.cs", HangMitigatingCancellationToken); @@ -1471,15 +1513,15 @@ await TestServices.EditorVerifier.CodeActionAsync( [WorkItem("https://github.com/dotnet/roslyn/issues/61334")] public async Task UseExpressionBodyBeforeExtractBaseClass() { - await SetUpEditorAsync(@" -public class Program -{ - $$public void M() - { - System.Console.WriteLine(0); - } -} -", HangMitigatingCancellationToken); + await SetUpEditorAsync(""" + public class Program + { + $$public void M() + { + System.Console.WriteLine(0); + } + } + """, HangMitigatingCancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync( [ @@ -1512,8 +1554,10 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( public async Task TestNonSourceDocumentRefactoring() { var markup = @"$$# Editorconfig File"; - var expectedText = @"# Editorconfig File -# Refactored"; + var expectedText = """ + # Editorconfig File + # Refactored + """; await TestServices.SolutionExplorer.OpenFileAsync(ProjectName, "Class1.cs", HangMitigatingCancellationToken); await TestServices.SolutionExplorer.AddAnalyzerReferenceAsync(ProjectName, typeof(NonSourceFileRefactoring).Assembly.Location, HangMitigatingCancellationToken); @@ -1543,14 +1587,15 @@ await TestServices.EditorVerifier.CodeActionAsync( [IdeFact, Trait(Traits.Feature, Traits.Features.CodeGeneration)] public async Task TestRefactoringsAreSortedByPriority() { - var codeFormat = @" -#pragma warning disable IDE0060 // Remove unused parameter -class C -{ - public C(int x1, int x2, int x3) - { - } -};"; + var codeFormat = """ + #pragma warning disable IDE0060 // Remove unused parameter + class C + { + public C(int x1, int x2, int x3) + { + } + }; + """; for (var i = 1; i <= 3; i++) { var code = codeFormat.Replace($"x{i}", $"$$x{i}"); From 6121e9b4e74c7a0789065f383dbf697c7311017f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 13 Nov 2024 10:09:26 -0800 Subject: [PATCH 323/508] Fix test --- .../CSharp/CSharpCodeActions.cs | 31 ++----------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs index e3b7b6dee553e..f2cc6b6731668 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs @@ -328,6 +328,7 @@ public int Y2 [InlineData(FixAllScope.Solution)] [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] [WorkItem("https://github.com/dotnet/roslyn/issues/33507")] + [WorkItem("https://github.com/dotnet/roslyn/issues/74761")] public async Task FixAllOccurrencesIgnoresGeneratedCode(FixAllScope scope) { var markup = """ @@ -352,15 +353,6 @@ class C using System; using $$System.Threading; - class D - { - public IntPtr X1 { get; set; } - } - """; - var expectedGeneratedSource = """ - // - using System; - class D { public IntPtr X1 { get; set; } @@ -394,26 +386,7 @@ await TestServices.EditorVerifier.CodeActionAsync( // The current behavior is observable; any change to this behavior should be part of an intentional design // change. await TestServices.Editor.MoveCaretAsync(generatedSourcePosition, HangMitigatingCancellationToken); - await TestServices.Editor.InvokeCodeActionListAsync(HangMitigatingCancellationToken); - await TestServices.EditorVerifier.CodeActionAsync( - "Remove unnecessary usings", - applyFix: true, - fixAllScope: FixAllScope.Document, - cancellationToken: HangMitigatingCancellationToken); - - AssertEx.EqualOrDiff(generatedSource, await TestServices.Editor.GetTextAsync(HangMitigatingCancellationToken)); - - // Verify that the code action can still be applied manually from within the generated file. - // This is a regression test for correctness with respect to the design. - await TestServices.Editor.MoveCaretAsync(generatedSourcePosition, HangMitigatingCancellationToken); - await TestServices.Editor.InvokeCodeActionListAsync(HangMitigatingCancellationToken); - await TestServices.EditorVerifier.CodeActionAsync( - "Remove unnecessary usings", - applyFix: true, - fixAllScope: null, - cancellationToken: HangMitigatingCancellationToken); - - AssertEx.EqualOrDiff(expectedGeneratedSource, await TestServices.Editor.GetTextAsync(HangMitigatingCancellationToken)); + await TestServices.EditorVerifier.CodeActionsNotShowingAsync(HangMitigatingCancellationToken); } [CriticalIdeTheory] From 3afc07b87f467c1a053d0934dd5f2f1d671ec54f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 13 Nov 2024 10:22:42 -0800 Subject: [PATCH 324/508] Fix test --- .../CSharp/CSharpCodeActions.cs | 24 ++++--------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs index f2cc6b6731668..e7ea9428208d1 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs @@ -389,12 +389,11 @@ await TestServices.EditorVerifier.CodeActionAsync( await TestServices.EditorVerifier.CodeActionsNotShowingAsync(HangMitigatingCancellationToken); } - [CriticalIdeTheory] - [InlineData(FixAllScope.Project)] - [InlineData(FixAllScope.Solution)] + [IdeFact] [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] [WorkItem("https://github.com/dotnet/roslyn/issues/33507")] - public async Task FixAllOccurrencesTriggeredFromGeneratedCode(FixAllScope scope) + [WorkItem("https://github.com/dotnet/roslyn/issues/74761")] + public async Task FixAllOccurrencesNotTriggeredFromGeneratedCode() { var markup = """ // @@ -410,14 +409,6 @@ class C using System; using System.Threading; - class D - { - public IntPtr X1 { get; set; } - } - """; - var expectedSecondFile = """ - using System; - class D { public IntPtr X1 { get; set; } @@ -435,17 +426,12 @@ class D // change. MarkupTestFile.GetPosition(markup, out var expectedText, out int _); await SetUpEditorAsync(markup, HangMitigatingCancellationToken); - await TestServices.Editor.InvokeCodeActionListAsync(HangMitigatingCancellationToken); - await TestServices.EditorVerifier.CodeActionAsync( - "Remove unnecessary usings", - applyFix: true, - fixAllScope: scope, - cancellationToken: HangMitigatingCancellationToken); + await TestServices.EditorVerifier.CodeActionsNotShowingAsync(HangMitigatingCancellationToken); AssertEx.EqualOrDiff(expectedText, await TestServices.Editor.GetTextAsync(HangMitigatingCancellationToken)); await TestServices.SolutionExplorer.OpenFileAsync(ProjectName, "D.cs", HangMitigatingCancellationToken); - AssertEx.EqualOrDiff(expectedSecondFile, await TestServices.Editor.GetTextAsync(HangMitigatingCancellationToken)); + AssertEx.EqualOrDiff(secondFile, await TestServices.Editor.GetTextAsync(HangMitigatingCancellationToken)); } [IdeFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)] From 18a8664b4ad3b14eda57672219f331e0512edbc7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 13 Nov 2024 10:29:30 -0800 Subject: [PATCH 325/508] Add missing case --- src/Features/RulesMissingDocumentation.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Features/RulesMissingDocumentation.md b/src/Features/RulesMissingDocumentation.md index ad42d72a548d3..68c85896685b2 100644 --- a/src/Features/RulesMissingDocumentation.md +++ b/src/Features/RulesMissingDocumentation.md @@ -11,6 +11,7 @@ IDE0302 | | Simplify collection initialization | IDE0304 | | Simplify collection initialization | IDE0305 | | Simplify collection initialization | +IDE0306 | | Simplify collection initialization | IDE0320 | | Make anonymous function static | IDE0330 | | Use 'System.Threading.Lock' | IDE1007 | | | From 6952887849f54e4821e74a5f188f8d12a1e3ae3c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 13 Nov 2024 11:16:04 -0800 Subject: [PATCH 326/508] IN progress --- .../CSharpUseCollectionInitializerAnalyzer.cs | 242 ++++++++++-------- ...CollectionInitializerDiagnosticAnalyzer.cs | 22 +- ...zerCodeFixProvider_CollectionExpression.cs | 4 +- ...onInitializerTests_CollectionExpression.cs | 22 ++ ...CollectionInitializerDiagnosticAnalyzer.cs | 16 +- ...CollectionInitializerDiagnosticAnalyzer.vb | 11 +- 6 files changed, 197 insertions(+), 120 deletions(-) diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerAnalyzer.cs index fabc39b46613b..1e141c4520f8f 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerAnalyzer.cs @@ -7,6 +7,7 @@ using System.Threading; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.UseCollectionExpression; @@ -50,150 +51,167 @@ protected override bool AnalyzeMatchesAndCollectionConstructorForCollectionExpre ArrayBuilder> postMatches, CancellationToken cancellationToken) { - // Constructor wasn't called with any arguments. Nothing to validate. - var argumentList = _objectCreationExpression.ArgumentList; - if (argumentList is null || argumentList.Arguments.Count == 0) - return true; - - // Anything beyond just a single capacity argument (or single value to populate the collection with) isn't - // anything we can handle. - if (argumentList.Arguments.Count != 1) + if (!AnalyzeConstructor()) return false; - if (this.SemanticModel.GetSymbolInfo(_objectCreationExpression, cancellationToken).Symbol is not IMethodSymbol - { - MethodKind: MethodKind.Constructor, - Parameters.Length: 1, - } constructor) + // Any initializer values should be added to the collection expr, after all the values directly passed to the + // constructor, and before any values added by the statements that followed the object creation expression. + var initializer = _objectCreationExpression.Initializer; + if (initializer != null) { - return false; + foreach (var expression in initializer.Expressions) + preMatches.Add(new(expression, UseSpread: false)); } - var ienumerableOfTType = this.SemanticModel.Compilation.IEnumerableOfTType(); - var firstParameter = constructor.Parameters[0]; - if (Equals(firstParameter.Type.OriginalDefinition, ienumerableOfTType) || - firstParameter.Type.AllInterfaces.Any(i => Equals(i.OriginalDefinition, ienumerableOfTType))) - { - // Took a single argument that implements IEnumerable. We handle this by spreading that argument as the - // first thing added to the collection. - preMatches.Add(new(argumentList.Arguments[0].Expression, UseSpread: true)); - return true; - } - else if (firstParameter is { Type.SpecialType: SpecialType.System_Int32, Name: "capacity" }) + return true; + + bool AnalyzeConstructor() { - // is a single `int capacity` constructor. + // If constructor wasn't called with any arguments, then there's nothing to validate. + var argumentList = _objectCreationExpression.ArgumentList; + if (argumentList is null || argumentList.Arguments.Count == 0) + return true; + + // Anything beyond just a single capacity argument (or single value to populate the collection with) isn't + // anything we can handle. + if (argumentList.Arguments.Count != 1) + return false; - // The original collection could have been passed elements explicitly in its initializer. Ensure we account for - // that as well. - var individualElementCount = _objectCreationExpression.Initializer?.Expressions.Count ?? 0; + if (this.SemanticModel.GetSymbolInfo(_objectCreationExpression, cancellationToken).Symbol is not IMethodSymbol + { + MethodKind: MethodKind.Constructor, + Parameters.Length: 1, + } constructor) + { + return false; + } - // Walk the matches, determining what individual elements are added as-is, as well as what values are going to - // be spread into the final collection. We'll then ensure a correspondance between both and the expression the - // user is currently passing to the 'capacity' argument to make sure they're entirely congruent. - using var _1 = ArrayBuilder.GetInstance(out var spreadElements); - foreach (var match in postMatches) + var ienumerableOfTType = this.SemanticModel.Compilation.IEnumerableOfTType(); + var firstParameter = constructor.Parameters[0]; + if (Equals(firstParameter.Type.OriginalDefinition, ienumerableOfTType) || + firstParameter.Type.AllInterfaces.Any(i => Equals(i.OriginalDefinition, ienumerableOfTType))) { - switch (match.Node) - { - case ExpressionStatementSyntax { Expression: InvocationExpressionSyntax invocation } expressionStatement: - // x.AddRange(y). Have to make sure we see y.Count in the capacity list. - // x.Add(y, z). Increment the total number of elements by the arg count. - if (match.UseSpread) - spreadElements.Add(invocation.ArgumentList.Arguments[0].Expression); - else - individualElementCount += invocation.ArgumentList.Arguments.Count; - - continue; - - case ForEachStatementSyntax foreachStatement: - // foreach (var v in expr) x.Add(v). Have to make sure we see expr.Count in the capacity list. - spreadElements.Add(foreachStatement.Expression); - continue; - - default: - // Something we don't support (yet). - return false; - } + // Took a single argument that implements IEnumerable. We handle this by spreading that argument as the + // first thing added to the collection. + preMatches.Add(new(argumentList.Arguments[0].Expression, UseSpread: true)); + return true; } + else if (firstParameter is { Type.SpecialType: SpecialType.System_Int32, Name: "capacity" }) + { + // is a single `int capacity` constructor. - // Now, break up an expression like `1 + x.Length + y.Count` into the parts separated by the +'s - var currentArgumentExpression = argumentList.Arguments[0].Expression; - using var _2 = ArrayBuilder.GetInstance(out var expressionPieces); + // The original collection could have been passed elements explicitly in its initializer. Ensure we account for + // that as well. + var individualElementCount = _objectCreationExpression.Initializer?.Expressions.Count ?? 0; - while (true) - { - if (currentArgumentExpression is BinaryExpressionSyntax binaryExpression) + // Walk the matches, determining what individual elements are added as-is, as well as what values are going to + // be spread into the final collection. We'll then ensure a correspondance between both and the expression the + // user is currently passing to the 'capacity' argument to make sure they're entirely congruent. + using var _1 = ArrayBuilder.GetInstance(out var spreadElements); + foreach (var match in postMatches) { - if (binaryExpression.Kind() != SyntaxKind.AddExpression) - return false; - - expressionPieces.Add(binaryExpression.Right); - currentArgumentExpression = binaryExpression.Left; + switch (match.Node) + { + case ExpressionStatementSyntax { Expression: InvocationExpressionSyntax invocation } expressionStatement: + // x.AddRange(y). Have to make sure we see y.Count in the capacity list. + // x.Add(y, z). Increment the total number of elements by the arg count. + if (match.UseSpread) + spreadElements.Add(invocation.ArgumentList.Arguments[0].Expression); + else + individualElementCount += invocation.ArgumentList.Arguments.Count; + + continue; + + case ForEachStatementSyntax foreachStatement: + // foreach (var v in expr) x.Add(v). Have to make sure we see expr.Count in the capacity list. + spreadElements.Add(foreachStatement.Expression); + continue; + + default: + // Something we don't support (yet). + return false; + } } - else + + // Now, break up an expression like `1 + x.Length + y.Count` into the parts separated by the +'s + var currentArgumentExpression = argumentList.Arguments[0].Expression; + using var _2 = ArrayBuilder.GetInstance(out var expressionPieces); + + while (true) { - expressionPieces.Add(currentArgumentExpression); - break; + if (currentArgumentExpression is BinaryExpressionSyntax binaryExpression) + { + if (binaryExpression.Kind() != SyntaxKind.AddExpression) + return false; + + expressionPieces.Add(binaryExpression.Right); + currentArgumentExpression = binaryExpression.Left; + } + else + { + expressionPieces.Add(currentArgumentExpression); + break; + } } - } - // Determine the total constant value provided in the expression. For each constant we see, remove that - // constant from the pieces list. That way the pieces list only corresponds to the values to spread. - var totalConstantValue = 0; - for (var i = expressionPieces.Count - 1; i >= 0; i--) - { - var piece = expressionPieces[i]; - var constant = this.SemanticModel.GetConstantValue(piece, cancellationToken); - if (constant.Value is int value) + // Determine the total constant value provided in the expression. For each constant we see, remove that + // constant from the pieces list. That way the pieces list only corresponds to the values to spread. + var totalConstantValue = 0; + for (var i = expressionPieces.Count - 1; i >= 0; i--) { - totalConstantValue += value; - expressionPieces.RemoveAt(i); + var piece = expressionPieces[i]; + var constant = this.SemanticModel.GetConstantValue(piece, cancellationToken); + if (constant.Value is int value) + { + totalConstantValue += value; + expressionPieces.RemoveAt(i); + } } - } - // If the constant didn't match the number of individual elements to add, we can't update this code. - if (totalConstantValue != individualElementCount) - return false; + // If the constant didn't match the number of individual elements to add, we can't update this code. + if (totalConstantValue != individualElementCount) + return false; - // After removing the constants, we should have an expression for each value we're going to spread. - if (expressionPieces.Count != spreadElements.Count) - return false; + // After removing the constants, we should have an expression for each value we're going to spread. + if (expressionPieces.Count != spreadElements.Count) + return false; - // Now make sure we have a match for each part of `x.Length + y.Length` to an element being spread - // into the collection. - foreach (var piece in expressionPieces) - { - // we support x.Length, x.Count, and x.Count() - var current = piece; - if (piece is InvocationExpressionSyntax invocationExpression) + // Now make sure we have a match for each part of `x.Length + y.Length` to an element being spread + // into the collection. + foreach (var piece in expressionPieces) { - if (invocationExpression.ArgumentList.Arguments.Count != 0) + // we support x.Length, x.Count, and x.Count() + var current = piece; + if (piece is InvocationExpressionSyntax invocationExpression) + { + if (invocationExpression.ArgumentList.Arguments.Count != 0) + return false; + + current = invocationExpression.Expression; + } + + if (current is not MemberAccessExpressionSyntax(SyntaxKind.SimpleMemberAccessExpression) { Name.Identifier.ValueText: "Length" or "Count" } memberAccess) return false; - current = invocationExpression.Expression; - } + current = memberAccess.Expression; - if (current is not MemberAccessExpressionSyntax(SyntaxKind.SimpleMemberAccessExpression) { Name.Identifier.ValueText: "Length" or "Count" } memberAccess) - return false; + // Now see if we have an item we're spreading matching 'x'. + var matchIndex = spreadElements.FindIndex(SyntaxFacts.AreEquivalent, current); + if (matchIndex < 0) + return false; - current = memberAccess.Expression; + spreadElements.RemoveAt(matchIndex); + } - // Now see if we have an item we're spreading matching 'x'. - var matchIndex = spreadElements.FindIndex(SyntaxFacts.AreEquivalent, current); - if (matchIndex < 0) + // If we had any spread elements remaining we can't proceed. + if (spreadElements.Count > 0) return false; - spreadElements.RemoveAt(matchIndex); + // We're all good. The items we found matches up precisely to the capacity provided! + return true; } - // If we had any spread elements remaining we can't proceed. - if (spreadElements.Count > 0) - return false; - - // We're all good. The items we found matches up precisely to the capacity provided! - return true; + return false; } - - return false; } } diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerDiagnosticAnalyzer.cs index 69b943c5b43f5..666dc2a65ff09 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerDiagnosticAnalyzer.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Immutable; +using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.LanguageService; @@ -10,10 +12,13 @@ using Microsoft.CodeAnalysis.CSharp.UseCollectionExpression; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.UseCollectionExpression; using Microsoft.CodeAnalysis.UseCollectionInitializer; namespace Microsoft.CodeAnalysis.CSharp.UseCollectionInitializer; +using static SyntaxFactory; + [DiagnosticAnalyzer(LanguageNames.CSharp)] internal sealed class CSharpUseCollectionInitializerDiagnosticAnalyzer : AbstractUseCollectionInitializerDiagnosticAnalyzer< @@ -40,13 +45,26 @@ protected override bool AreCollectionInitializersSupported(Compilation compilati protected override bool AreCollectionExpressionsSupported(Compilation compilation) => compilation.LanguageVersion().SupportsCollectionExpressions(); - protected override bool CanUseCollectionExpression(SemanticModel semanticModel, BaseObjectCreationExpressionSyntax objectCreationExpression, INamedTypeSymbol? expressionType, bool allowSemanticsChange, CancellationToken cancellationToken, out bool changesSemantics) + protected override bool CanUseCollectionExpression( + SemanticModel semanticModel, + BaseObjectCreationExpressionSyntax objectCreationExpression, + INamedTypeSymbol? expressionType, + ImmutableArray> matches, + bool allowSemanticsChange, + CancellationToken cancellationToken, + out bool changesSemantics) { // Synthesize the final collection expression we would replace this object-creation with. That will allow us to // determine if we end up calling the right overload in cases of overloaded methods. - var replacement = UseCollectionExpressionHelpers.CreateReplacementCollectionExpressionForAnalysis(objectCreationExpression.Initializer); + var replacement = CollectionExpression(SeparatedList(matches.Where(m => m.Node is ExpressionSyntax).Select(CreateElement))); return UseCollectionExpressionHelpers.CanReplaceWithCollectionExpression( semanticModel, objectCreationExpression, replacement, expressionType, isSingletonInstance: false, allowSemanticsChange, skipVerificationForReplacedNode: true, cancellationToken, out changesSemantics); + + static CollectionElementSyntax CreateElement(CollectionMatch match) + { + var expression = (ExpressionSyntax)match.Node; + return match.UseSpread ? SpreadElement(expression) : ExpressionElement(expression); + } } } diff --git a/src/Analyzers/CSharp/CodeFixes/UseCollectionInitializer/CSharpUseCollectionInitializerCodeFixProvider_CollectionExpression.cs b/src/Analyzers/CSharp/CodeFixes/UseCollectionInitializer/CSharpUseCollectionInitializerCodeFixProvider_CollectionExpression.cs index 849f1568fbcfd..e0993b94c6cd1 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseCollectionInitializer/CSharpUseCollectionInitializerCodeFixProvider_CollectionExpression.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseCollectionInitializer/CSharpUseCollectionInitializerCodeFixProvider_CollectionExpression.cs @@ -30,7 +30,9 @@ private static Task CreateCollectionExpressionAsync( objectCreation, preMatches, postMatches, - static objectCreation => objectCreation.Initializer, + // We don't return any initializer in the case of converting a `new List { 1, 2, 3 }` to a collection + // expr. This is because `1, 2, 3` will be in the preMatches instead. + getInitializer: static _ => null, static (objectCreation, initializer) => objectCreation.WithInitializer(initializer), cancellationToken); } diff --git a/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests_CollectionExpression.cs b/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests_CollectionExpression.cs index 7a19fa362e961..3d5ad523fa0a8 100644 --- a/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests_CollectionExpression.cs +++ b/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests_CollectionExpression.cs @@ -5866,4 +5866,26 @@ public class Class2 { } ReferenceAssemblies = ReferenceAssemblies.Net.Net80, }.RunAsync(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75894")] + public async Task TestNotOnDictionaryConstructor() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + class C + { + void Main() + { + Dictionary a = null; + Dictionary d = new(a); + } + } + """, + LanguageVersion = LanguageVersion.CSharp13, + ReferenceAssemblies = ReferenceAssemblies.Net.Net90, + }.RunAsync(); + } } diff --git a/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractUseCollectionInitializerDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractUseCollectionInitializerDiagnosticAnalyzer.cs index 6b84494d3acfc..b443ab3adb98b 100644 --- a/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractUseCollectionInitializerDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractUseCollectionInitializerDiagnosticAnalyzer.cs @@ -80,7 +80,13 @@ protected AbstractUseCollectionInitializerDiagnosticAnalyzer() protected abstract bool AreCollectionInitializersSupported(Compilation compilation); protected abstract bool AreCollectionExpressionsSupported(Compilation compilation); protected abstract bool CanUseCollectionExpression( - SemanticModel semanticModel, TObjectCreationExpressionSyntax objectCreationExpression, INamedTypeSymbol? expressionType, bool allowSemanticsChange, CancellationToken cancellationToken, out bool changesSemantics); + SemanticModel semanticModel, + TObjectCreationExpressionSyntax objectCreationExpression, + INamedTypeSymbol? expressionType, + ImmutableArray> matches, + bool allowSemanticsChange, + CancellationToken cancellationToken, + out bool changesSemantics); protected abstract TAnalyzer GetAnalyzer(); @@ -218,15 +224,17 @@ private void AnalyzeNode( if (!this.AreCollectionExpressionsSupported(context.Compilation)) return null; - var (_, matches) = analyzer.Analyze(semanticModel, syntaxFacts, objectCreationExpression, analyzeForCollectionExpression: true, cancellationToken); + var (preMatches, postMatches) = analyzer.Analyze(semanticModel, syntaxFacts, objectCreationExpression, analyzeForCollectionExpression: true, cancellationToken); // If analysis failed, we can't change this, no matter what. - if (matches.IsDefault) + if (preMatches.IsDefault || postMatches.IsDefault) return null; + var matches = preMatches.Concat(postMatches); + // Check if it would actually be legal to use a collection expression here though. var allowSemanticsChange = preferExpressionOption.Value == CollectionExpressionPreference.WhenTypesLooselyMatch; - if (!CanUseCollectionExpression(semanticModel, objectCreationExpression, expressionType, allowSemanticsChange, cancellationToken, out var changesSemantics)) + if (!CanUseCollectionExpression(semanticModel, objectCreationExpression, expressionType, matches, allowSemanticsChange, cancellationToken, out var changesSemantics)) return null; return (matches, shouldUseCollectionExpression: true, changesSemantics); diff --git a/src/Analyzers/VisualBasic/Analyzers/UseCollectionInitializer/VisualBasicUseCollectionInitializerDiagnosticAnalyzer.vb b/src/Analyzers/VisualBasic/Analyzers/UseCollectionInitializer/VisualBasicUseCollectionInitializerDiagnosticAnalyzer.vb index b15c4bf2bd948..f8227d27903ee 100644 --- a/src/Analyzers/VisualBasic/Analyzers/UseCollectionInitializer/VisualBasicUseCollectionInitializerDiagnosticAnalyzer.vb +++ b/src/Analyzers/VisualBasic/Analyzers/UseCollectionInitializer/VisualBasicUseCollectionInitializerDiagnosticAnalyzer.vb @@ -2,9 +2,11 @@ ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. +Imports System.Collections.Immutable Imports System.Threading Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.LanguageService +Imports Microsoft.CodeAnalysis.UseCollectionExpression Imports Microsoft.CodeAnalysis.UseCollectionInitializer Imports Microsoft.CodeAnalysis.VisualBasic.LanguageService Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -38,7 +40,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UseCollectionInitializer Return False End Function - Protected Overrides Function CanUseCollectionExpression(semanticModel As SemanticModel, objectCreationExpression As ObjectCreationExpressionSyntax, expressionType As INamedTypeSymbol, allowSemanticsChange As Boolean, cancellationToken As CancellationToken, ByRef changesSemantics As Boolean) As Boolean + Protected Overrides Function CanUseCollectionExpression( + semanticModel As SemanticModel, + objectCreationExpression As ObjectCreationExpressionSyntax, + expressionType As INamedTypeSymbol, + matches As ImmutableArray(Of CollectionMatch(Of SyntaxNode)), + allowSemanticsChange As Boolean, + cancellationToken As CancellationToken, + ByRef changesSemantics As Boolean) As Boolean Throw ExceptionUtilities.Unreachable() End Function End Class From f0afe6e1069d4c865929245022f5e485329f8ef0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 13 Nov 2024 11:20:04 -0800 Subject: [PATCH 327/508] Pass along prematches --- .../CSharpUseCollectionInitializerAnalyzer.cs | 242 ++++++++---------- ...CollectionInitializerDiagnosticAnalyzer.cs | 18 +- ...zerCodeFixProvider_CollectionExpression.cs | 4 +- ...CollectionInitializerDiagnosticAnalyzer.cs | 8 +- 4 files changed, 132 insertions(+), 140 deletions(-) diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerAnalyzer.cs index 1e141c4520f8f..0e912909ab92f 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerAnalyzer.cs @@ -7,7 +7,6 @@ using System.Threading; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.UseCollectionExpression; @@ -51,167 +50,150 @@ protected override bool AnalyzeMatchesAndCollectionConstructorForCollectionExpre ArrayBuilder> postMatches, CancellationToken cancellationToken) { - if (!AnalyzeConstructor()) + // If constructor wasn't called with any arguments, then there's nothing to validate. + var argumentList = _objectCreationExpression.ArgumentList; + if (argumentList is null || argumentList.Arguments.Count == 0) + return true; + + // Anything beyond just a single capacity argument (or single value to populate the collection with) isn't + // anything we can handle. + if (argumentList.Arguments.Count != 1) return false; - // Any initializer values should be added to the collection expr, after all the values directly passed to the - // constructor, and before any values added by the statements that followed the object creation expression. - var initializer = _objectCreationExpression.Initializer; - if (initializer != null) + if (this.SemanticModel.GetSymbolInfo(_objectCreationExpression, cancellationToken).Symbol is not IMethodSymbol + { + MethodKind: MethodKind.Constructor, + Parameters.Length: 1, + } constructor) { - foreach (var expression in initializer.Expressions) - preMatches.Add(new(expression, UseSpread: false)); + return false; } - return true; - - bool AnalyzeConstructor() + var ienumerableOfTType = this.SemanticModel.Compilation.IEnumerableOfTType(); + var firstParameter = constructor.Parameters[0]; + if (Equals(firstParameter.Type.OriginalDefinition, ienumerableOfTType) || + firstParameter.Type.AllInterfaces.Any(i => Equals(i.OriginalDefinition, ienumerableOfTType))) { - // If constructor wasn't called with any arguments, then there's nothing to validate. - var argumentList = _objectCreationExpression.ArgumentList; - if (argumentList is null || argumentList.Arguments.Count == 0) - return true; - - // Anything beyond just a single capacity argument (or single value to populate the collection with) isn't - // anything we can handle. - if (argumentList.Arguments.Count != 1) - return false; + // Took a single argument that implements IEnumerable. We handle this by spreading that argument as the + // first thing added to the collection. + preMatches.Add(new(argumentList.Arguments[0].Expression, UseSpread: true)); + return true; + } + else if (firstParameter is { Type.SpecialType: SpecialType.System_Int32, Name: "capacity" }) + { + // is a single `int capacity` constructor. - if (this.SemanticModel.GetSymbolInfo(_objectCreationExpression, cancellationToken).Symbol is not IMethodSymbol - { - MethodKind: MethodKind.Constructor, - Parameters.Length: 1, - } constructor) - { - return false; - } + // The original collection could have been passed elements explicitly in its initializer. Ensure we account for + // that as well. + var individualElementCount = _objectCreationExpression.Initializer?.Expressions.Count ?? 0; - var ienumerableOfTType = this.SemanticModel.Compilation.IEnumerableOfTType(); - var firstParameter = constructor.Parameters[0]; - if (Equals(firstParameter.Type.OriginalDefinition, ienumerableOfTType) || - firstParameter.Type.AllInterfaces.Any(i => Equals(i.OriginalDefinition, ienumerableOfTType))) + // Walk the matches, determining what individual elements are added as-is, as well as what values are going to + // be spread into the final collection. We'll then ensure a correspondance between both and the expression the + // user is currently passing to the 'capacity' argument to make sure they're entirely congruent. + using var _1 = ArrayBuilder.GetInstance(out var spreadElements); + foreach (var match in postMatches) { - // Took a single argument that implements IEnumerable. We handle this by spreading that argument as the - // first thing added to the collection. - preMatches.Add(new(argumentList.Arguments[0].Expression, UseSpread: true)); - return true; + switch (match.Node) + { + case ExpressionStatementSyntax { Expression: InvocationExpressionSyntax invocation } expressionStatement: + // x.AddRange(y). Have to make sure we see y.Count in the capacity list. + // x.Add(y, z). Increment the total number of elements by the arg count. + if (match.UseSpread) + spreadElements.Add(invocation.ArgumentList.Arguments[0].Expression); + else + individualElementCount += invocation.ArgumentList.Arguments.Count; + + continue; + + case ForEachStatementSyntax foreachStatement: + // foreach (var v in expr) x.Add(v). Have to make sure we see expr.Count in the capacity list. + spreadElements.Add(foreachStatement.Expression); + continue; + + default: + // Something we don't support (yet). + return false; + } } - else if (firstParameter is { Type.SpecialType: SpecialType.System_Int32, Name: "capacity" }) - { - // is a single `int capacity` constructor. - // The original collection could have been passed elements explicitly in its initializer. Ensure we account for - // that as well. - var individualElementCount = _objectCreationExpression.Initializer?.Expressions.Count ?? 0; + // Now, break up an expression like `1 + x.Length + y.Count` into the parts separated by the +'s + var currentArgumentExpression = argumentList.Arguments[0].Expression; + using var _2 = ArrayBuilder.GetInstance(out var expressionPieces); - // Walk the matches, determining what individual elements are added as-is, as well as what values are going to - // be spread into the final collection. We'll then ensure a correspondance between both and the expression the - // user is currently passing to the 'capacity' argument to make sure they're entirely congruent. - using var _1 = ArrayBuilder.GetInstance(out var spreadElements); - foreach (var match in postMatches) + while (true) + { + if (currentArgumentExpression is BinaryExpressionSyntax binaryExpression) { - switch (match.Node) - { - case ExpressionStatementSyntax { Expression: InvocationExpressionSyntax invocation } expressionStatement: - // x.AddRange(y). Have to make sure we see y.Count in the capacity list. - // x.Add(y, z). Increment the total number of elements by the arg count. - if (match.UseSpread) - spreadElements.Add(invocation.ArgumentList.Arguments[0].Expression); - else - individualElementCount += invocation.ArgumentList.Arguments.Count; - - continue; - - case ForEachStatementSyntax foreachStatement: - // foreach (var v in expr) x.Add(v). Have to make sure we see expr.Count in the capacity list. - spreadElements.Add(foreachStatement.Expression); - continue; - - default: - // Something we don't support (yet). - return false; - } - } - - // Now, break up an expression like `1 + x.Length + y.Count` into the parts separated by the +'s - var currentArgumentExpression = argumentList.Arguments[0].Expression; - using var _2 = ArrayBuilder.GetInstance(out var expressionPieces); + if (binaryExpression.Kind() != SyntaxKind.AddExpression) + return false; - while (true) + expressionPieces.Add(binaryExpression.Right); + currentArgumentExpression = binaryExpression.Left; + } + else { - if (currentArgumentExpression is BinaryExpressionSyntax binaryExpression) - { - if (binaryExpression.Kind() != SyntaxKind.AddExpression) - return false; - - expressionPieces.Add(binaryExpression.Right); - currentArgumentExpression = binaryExpression.Left; - } - else - { - expressionPieces.Add(currentArgumentExpression); - break; - } + expressionPieces.Add(currentArgumentExpression); + break; } + } - // Determine the total constant value provided in the expression. For each constant we see, remove that - // constant from the pieces list. That way the pieces list only corresponds to the values to spread. - var totalConstantValue = 0; - for (var i = expressionPieces.Count - 1; i >= 0; i--) + // Determine the total constant value provided in the expression. For each constant we see, remove that + // constant from the pieces list. That way the pieces list only corresponds to the values to spread. + var totalConstantValue = 0; + for (var i = expressionPieces.Count - 1; i >= 0; i--) + { + var piece = expressionPieces[i]; + var constant = this.SemanticModel.GetConstantValue(piece, cancellationToken); + if (constant.Value is int value) { - var piece = expressionPieces[i]; - var constant = this.SemanticModel.GetConstantValue(piece, cancellationToken); - if (constant.Value is int value) - { - totalConstantValue += value; - expressionPieces.RemoveAt(i); - } + totalConstantValue += value; + expressionPieces.RemoveAt(i); } + } - // If the constant didn't match the number of individual elements to add, we can't update this code. - if (totalConstantValue != individualElementCount) - return false; + // If the constant didn't match the number of individual elements to add, we can't update this code. + if (totalConstantValue != individualElementCount) + return false; - // After removing the constants, we should have an expression for each value we're going to spread. - if (expressionPieces.Count != spreadElements.Count) - return false; + // After removing the constants, we should have an expression for each value we're going to spread. + if (expressionPieces.Count != spreadElements.Count) + return false; - // Now make sure we have a match for each part of `x.Length + y.Length` to an element being spread - // into the collection. - foreach (var piece in expressionPieces) + // Now make sure we have a match for each part of `x.Length + y.Length` to an element being spread + // into the collection. + foreach (var piece in expressionPieces) + { + // we support x.Length, x.Count, and x.Count() + var current = piece; + if (piece is InvocationExpressionSyntax invocationExpression) { - // we support x.Length, x.Count, and x.Count() - var current = piece; - if (piece is InvocationExpressionSyntax invocationExpression) - { - if (invocationExpression.ArgumentList.Arguments.Count != 0) - return false; - - current = invocationExpression.Expression; - } - - if (current is not MemberAccessExpressionSyntax(SyntaxKind.SimpleMemberAccessExpression) { Name.Identifier.ValueText: "Length" or "Count" } memberAccess) + if (invocationExpression.ArgumentList.Arguments.Count != 0) return false; - current = memberAccess.Expression; + current = invocationExpression.Expression; + } - // Now see if we have an item we're spreading matching 'x'. - var matchIndex = spreadElements.FindIndex(SyntaxFacts.AreEquivalent, current); - if (matchIndex < 0) - return false; + if (current is not MemberAccessExpressionSyntax(SyntaxKind.SimpleMemberAccessExpression) { Name.Identifier.ValueText: "Length" or "Count" } memberAccess) + return false; - spreadElements.RemoveAt(matchIndex); - } + current = memberAccess.Expression; - // If we had any spread elements remaining we can't proceed. - if (spreadElements.Count > 0) + // Now see if we have an item we're spreading matching 'x'. + var matchIndex = spreadElements.FindIndex(SyntaxFacts.AreEquivalent, current); + if (matchIndex < 0) return false; - // We're all good. The items we found matches up precisely to the capacity provided! - return true; + spreadElements.RemoveAt(matchIndex); } - return false; + // If we had any spread elements remaining we can't proceed. + if (spreadElements.Count > 0) + return false; + + // We're all good. The items we found matches up precisely to the capacity provided! + return true; } + + return false; } } diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerDiagnosticAnalyzer.cs index 666dc2a65ff09..954962a44908d 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerDiagnosticAnalyzer.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; @@ -49,14 +51,17 @@ protected override bool CanUseCollectionExpression( SemanticModel semanticModel, BaseObjectCreationExpressionSyntax objectCreationExpression, INamedTypeSymbol? expressionType, - ImmutableArray> matches, + ImmutableArray> preMatches, bool allowSemanticsChange, CancellationToken cancellationToken, out bool changesSemantics) { // Synthesize the final collection expression we would replace this object-creation with. That will allow us to // determine if we end up calling the right overload in cases of overloaded methods. - var replacement = CollectionExpression(SeparatedList(matches.Where(m => m.Node is ExpressionSyntax).Select(CreateElement))); + var replacement = CollectionExpression(SeparatedList(preMatches + .Where(m => m.Node is ExpressionSyntax) + .Select(CreateElement) + .Concat(GetInitializerElements(objectCreationExpression.Initializer)))); return UseCollectionExpressionHelpers.CanReplaceWithCollectionExpression( semanticModel, objectCreationExpression, replacement, expressionType, isSingletonInstance: false, allowSemanticsChange, skipVerificationForReplacedNode: true, cancellationToken, out changesSemantics); @@ -66,5 +71,14 @@ static CollectionElementSyntax CreateElement(CollectionMatch match) var expression = (ExpressionSyntax)match.Node; return match.UseSpread ? SpreadElement(expression) : ExpressionElement(expression); } + + static IEnumerable GetInitializerElements(InitializerExpressionSyntax? initializer) + { + if (initializer != null) + { + foreach (var expression in initializer.Expressions) + yield return ExpressionElement(expression); + } + } } } diff --git a/src/Analyzers/CSharp/CodeFixes/UseCollectionInitializer/CSharpUseCollectionInitializerCodeFixProvider_CollectionExpression.cs b/src/Analyzers/CSharp/CodeFixes/UseCollectionInitializer/CSharpUseCollectionInitializerCodeFixProvider_CollectionExpression.cs index e0993b94c6cd1..849f1568fbcfd 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseCollectionInitializer/CSharpUseCollectionInitializerCodeFixProvider_CollectionExpression.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseCollectionInitializer/CSharpUseCollectionInitializerCodeFixProvider_CollectionExpression.cs @@ -30,9 +30,7 @@ private static Task CreateCollectionExpressionAsync( objectCreation, preMatches, postMatches, - // We don't return any initializer in the case of converting a `new List { 1, 2, 3 }` to a collection - // expr. This is because `1, 2, 3` will be in the preMatches instead. - getInitializer: static _ => null, + static objectCreation => objectCreation.Initializer, static (objectCreation, initializer) => objectCreation.WithInitializer(initializer), cancellationToken); } diff --git a/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractUseCollectionInitializerDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractUseCollectionInitializerDiagnosticAnalyzer.cs index b443ab3adb98b..671b11f76ec94 100644 --- a/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractUseCollectionInitializerDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractUseCollectionInitializerDiagnosticAnalyzer.cs @@ -83,7 +83,7 @@ protected abstract bool CanUseCollectionExpression( SemanticModel semanticModel, TObjectCreationExpressionSyntax objectCreationExpression, INamedTypeSymbol? expressionType, - ImmutableArray> matches, + ImmutableArray> preMatches, bool allowSemanticsChange, CancellationToken cancellationToken, out bool changesSemantics); @@ -230,14 +230,12 @@ private void AnalyzeNode( if (preMatches.IsDefault || postMatches.IsDefault) return null; - var matches = preMatches.Concat(postMatches); - // Check if it would actually be legal to use a collection expression here though. var allowSemanticsChange = preferExpressionOption.Value == CollectionExpressionPreference.WhenTypesLooselyMatch; - if (!CanUseCollectionExpression(semanticModel, objectCreationExpression, expressionType, matches, allowSemanticsChange, cancellationToken, out var changesSemantics)) + if (!CanUseCollectionExpression(semanticModel, objectCreationExpression, expressionType, preMatches, allowSemanticsChange, cancellationToken, out var changesSemantics)) return null; - return (matches, shouldUseCollectionExpression: true, changesSemantics); + return (preMatches.Concat(postMatches), shouldUseCollectionExpression: true, changesSemantics); } } From 084c34f4cc32797f1911b983fd45a82ecaa2950e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 13 Nov 2024 11:23:56 -0800 Subject: [PATCH 328/508] Simplify --- ...pUseCollectionInitializerDiagnosticAnalyzer.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerDiagnosticAnalyzer.cs index 954962a44908d..2452993e1a18e 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerDiagnosticAnalyzer.cs @@ -58,18 +58,19 @@ protected override bool CanUseCollectionExpression( { // Synthesize the final collection expression we would replace this object-creation with. That will allow us to // determine if we end up calling the right overload in cases of overloaded methods. - var replacement = CollectionExpression(SeparatedList(preMatches - .Where(m => m.Node is ExpressionSyntax) - .Select(CreateElement) - .Concat(GetInitializerElements(objectCreationExpression.Initializer)))); + var replacement = CollectionExpression(SeparatedList( + GetMatchElements(preMatches).Concat(GetInitializerElements(objectCreationExpression.Initializer)))); return UseCollectionExpressionHelpers.CanReplaceWithCollectionExpression( semanticModel, objectCreationExpression, replacement, expressionType, isSingletonInstance: false, allowSemanticsChange, skipVerificationForReplacedNode: true, cancellationToken, out changesSemantics); - static CollectionElementSyntax CreateElement(CollectionMatch match) + static IEnumerable GetMatchElements(ImmutableArray> preMatches) { - var expression = (ExpressionSyntax)match.Node; - return match.UseSpread ? SpreadElement(expression) : ExpressionElement(expression); + foreach (var match in preMatches) + { + if (match.Node is ExpressionSyntax expression) + yield return match.UseSpread ? SpreadElement(expression) : ExpressionElement(expression); + } } static IEnumerable GetInitializerElements(InitializerExpressionSyntax? initializer) From 1745235df498f5f3c1e4ded96baa88ad6a7b4be7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 13 Nov 2024 11:24:46 -0800 Subject: [PATCH 329/508] Revert --- .../CSharpUseCollectionInitializerAnalyzer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerAnalyzer.cs index 0e912909ab92f..fabc39b46613b 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerAnalyzer.cs @@ -50,7 +50,7 @@ protected override bool AnalyzeMatchesAndCollectionConstructorForCollectionExpre ArrayBuilder> postMatches, CancellationToken cancellationToken) { - // If constructor wasn't called with any arguments, then there's nothing to validate. + // Constructor wasn't called with any arguments. Nothing to validate. var argumentList = _objectCreationExpression.ArgumentList; if (argumentList is null || argumentList.Arguments.Count == 0) return true; From 0307d397be7acfc2f3ec6cb069fdd1f7666135f6 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 13 Nov 2024 11:24:59 -0800 Subject: [PATCH 330/508] Lint --- .../CSharpUseCollectionInitializerDiagnosticAnalyzer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerDiagnosticAnalyzer.cs index 2452993e1a18e..c0bd02c1d2d3c 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerDiagnosticAnalyzer.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; From 3d723b2986eff0d98bf6d47da83cea51178db1fe Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Wed, 13 Nov 2024 12:53:55 -0800 Subject: [PATCH 331/508] Fix an allocation regression due to the recent SegmentedList change caught by speedometer (#75895) The change in https://github.com/dotnet/roslyn/pull/75756 was incorrect when the existing number of items is between SegmentSize / 2 (inclusive) and SegmentSize (exclusive). In this case, the size of the newCapacity would end up as exactly the requested capacity, causing a potentially O(n^2) allocation growth pattern if caller was just increasing the requested capacity by one from it's current size. The fix is just to handle that case directly, and if the existing size falls into that range, to simply set the desired newCapacity to the SegmentSize. --- .../List/SegmentedList.Generic.Tests.Capacity.cs | 13 +++++++++++++ src/Dependencies/Collections/SegmentedList`1.cs | 5 +++++ 2 files changed, 18 insertions(+) diff --git a/src/Compilers/Core/CodeAnalysisTest/Collections/List/SegmentedList.Generic.Tests.Capacity.cs b/src/Compilers/Core/CodeAnalysisTest/Collections/List/SegmentedList.Generic.Tests.Capacity.cs index ec2347a570b8e..6df220121fb62 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Collections/List/SegmentedList.Generic.Tests.Capacity.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Collections/List/SegmentedList.Generic.Tests.Capacity.cs @@ -140,5 +140,18 @@ public void EnsureCapacity_MatchesSizeWithLargeCapacityRequest(int segmentCount) Assert.Equal(expectedCapacity, list.Capacity); } + + [Fact] + public void EnsureCapacity_InitialCapacitySlightlyMoreThanHalfSegmentSizeGrowsToFullSegmentSize() + { + var elementCount = SegmentedArray.TestAccessor.SegmentSize / 2 + 1; + var list = new SegmentedList(elementCount); + + Assert.Equal(elementCount, list.Capacity); + + list.EnsureCapacity(elementCount + 1); + + Assert.Equal(SegmentedArray.TestAccessor.SegmentSize, list.Capacity); + } } } diff --git a/src/Dependencies/Collections/SegmentedList`1.cs b/src/Dependencies/Collections/SegmentedList`1.cs index f00c76a6897df..9e6a45a8ede98 100644 --- a/src/Dependencies/Collections/SegmentedList`1.cs +++ b/src/Dependencies/Collections/SegmentedList`1.cs @@ -520,6 +520,11 @@ internal void Grow(int capacity) // should be DefaultCapacity. Otherwise, the new capacity should be double the current array size. newCapacity = _items.Length == 0 ? DefaultCapacity : _items.Length * 2; } + else if (_items.Length < SegmentedArrayHelper.GetSegmentSize()) + { + // There is only a single segment that is over half full. Increase it to a full segment. + newCapacity = SegmentedArrayHelper.GetSegmentSize(); + } else { // If the last segment is fully sized, increase the number of segments by the desired growth rate From c0556d28bfbe111b723f0d1a8739fadb20109e0c Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Wed, 13 Nov 2024 22:22:24 +0100 Subject: [PATCH 332/508] Workaround failing official builds (#75898) --- azure-pipelines-official.yml | 1 + eng/InternalTools.props | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/azure-pipelines-official.yml b/azure-pipelines-official.yml index 343323ce6d2ae..df7f447156156 100644 --- a/azure-pipelines-official.yml +++ b/azure-pipelines-official.yml @@ -308,6 +308,7 @@ extends: /p:DotnetPublishUsingPipelines=true /p:IgnoreIbcMergeErrors=true /p:GenerateSbom=true + /p:ForceAzureComSources=true condition: succeeded() - template: /eng/common/templates-official/steps/generate-sbom.yml@self diff --git a/eng/InternalTools.props b/eng/InternalTools.props index ab7600ec62793..110ac628fd153 100644 --- a/eng/InternalTools.props +++ b/eng/InternalTools.props @@ -10,6 +10,15 @@ $(RestoreSources); https://pkgs.dev.azure.com/devdiv/_packaging/dotnet-core-internal-tooling/nuget/v3/index.json + + + + https://pkgs.dev.azure.com/devdiv/_packaging/dotnet-core-internal-tooling/nuget/v3/index.json; + https://pkgs.dev.azure.com/devdiv/_packaging/VS/nuget/v3/index.json; + https://pkgs.dev.azure.com/devdiv/_packaging/Engineering/nuget/v3/index.json; + https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json; + https://pkgs.dev.azure.com/devdiv/_packaging/dotnet-core-internal-tooling/nuget/v3/index.json + From f9212c28f04a5038497d12a822b0b9e194b7405a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 13 Nov 2024 13:51:15 -0800 Subject: [PATCH 333/508] improve goto-def on an invalid override --- .../CSharpGoToDefinitionTests.vb | 89 +++++++++++++++++++ .../Extensions/SemanticModelExtensions.cs | 2 +- .../Core/Extensions/ISymbolExtensions.cs | 64 ++++++++++++- .../Core/Extensions/ITypeSymbolExtensions.cs | 13 +-- 4 files changed, 160 insertions(+), 8 deletions(-) diff --git a/src/EditorFeatures/Test2/GoToDefinition/CSharpGoToDefinitionTests.vb b/src/EditorFeatures/Test2/GoToDefinition/CSharpGoToDefinitionTests.vb index 0e2d5bb68980a..5fc5369d1ed53 100644 --- a/src/EditorFeatures/Test2/GoToDefinition/CSharpGoToDefinitionTests.vb +++ b/src/EditorFeatures/Test2/GoToDefinition/CSharpGoToDefinitionTests.vb @@ -855,6 +855,95 @@ class C Await TestAsync(workspace) End Function + + Public Async Function TestCSharpGoToOverriddenDefinition_FromOverride_LooseMatch() As Task + Dim workspace = + + + +class C +{ + public virtual void [|F|](bool x) + { + } +} + +class D : C +{ + public $$override void F(int x) + { + } +} + + + + + Await TestAsync(workspace) + End Function + + + Public Async Function TestCSharpGoToOverriddenDefinition_FromOverride_LooseMatch2() As Task + Dim workspace = + + + +class C +{ + public virtual void F() + { + } + + public virtual void [|F|](bool x) + { + } +} + +class D : C +{ + public $$override void F(int x) + { + } +} + + + + + Await TestAsync(workspace) + End Function + + + Public Async Function TestCSharpGoToOverriddenDefinition_FromOverride_LooseMatch3() As Task + Dim workspace = + + + +class B +{ + public virtual void F(bool x) + { + } +} + +class C +{ + public virtual void [|F|]() + { + } +} + +class D : C +{ + public $$override void F(int x) + { + } +} + + + + + Await TestAsync(workspace) + End Function + Public Async Function TestCSharpGoToUnmanaged_Keyword() As Task Dim workspace = diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs index 0866fcf4877c4..cfc7ffee17c3a 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs @@ -98,7 +98,7 @@ public static TokenSemanticInfo GetSemanticInfo( { // on an "override" token, we'll find the overridden symbol var overriddingSymbol = semanticFacts.GetDeclaredSymbol(semanticModel, overriddingIdentifier.Value, cancellationToken); - var overriddenSymbol = overriddingSymbol.GetOverriddenMember(); + var overriddenSymbol = overriddingSymbol.GetOverriddenMember(allowLooseMatch: true); allSymbols = overriddenSymbol is null ? [] : [overriddenSymbol]; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs index 6a023012023e1..bd0f556a5c438 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs @@ -72,8 +72,12 @@ public static SymbolVisibility GetResultantVisibility(this ISymbol symbol) return visibility; } - public static ISymbol? GetOverriddenMember(this ISymbol? symbol) - => symbol switch + public static ISymbol? GetOverriddenMember(this ISymbol? symbol, bool allowLooseMatch = false) + { + if (symbol is null) + return null; + + ISymbol? exactMatch = symbol switch { IMethodSymbol method => method.OverriddenMethod, IPropertySymbol property => property.OverriddenProperty, @@ -81,6 +85,62 @@ public static SymbolVisibility GetResultantVisibility(this ISymbol symbol) _ => null, }; + if (exactMatch != null) + return exactMatch; + + if (allowLooseMatch) + { + foreach (var baseType in symbol.ContainingType.GetBaseTypes()) + { + if (TryFindLooseMatch(symbol, baseType, out var looseMatch)) + return looseMatch; + } + } + + return null; + + bool TryFindLooseMatch(ISymbol symbol, INamedTypeSymbol baseType, [NotNullWhen(true)] out ISymbol? looseMatch) + { + using var _ = ArrayBuilder.GetInstance(out var candidateMethods); + + foreach (var member in baseType.GetMembers(symbol.Name)) + { + if (member.Kind != symbol.Kind) + continue; + + if (member.IsSealed) + continue; + + if (!member.IsVirtual && !member.IsOverride && !member.IsAbstract) + continue; + + if (symbol.Kind is SymbolKind.Event or SymbolKind.Property) + { + // We've found a matching event/property in the base type (perhaps differing by return type). This + // is a good enough match to return as a loose match for the starting symbol. + looseMatch = member; + return true; + } + else if (member is IMethodSymbol method) + { + // We found a method. Keep track of this so we can try to find the best possible loose match. + candidateMethods.Add(method); + } + } + + var parameterCount = symbol.GetParameters().Length; + candidateMethods.Sort((m1, m2) => + { + var parameterDiff1 = Math.Abs(m1.Parameters.Length - parameterCount); + var parameterDiff2 = Math.Abs(m2.Parameters.Length - parameterCount); + return parameterDiff1 - parameterDiff2; + }); + + looseMatch = candidateMethods.FirstOrDefault(); + return looseMatch != null; + } + } + public static ImmutableArray ExplicitInterfaceImplementations(this ISymbol symbol) => symbol switch { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs index c3db3f7a06779..4480ec0a11ff2 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs @@ -121,13 +121,16 @@ public static IEnumerable GetBaseTypesAndThis(this ITypeSymbol? typ } } - public static IEnumerable GetBaseTypes(this ITypeSymbol type) + public static IEnumerable GetBaseTypes(this ITypeSymbol? type) { - var current = type.BaseType; - while (current != null) + if (type is not null) { - yield return current; - current = current.BaseType; + var current = type.BaseType; + while (current != null) + { + yield return current; + current = current.BaseType; + } } } From 174e4d98d25c2b7da83d7b2a6f79ff5cf5e977e5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 13 Nov 2024 14:21:18 -0800 Subject: [PATCH 334/508] Fix issue where we were recommending removing the GetEnumerator method used in a foreach --- ...rpRemoveUnusedMembersDiagnosticAnalyzer.cs | 3 ++ .../RemoveUnusedMembersTests.cs | 30 ++++++++++++++- ...ctRemoveUnusedMembersDiagnosticAnalyzer.cs | 38 ++++++++++++------- ...icRemoveUnusedMembersDiagnosticAnalyzer.vb | 3 ++ .../SemanticFacts/CSharpSemanticFacts.cs | 24 +++++------- 5 files changed, 70 insertions(+), 28 deletions(-) diff --git a/src/Analyzers/CSharp/Analyzers/RemoveUnusedMembers/CSharpRemoveUnusedMembersDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/RemoveUnusedMembers/CSharpRemoveUnusedMembersDiagnosticAnalyzer.cs index 55ed47da767b2..d0c61fa9d6eb2 100644 --- a/src/Analyzers/CSharp/Analyzers/RemoveUnusedMembers/CSharpRemoveUnusedMembersDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/RemoveUnusedMembers/CSharpRemoveUnusedMembersDiagnosticAnalyzer.cs @@ -7,6 +7,7 @@ using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.RemoveUnusedMembers; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -20,6 +21,8 @@ internal sealed class CSharpRemoveUnusedMembersDiagnosticAnalyzer TypeDeclarationSyntax, MemberDeclarationSyntax> { + protected override ISemanticFacts SemanticFacts => CSharpSemanticFacts.Instance; + protected override IEnumerable GetTypeDeclarations(INamedTypeSymbol namedType, CancellationToken cancellationToken) { return namedType.DeclaringSyntaxReferences diff --git a/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs index c06801cfe9adc..52566516aeaee 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs @@ -15,7 +15,6 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.RemoveUnusedMembers; -using static Microsoft.CodeAnalysis.CSharp.UsePatternCombinators.AnalyzedPattern; using VerifyCS = CSharpCodeFixVerifier< CSharpRemoveUnusedMembersDiagnosticAnalyzer, CSharpRemoveUnusedMembersCodeFixProvider>; @@ -3243,4 +3242,33 @@ class C await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31582")] + public async Task TestForeach() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections; + + static class Program + { + static void Main(string[] args) + { + foreach (var i in 1..10) + Console.WriteLine(i); + } + + static IEnumerator GetEnumerator(this Range range) + { + for (int i = range.Start.Value; i < range.End.Value; i++) + yield return i; + } + } + """, + LanguageVersion = LanguageVersion.CSharp13, + ReferenceAssemblies = ReferenceAssemblies.Net.Net90, + }.RunAsync(); + } } diff --git a/src/Analyzers/Core/Analyzers/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs index 814efb24c087c..e947de09d2524 100644 --- a/src/Analyzers/Core/Analyzers/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.CodeQuality; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -25,8 +26,11 @@ internal abstract class AbstractRemoveUnusedMembersDiagnosticAnalyzer< TDocumentationCommentTriviaSyntax, TIdentifierNameSyntax, TTypeDeclarationSyntax, - TMemberDeclarationSyntax> - : AbstractCodeQualityDiagnosticAnalyzer + TMemberDeclarationSyntax>() + : AbstractCodeQualityDiagnosticAnalyzer( + [s_removeUnusedMembersRule, s_removeUnreadMembersRule], + // We want to analyze references in generated code, but not report unused members in generated code. + GeneratedCodeAnalysisFlags.Analyze) where TDocumentationCommentTriviaSyntax : SyntaxNode where TIdentifierNameSyntax : SyntaxNode where TTypeDeclarationSyntax : TMemberDeclarationSyntax @@ -56,11 +60,7 @@ internal abstract class AbstractRemoveUnusedMembersDiagnosticAnalyzer< new LocalizableResourceString(nameof(AnalyzersResources.Private_member_0_can_be_removed_as_the_value_assigned_to_it_is_never_read), AnalyzersResources.ResourceManager, typeof(AnalyzersResources)), hasAnyCodeStyleOption: false, isUnnecessary: true); - protected AbstractRemoveUnusedMembersDiagnosticAnalyzer() - : base([s_removeUnusedMembersRule, s_removeUnreadMembersRule], - GeneratedCodeAnalysisFlags.Analyze) // We want to analyze references in generated code, but not report unused members in generated code. - { - } + protected abstract ISemanticFacts SemanticFacts { get; } protected abstract IEnumerable GetTypeDeclarations(INamedTypeSymbol namedType, CancellationToken cancellationToken); protected abstract SyntaxList GetMembers(TTypeDeclarationSyntax typeDeclaration); @@ -86,7 +86,8 @@ protected virtual void HandleNamedTypeSymbolStart(SymbolStartAnalysisContext con private sealed class CompilationAnalyzer { - private readonly object _gate; + private readonly object _gate = new(); + /// /// State map for candidate member symbols, with the value indicating how each symbol is used in executable code. /// @@ -114,7 +115,6 @@ private CompilationAnalyzer( Compilation compilation, AbstractRemoveUnusedMembersDiagnosticAnalyzer analyzer) { - _gate = new object(); _analyzer = analyzer; _taskType = compilation.TaskType(); @@ -204,6 +204,7 @@ private void RegisterActions(CompilationStartAnalysisContext compilationStartCon symbolStartContext.RegisterOperationAction(AnalyzeInvocationOperation, OperationKind.Invocation); symbolStartContext.RegisterOperationAction(AnalyzeNameOfOperation, OperationKind.NameOf); symbolStartContext.RegisterOperationAction(AnalyzeObjectCreationOperation, OperationKind.ObjectCreation); + symbolStartContext.RegisterOperationAction(AnalyzeLoopOperation, OperationKind.Loop); // We bail out reporting diagnostics for named types if it contains following kind of operations: // 1. Invalid operations, i.e. erroneous code: @@ -211,8 +212,9 @@ private void RegisterActions(CompilationStartAnalysisContext compilationStartCon // is still editing code and fixing unresolved references to symbols, such as overload resolution errors. // 2. Dynamic operations, where we do not know the exact member being referenced at compile time. // 3. Operations with OperationKind.None. - symbolStartContext.RegisterOperationAction(_ => hasUnsupportedOperation = true, OperationKind.Invalid, OperationKind.None, - OperationKind.DynamicIndexerAccess, OperationKind.DynamicInvocation, OperationKind.DynamicMemberReference, OperationKind.DynamicObjectCreation); + symbolStartContext.RegisterOperationAction( + _ => hasUnsupportedOperation = true, + OperationKind.Invalid, OperationKind.None, OperationKind.DynamicIndexerAccess, OperationKind.DynamicInvocation, OperationKind.DynamicMemberReference, OperationKind.DynamicObjectCreation); symbolStartContext.RegisterSymbolEndAction(symbolEndContext => OnSymbolEnd(symbolEndContext, hasUnsupportedOperation)); @@ -369,6 +371,18 @@ memberReference.Parent is IIncrementOrDecrementOperation || } } + private void AnalyzeLoopOperation(OperationAnalysisContext operationContext) + { + var operation = operationContext.Operation; + if (operation is not IForEachLoopOperation loopOperation) + return; + + var symbols = _analyzer.SemanticFacts.GetForEachSymbols(operation.SemanticModel!, loopOperation.Syntax); + OnSymbolUsage(symbols.CurrentProperty, ValueUsageInfo.Read); + OnSymbolUsage(symbols.GetEnumeratorMethod, ValueUsageInfo.Read); + OnSymbolUsage(symbols.MoveNextMethod, ValueUsageInfo.Read); + } + private void AnalyzeInvocationOperation(OperationAnalysisContext operationContext) { var targetMethod = ((IInvocationOperation)operationContext.Operation).TargetMethod.OriginalDefinition; @@ -380,9 +394,7 @@ private void AnalyzeInvocationOperation(OperationAnalysisContext operationContex // If the invoked method is a reduced extension method, also mark the original // method from which it was reduced as "used". if (targetMethod.ReducedFrom != null) - { OnSymbolUsage(targetMethod.ReducedFrom, ValueUsageInfo.Read); - } } private void AnalyzeNameOfOperation(OperationAnalysisContext operationContext) diff --git a/src/Analyzers/VisualBasic/Analyzers/RemoveUnusedMembers/VisualBasicRemoveUnusedMembersDiagnosticAnalyzer.vb b/src/Analyzers/VisualBasic/Analyzers/RemoveUnusedMembers/VisualBasicRemoveUnusedMembersDiagnosticAnalyzer.vb index 21a7da1fd0061..fdb5b329bf038 100644 --- a/src/Analyzers/VisualBasic/Analyzers/RemoveUnusedMembers/VisualBasicRemoveUnusedMembersDiagnosticAnalyzer.vb +++ b/src/Analyzers/VisualBasic/Analyzers/RemoveUnusedMembers/VisualBasicRemoveUnusedMembersDiagnosticAnalyzer.vb @@ -4,6 +4,7 @@ Imports System.Threading Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.LanguageService Imports Microsoft.CodeAnalysis.RemoveUnusedMembers Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -16,6 +17,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnusedMembers TypeBlockSyntax, StatementSyntax) + Protected Overrides ReadOnly Property SemanticFacts As ISemanticFacts = VisualBasicSemanticFacts.Instance + Protected Overrides Sub HandleNamedTypeSymbolStart(context As SymbolStartAnalysisContext, onSymbolUsageFound As Action(Of ISymbol, ValueUsageInfo)) ' Mark all methods with handles clause as having a read reference ' to ensure that we consider the method as "used". diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs index c76a5a8def8fa..47261897ffdd0 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs @@ -166,22 +166,18 @@ private static void AppendAliasNames(IEnumerable } } - public ForEachSymbols GetForEachSymbols(SemanticModel semanticModel, SyntaxNode forEachStatement) + public ForEachSymbols GetForEachSymbols(SemanticModel semanticModel, SyntaxNode node) { - if (forEachStatement is CommonForEachStatementSyntax csforEachStatement) - { - var info = semanticModel.GetForEachStatementInfo(csforEachStatement); - return new ForEachSymbols( - info.GetEnumeratorMethod, - info.MoveNextMethod, - info.CurrentProperty, - info.DisposeMethod, - info.ElementType); - } - else - { + if (node is not CommonForEachStatementSyntax forEachStatement) return default; - } + + var info = semanticModel.GetForEachStatementInfo(forEachStatement); + return new ForEachSymbols( + info.GetEnumeratorMethod, + info.MoveNextMethod, + info.CurrentProperty, + info.DisposeMethod, + info.ElementType); } public SymbolInfo GetCollectionInitializerSymbolInfo(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken) From 13cdd19899c7dffa39053f664deb8c5e528c861e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 13 Nov 2024 14:21:40 -0800 Subject: [PATCH 335/508] Work item --- .../Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs index 52566516aeaee..d7ab7dbffa2a7 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs @@ -3243,7 +3243,7 @@ class C await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31582")] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/57470")] public async Task TestForeach() { await new VerifyCS.Test From ae3061fec0471e4715101f3f6aa4995dfde52259 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 13 Nov 2024 15:32:59 -0800 Subject: [PATCH 336/508] Simplify --- .../Compiler/Core/Extensions/ITypeSymbolExtensions.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs index 4480ec0a11ff2..1de694e2114dc 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs @@ -123,14 +123,11 @@ public static IEnumerable GetBaseTypesAndThis(this ITypeSymbol? typ public static IEnumerable GetBaseTypes(this ITypeSymbol? type) { - if (type is not null) + var current = type?.BaseType; + while (current != null) { - var current = type.BaseType; - while (current != null) - { - yield return current; - current = current.BaseType; - } + yield return current; + current = current.BaseType; } } From 23edf8dc6fff6ec8020d64a4b4c558bf354704b1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 13 Nov 2024 15:37:33 -0800 Subject: [PATCH 337/508] Simplify --- .../Core/Extensions/ISymbolExtensions.cs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs index bd0f556a5c438..d4e7dbb5e49a9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs @@ -101,7 +101,8 @@ public static SymbolVisibility GetResultantVisibility(this ISymbol symbol) bool TryFindLooseMatch(ISymbol symbol, INamedTypeSymbol baseType, [NotNullWhen(true)] out ISymbol? looseMatch) { - using var _ = ArrayBuilder.GetInstance(out var candidateMethods); + IMethodSymbol? bestMethod = null; + var parameterCount = symbol.GetParameters().Length; foreach (var member in baseType.GetMembers(symbol.Name)) { @@ -123,20 +124,13 @@ bool TryFindLooseMatch(ISymbol symbol, INamedTypeSymbol baseType, [NotNullWhen(t } else if (member is IMethodSymbol method) { - // We found a method. Keep track of this so we can try to find the best possible loose match. - candidateMethods.Add(method); + // Prefer methods that are closed in parameter count to the original method we started with. + if (bestMethod is null || Math.Abs(method.Parameters.Length - parameterCount) < Math.Abs(bestMethod.Parameters.Length - parameterCount)) + bestMethod = method; } } - var parameterCount = symbol.GetParameters().Length; - candidateMethods.Sort((m1, m2) => - { - var parameterDiff1 = Math.Abs(m1.Parameters.Length - parameterCount); - var parameterDiff2 = Math.Abs(m2.Parameters.Length - parameterCount); - return parameterDiff1 - parameterDiff2; - }); - - looseMatch = candidateMethods.FirstOrDefault(); + looseMatch = bestMethod; return looseMatch != null; } } From e75664fa1ebe4b064ea84814a006b2d6e7fe2e11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Wed, 13 Nov 2024 15:54:01 -0800 Subject: [PATCH 338/508] [EnC] Implement support for restarting subset of projects affected by rude edits (#75874) --- eng/Directory.Packages.props | 2 +- src/Compilers/Test/Core/Assert/AssertEx.cs | 8 +- .../Test/Core/InstrumentationChecker.cs | 2 +- .../EditAndContinueLanguageService.cs | 77 +++++++++- .../EditAndContinueLanguageServiceBridge.cs | 15 +- .../Debugger/GlassTestsHotReloadService.cs | 2 +- .../EditAndContinueLanguageServiceTests.cs | 8 +- .../Diagnostics/DiagnosticProviderTests.vb | 2 +- .../IManagedHotReloadLanguageService.cs | 7 + .../EditAndContinue/DebuggingSession.cs | 93 +++++++++--- .../DebuggingSessionTelemetry.cs | 3 + .../EditAndContinue/EditAndContinueService.cs | 11 +- .../EditAndContinue/EditSessionTelemetry.cs | 6 + .../EmitSolutionUpdateResults.cs | 92 +++++++----- .../IEditAndContinueService.cs | 4 +- .../Remote/IRemoteEditAndContinueService.cs | 3 +- .../Remote/RemoteDebuggingSessionProxy.cs | 26 +++- .../API/UnitTestingHotReloadService.cs | 2 +- .../Watch/Api/WatchHotReloadService.cs | 42 ++++-- .../EditAndContinueWorkspaceServiceTests.cs | 139 ++++++++++++++---- .../EmitSolutionUpdateResultsTests.cs | 100 ++++++++----- .../RemoteEditAndContinueServiceTests.cs | 9 +- .../WatchHotReloadServiceTests.cs | 22 +-- .../EditAndContinueWorkspaceTestBase.cs | 25 +++- .../EditAndContinue/Extensions.cs | 3 +- .../MockEditAndContinueService.cs | 10 +- .../ManagedHotReloadLanguageServiceBridge.cs | 9 +- .../ManagedHotReloadLanguageService.cs | 49 +++++- .../RemoteEditAndContinueService.cs | 18 ++- 29 files changed, 606 insertions(+), 183 deletions(-) diff --git a/eng/Directory.Packages.props b/eng/Directory.Packages.props index 4b1707551e3af..e63fad5dc2001 100644 --- a/eng/Directory.Packages.props +++ b/eng/Directory.Packages.props @@ -122,7 +122,7 @@ - + diff --git a/src/Compilers/Test/Core/Assert/AssertEx.cs b/src/Compilers/Test/Core/Assert/AssertEx.cs index 49a41a88e565e..cbe7bc403bd09 100644 --- a/src/Compilers/Test/Core/Assert/AssertEx.cs +++ b/src/Compilers/Test/Core/Assert/AssertEx.cs @@ -182,15 +182,15 @@ public static void Equal(ImmutableArray expected, IEnumerable actual, I public static void Equal(IEnumerable expected, ImmutableArray actual) => Equal(expected, actual, comparer: null, message: null, itemInspector: null); - public static void Equal(IEnumerable expected, ImmutableArray actual, IEqualityComparer comparer = null, string message = null, string itemSeparator = null) + public static void SequenceEqual(IEnumerable expected, IEnumerable actual, IEqualityComparer comparer = null, string message = null, string itemSeparator = null) { - if (expected == null || actual.IsDefault) + if (expected == null || actual == null) { - Assert.True((expected == null) == actual.IsDefault, message); + Assert.True(expected is null == actual is null, message); } else { - Equal(expected, (IEnumerable)actual, comparer, message, itemSeparator); + Equal(expected, actual, comparer, message, itemSeparator); } } diff --git a/src/Compilers/Test/Core/InstrumentationChecker.cs b/src/Compilers/Test/Core/InstrumentationChecker.cs index 8691cf0134f16..6427ec517890e 100644 --- a/src/Compilers/Test/Core/InstrumentationChecker.cs +++ b/src/Compilers/Test/Core/InstrumentationChecker.cs @@ -335,7 +335,7 @@ public void CompleteCheck(Compilation compilation, string source) var actualSnippets = GetActualSnippets(method, reader, sourceLines); var expectedSnippets = _spanExpectations[method].SnippetExpectations; - AssertEx.Equal(expectedSnippets, actualSnippets, new SnippetComparer(), $"Validation of method {method} failed."); + AssertEx.SequenceEqual(expectedSnippets, actualSnippets, new SnippetComparer(), $"Validation of method {method} failed."); } } diff --git a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs index 93a157f97d471..d0375b06d13c8 100644 --- a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs +++ b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs @@ -6,6 +6,7 @@ using System.Collections.Immutable; using System.Composition; using System.Diagnostics; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.BrokeredServices; @@ -13,6 +14,8 @@ using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.Debugger.Contracts.HotReload; using Roslyn.Utilities; @@ -21,6 +24,7 @@ namespace Microsoft.CodeAnalysis.EditAndContinue; [Shared] [Export(typeof(IManagedHotReloadLanguageService))] +[Export(typeof(IManagedHotReloadLanguageService2))] [Export(typeof(IEditAndContinueSolutionProvider))] [Export(typeof(EditAndContinueLanguageService))] [ExportMetadata("UIContext", EditAndContinueUIContext.EncCapableProjectExistsInWorkspaceUIContextString)] @@ -33,7 +37,7 @@ internal sealed class EditAndContinueLanguageService( Lazy debuggerService, PdbMatchingSourceTextProvider sourceTextProvider, IDiagnosticsRefresher diagnosticRefresher, - IAsynchronousOperationListenerProvider listenerProvider) : IManagedHotReloadLanguageService, IEditAndContinueSolutionProvider + IAsynchronousOperationListenerProvider listenerProvider) : IManagedHotReloadLanguageService2, IEditAndContinueSolutionProvider { private sealed class NoSessionException : InvalidOperationException { @@ -242,6 +246,64 @@ public async ValueTask DiscardUpdatesAsync(CancellationToken cancellationToken) } } + public async ValueTask UpdateBaselinesAsync(ImmutableArray projectPaths, CancellationToken cancellationToken) + { + if (_disabled) + { + return; + } + + var currentDesignTimeSolution = GetCurrentDesignTimeSolution(); + var currentCompileTimeSolution = GetCurrentCompileTimeSolution(currentDesignTimeSolution); + + try + { + SolutionCommitted?.Invoke(currentDesignTimeSolution); + } + catch (Exception e) when (FatalError.ReportAndCatch(e)) + { + } + + _committedDesignTimeSolution = currentDesignTimeSolution; + var projectIds = await GetProjectIdsAsync(projectPaths, currentCompileTimeSolution, cancellationToken).ConfigureAwait(false); + + try + { + await GetDebuggingSession().UpdateBaselinesAsync(currentCompileTimeSolution, projectIds, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) + { + } + + foreach (var projectId in projectIds) + { + workspaceProvider.Value.Workspace.EnqueueUpdateSourceGeneratorVersion(projectId, forceRegeneration: false); + } + } + + private async ValueTask> GetProjectIdsAsync(ImmutableArray projectPaths, Solution solution, CancellationToken cancellationToken) + { + using var _ = ArrayBuilder.GetInstance(out var projectIds); + foreach (var path in projectPaths) + { + var projectId = solution.Projects.FirstOrDefault(project => project.FilePath == path)?.Id; + if (projectId != null) + { + projectIds.Add(projectId); + } + else + { + await _logger.LogAsync(new HotReloadLogMessage( + HotReloadVerbosity.Diagnostic, + $"Project with path '{path}' not found in the current solution.", + errorLevel: HotReloadDiagnosticErrorLevel.Warning), + cancellationToken).ConfigureAwait(false); + } + } + + return projectIds.ToImmutable(); + } + public async ValueTask EndSessionAsync(CancellationToken cancellationToken) { sessionState.IsSessionActive = false; @@ -309,7 +371,11 @@ public async ValueTask HasChangesAsync(string? sourceFilePath, Cancellatio } } - public async ValueTask GetUpdatesAsync(CancellationToken cancellationToken) + [Obsolete] + public ValueTask GetUpdatesAsync(CancellationToken cancellationToken) + => GetUpdatesAsync(runningProjects: [], cancellationToken); + + public async ValueTask GetUpdatesAsync(ImmutableArray runningProjects, CancellationToken cancellationToken) { if (_disabled) { @@ -319,7 +385,12 @@ public async ValueTask GetUpdatesAsync(CancellationToke var designTimeSolution = GetCurrentDesignTimeSolution(); var solution = GetCurrentCompileTimeSolution(designTimeSolution); var activeStatementSpanProvider = GetActiveStatementSpanProvider(solution); - var result = await GetDebuggingSession().EmitSolutionUpdateAsync(solution, activeStatementSpanProvider, cancellationToken).ConfigureAwait(false); + + using var _ = PooledHashSet.GetInstance(out var runningProjectPaths); + runningProjectPaths.AddAll(runningProjects); + + var runningProjectIds = solution.Projects.Where(p => p.FilePath != null && runningProjectPaths.Contains(p.FilePath)).Select(static p => p.Id).ToImmutableHashSet(); + var result = await GetDebuggingSession().EmitSolutionUpdateAsync(solution, runningProjectIds, activeStatementSpanProvider, cancellationToken).ConfigureAwait(false); // Only store the solution if we have any changes to apply, otherwise CommitUpdatesAsync/DiscardUpdatesAsync won't be called. if (result.ModuleUpdates.Status == ModuleUpdateStatus.Ready) diff --git a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageServiceBridge.cs b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageServiceBridge.cs index f4071f8137054..be9aadc407671 100644 --- a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageServiceBridge.cs +++ b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageServiceBridge.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.VisualStudio.Debugger.Contracts.HotReload; @@ -13,7 +15,7 @@ namespace Microsoft.CodeAnalysis.EditAndContinue; /// TODO (https://github.com/dotnet/roslyn/issues/72713): /// Once debugger is updated to use the brokered service, this class should be removed and should be exported directly. /// -internal sealed partial class ManagedEditAndContinueLanguageServiceBridge(EditAndContinueLanguageService service) : IManagedHotReloadLanguageService +internal sealed partial class ManagedEditAndContinueLanguageServiceBridge(EditAndContinueLanguageService service) : IManagedHotReloadLanguageService2 { public ValueTask StartSessionAsync(CancellationToken cancellationToken) => service.StartSessionAsync(cancellationToken); @@ -30,12 +32,19 @@ public ValueTask ExitBreakStateAsync(CancellationToken cancellationToken) public ValueTask OnCapabilitiesChangedAsync(CancellationToken cancellationToken) => service.OnCapabilitiesChangedAsync(cancellationToken); - public async ValueTask GetUpdatesAsync(CancellationToken cancellationToken) - => (await service.GetUpdatesAsync(cancellationToken).ConfigureAwait(false)); + [Obsolete] + public ValueTask GetUpdatesAsync(CancellationToken cancellationToken) + => service.GetUpdatesAsync(cancellationToken); + + public ValueTask GetUpdatesAsync(ImmutableArray runningProjects, CancellationToken cancellationToken) + => service.GetUpdatesAsync(runningProjects, cancellationToken); public ValueTask CommitUpdatesAsync(CancellationToken cancellationToken) => service.CommitUpdatesAsync(cancellationToken); + public ValueTask UpdateBaselinesAsync(ImmutableArray projectPaths, CancellationToken cancellationToken) + => service.UpdateBaselinesAsync(projectPaths, cancellationToken); + public ValueTask DiscardUpdatesAsync(CancellationToken cancellationToken) => service.DiscardUpdatesAsync(cancellationToken); diff --git a/src/EditorFeatures/ExternalAccess/Debugger/GlassTestsHotReloadService.cs b/src/EditorFeatures/ExternalAccess/Debugger/GlassTestsHotReloadService.cs index 891067cc79258..41c74790a3687 100644 --- a/src/EditorFeatures/ExternalAccess/Debugger/GlassTestsHotReloadService.cs +++ b/src/EditorFeatures/ExternalAccess/Debugger/GlassTestsHotReloadService.cs @@ -84,7 +84,7 @@ public void EndDebuggingSession() public async ValueTask GetUpdatesAsync(Solution solution, CancellationToken cancellationToken) { - var results = (await _encService.EmitSolutionUpdateAsync(GetSessionId(), solution, s_noActiveStatementSpanProvider, cancellationToken).ConfigureAwait(false)).Dehydrate(); + var results = (await _encService.EmitSolutionUpdateAsync(GetSessionId(), solution, runningProjects: [], s_noActiveStatementSpanProvider, cancellationToken).ConfigureAwait(false)).Dehydrate(); return new ManagedHotReloadUpdates(results.ModuleUpdates.Updates.FromContract(), results.GetAllDiagnostics().FromContract()); } } diff --git a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueLanguageServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueLanguageServiceTests.cs index 5d24bdf93285a..eaf50833afbf7 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueLanguageServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueLanguageServiceTests.cs @@ -156,7 +156,7 @@ await localWorkspace.ChangeSolutionAsync(localWorkspace.CurrentSolution var diagnosticDescriptor1 = EditAndContinueDiagnosticDescriptors.GetDescriptor(EditAndContinueErrorCode.ErrorReadingFile); - mockEncService.EmitSolutionUpdateImpl = (solution, _) => + mockEncService.EmitSolutionUpdateImpl = (solution, runningProjects, _) => { var syntaxTree = solution.GetRequiredDocument(documentId).GetSyntaxTreeSynchronously(CancellationToken.None)!; @@ -171,11 +171,13 @@ await localWorkspace.ChangeSolutionAsync(localWorkspace.CurrentSolution ModuleUpdates = new ModuleUpdates(ModuleUpdateStatus.Ready, []), Diagnostics = [new ProjectDiagnostics(project.Id, [documentDiagnostic, projectDiagnostic])], RudeEdits = [new ProjectDiagnostics(project.Id, [rudeEditDiagnostic])], - SyntaxError = syntaxError + SyntaxError = syntaxError, + ProjectsToRebuild = [project.Id], + ProjectsToRestart = [project.Id] }; }; - var updates = await localService.GetUpdatesAsync(CancellationToken.None); + var updates = await localService.GetUpdatesAsync(runningProjects: [project.FilePath], CancellationToken.None); Assert.Equal(++observedDiagnosticVersion, diagnosticRefresher.GlobalStateVersion); diff --git a/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb b/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb index 19a9f764f34be..088b930be272f 100644 --- a/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb @@ -271,7 +271,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim expectedDiagnostics = GetExpectedDiagnostics(workspace, diagnostics) If ordered Then - AssertEx.Equal(expectedDiagnostics, actualDiagnostics, New Comparer()) + AssertEx.SequenceEqual(expectedDiagnostics, actualDiagnostics, New Comparer()) Else AssertEx.SetEqual(expectedDiagnostics, actualDiagnostics, New Comparer()) End If diff --git a/src/Features/Core/Portable/Contracts/EditAndContinue/IManagedHotReloadLanguageService.cs b/src/Features/Core/Portable/Contracts/EditAndContinue/IManagedHotReloadLanguageService.cs index aad37a672ebbb..3ca98a0430989 100644 --- a/src/Features/Core/Portable/Contracts/EditAndContinue/IManagedHotReloadLanguageService.cs +++ b/src/Features/Core/Portable/Contracts/EditAndContinue/IManagedHotReloadLanguageService.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -19,3 +20,9 @@ internal interface IManagedHotReloadLanguageService ValueTask OnCapabilitiesChangedAsync(CancellationToken cancellationToken); ValueTask StartSessionAsync(CancellationToken cancellationToken); } + +internal interface IManagedHotReloadLanguageService2 : IManagedHotReloadLanguageService +{ + ValueTask GetUpdatesAsync(ImmutableArray runningProjects, CancellationToken cancellationToken); + ValueTask UpdateBaselinesAsync(ImmutableArray projectPaths, CancellationToken cancellationToken); +} diff --git a/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs b/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs index f528fb3a0fc29..50950beebc570 100644 --- a/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs +++ b/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs @@ -53,7 +53,7 @@ internal sealed class DebuggingSession : IDisposable /// even when it's replaced in by a newer baseline. /// private readonly Dictionary _projectBaselines = []; - private readonly List _initialBaselineModuleReaders = []; + private readonly Dictionary _initialBaselineModuleReaders = []; private readonly object _projectEmitBaselinesGuard = new(); /// @@ -143,9 +143,13 @@ public void Dispose() // Wait for all operations on baseline to finish before we dispose the readers. _baselineAccessLock.EnterWriteLock(); - foreach (var reader in GetBaselineModuleReaders()) + lock (_projectEmitBaselinesGuard) { - reader.Dispose(); + foreach (var (_, readers) in _initialBaselineModuleReaders) + { + readers.metadata.Dispose(); + readers.pdb.Dispose(); + } } _baselineAccessLock.ExitWriteLock(); @@ -223,14 +227,6 @@ internal void RestartEditSession(ImmutableDictionary GetBaselineModuleReaders() - { - lock (_projectEmitBaselinesGuard) - { - return _initialBaselineModuleReaders.ToImmutableArrayOrEmpty(); - } - } - internal CompilationOutputs GetCompilationOutputs(Project project) => _compilationOutputsProvider(project); @@ -353,8 +349,7 @@ internal bool TryGetOrCreateEmitBaseline( baseline = new ProjectBaseline(baselineProject.Id, initialBaseline, generation: 0); _projectBaselines.Add(baselineProject.Id, baseline); - _initialBaselineModuleReaders.Add(metadataReaderProvider); - _initialBaselineModuleReaders.Add(debugInfoReaderProvider); + _initialBaselineModuleReaders.Add(baselineProject.Id, (metadataReaderProvider, debugInfoReaderProvider)); } return true; @@ -517,6 +512,7 @@ CommittedSolution.DocumentState.Indeterminate or public async ValueTask EmitSolutionUpdateAsync( Solution solution, + IImmutableSet runningProjects, ActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken) { @@ -552,6 +548,14 @@ public async ValueTask EmitSolutionUpdateAsync( } } + EmitSolutionUpdateResults.GetProjectsToRebuildAndRestart( + solution, + solutionUpdate.ModuleUpdates, + rudeEditDiagnostics, + runningProjects, + out var projectsToRestart, + out var projectsToRebuild); + // Note that we may return empty deltas if all updates have been deferred. // The debugger will still call commit or discard on the update batch. return new EmitSolutionUpdateResults() @@ -561,6 +565,8 @@ public async ValueTask EmitSolutionUpdateAsync( Diagnostics = solutionUpdate.Diagnostics, RudeEdits = rudeEditDiagnostics.ToImmutable(), SyntaxError = solutionUpdate.SyntaxError, + ProjectsToRestart = projectsToRestart, + ProjectsToRebuild = projectsToRebuild }; } @@ -608,6 +614,38 @@ public void DiscardSolutionUpdate() _ = RetrievePendingUpdate(); } + public void UpdateBaselines(Solution solution, ImmutableArray rebuiltProjects) + { + ThrowIfDisposed(); + + // Make sure the solution snapshot has all source-generated documents up-to-date. + solution = solution.WithUpToDateSourceGeneratorDocuments(solution.ProjectIds); + + LastCommittedSolution.CommitSolution(solution); + + lock (_projectEmitBaselinesGuard) + { + foreach (var projectId in rebuiltProjects) + { + _projectBaselines.Remove(projectId); + + var (metadata, pdb) = _initialBaselineModuleReaders[projectId]; + metadata.Dispose(); + pdb.Dispose(); + + _initialBaselineModuleReaders.Remove(projectId); + } + } + + foreach (var projectId in rebuiltProjects) + { + _editSessionTelemetry.LogUpdatedBaseline(solution.GetRequiredProject(projectId).State.ProjectInfo.Attributes.TelemetryId); + } + + // Restart edit session reusing previous non-remappable regions and break state: + RestartEditSession(nonRemappableRegions: null, inBreakState: null); + } + /// /// Returns s for each document of , /// or default if not in a break state. @@ -851,31 +889,42 @@ internal TestAccessor GetTestAccessor() internal readonly struct TestAccessor(DebuggingSession instance) { - private readonly DebuggingSession _instance = instance; - public ImmutableHashSet GetModulesPreparedForUpdate() { - lock (_instance._modulesPreparedForUpdateGuard) + lock (instance._modulesPreparedForUpdateGuard) { - return [.. _instance._modulesPreparedForUpdate]; + return [.. instance._modulesPreparedForUpdate]; } } public EmitBaseline GetProjectEmitBaseline(ProjectId id) { - lock (_instance._projectEmitBaselinesGuard) + lock (instance._projectEmitBaselinesGuard) + { + return instance._projectBaselines[id].EmitBaseline; + } + } + + public bool HasProjectEmitBaseline(ProjectId id) + { + lock (instance._projectEmitBaselinesGuard) { - return _instance._projectBaselines[id].EmitBaseline; + return instance._projectBaselines.ContainsKey(id); } } public ImmutableArray GetBaselineModuleReaders() - => _instance.GetBaselineModuleReaders(); + { + lock (instance._projectEmitBaselinesGuard) + { + return instance._initialBaselineModuleReaders.Values.SelectMany(entry => new IDisposable[] { entry.metadata, entry.pdb }).ToImmutableArray(); + } + } public PendingUpdate? GetPendingSolutionUpdate() - => _instance._pendingUpdate; + => instance._pendingUpdate; public void SetTelemetryLogger(Action logger, Func getNextId) - => _instance._reportTelemetry = data => DebuggingSessionTelemetry.Log(data, logger, getNextId); + => instance._reportTelemetry = data => DebuggingSessionTelemetry.Log(data, logger, getNextId); } } diff --git a/src/Features/Core/Portable/EditAndContinue/DebuggingSessionTelemetry.cs b/src/Features/Core/Portable/EditAndContinue/DebuggingSessionTelemetry.cs index 2a9b478b6a437..32f2582d3db11 100644 --- a/src/Features/Core/Portable/EditAndContinue/DebuggingSessionTelemetry.cs +++ b/src/Features/Core/Portable/EditAndContinue/DebuggingSessionTelemetry.cs @@ -116,6 +116,9 @@ public static void Log(Data data, Action log, Func // Ids of all projects whose binaries were successfully updated during the session. map["ProjectIdsWithAppliedChanges"] = editSessionData.Committed ? editSessionData.ProjectsWithValidDelta.Select(ProjectIdToPii) : ""; + // Ids of all projects whose binaries had their initial baselines updated (the projects were rebuilt during debugging session). + map["ProjectIdsWithUpdatedBaselines"] = editSessionData.ProjectsWithUpdatedBaselines.Select(ProjectIdToPii); + // Total milliseconds it took to emit the delta in this edit session. map["EmitDifferenceMilliseconds"] = (long)editSessionData.EmitDifferenceTime.TotalMilliseconds; diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueService.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueService.cs index 80b9b57f741b5..7e4f13cce471c 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueService.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueService.cs @@ -221,6 +221,7 @@ public ValueTask> GetDocumentDiagnosticsAsync(Documen public ValueTask EmitSolutionUpdateAsync( DebuggingSessionId sessionId, Solution solution, + IImmutableSet runningProjects, ActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken) { @@ -230,7 +231,7 @@ public ValueTask EmitSolutionUpdateAsync( return ValueTaskFactory.FromResult(EmitSolutionUpdateResults.Empty); } - return debuggingSession.EmitSolutionUpdateAsync(solution, activeStatementSpanProvider, cancellationToken); + return debuggingSession.EmitSolutionUpdateAsync(solution, runningProjects, activeStatementSpanProvider, cancellationToken); } public void CommitSolutionUpdate(DebuggingSessionId sessionId) @@ -249,6 +250,14 @@ public void DiscardSolutionUpdate(DebuggingSessionId sessionId) debuggingSession.DiscardSolutionUpdate(); } + public void UpdateBaselines(DebuggingSessionId sessionId, Solution solution, ImmutableArray rebuiltProjects) + { + var debuggingSession = TryGetDebuggingSession(sessionId); + Contract.ThrowIfNull(debuggingSession); + + debuggingSession.UpdateBaselines(solution, rebuiltProjects); + } + public ValueTask>> GetBaseActiveStatementSpansAsync(DebuggingSessionId sessionId, Solution solution, ImmutableArray documentIds, CancellationToken cancellationToken) { var debuggingSession = TryGetDebuggingSession(sessionId); diff --git a/src/Features/Core/Portable/EditAndContinue/EditSessionTelemetry.cs b/src/Features/Core/Portable/EditAndContinue/EditSessionTelemetry.cs index 73bc0540e4be0..d2ead3388ee92 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditSessionTelemetry.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditSessionTelemetry.cs @@ -18,6 +18,7 @@ internal readonly struct Data(EditSessionTelemetry telemetry) public readonly ImmutableArray<(ushort EditKind, ushort SyntaxKind, Guid projectId)> RudeEdits = telemetry._rudeEdits.AsImmutable(); public readonly ImmutableArray EmitErrorIds = telemetry._emitErrorIds.AsImmutable(); public readonly ImmutableArray ProjectsWithValidDelta = telemetry._projectsWithValidDelta.AsImmutable(); + public readonly ImmutableArray ProjectsWithUpdatedBaselines = telemetry._projectsWithUpdatedBaselines.AsImmutable(); public readonly EditAndContinueCapabilities Capabilities = telemetry._capabilities; public readonly bool HadCompilationErrors = telemetry._hadCompilationErrors; public readonly bool HadRudeEdits = telemetry._hadRudeEdits; @@ -38,6 +39,7 @@ internal readonly struct Data(EditSessionTelemetry telemetry) private readonly HashSet<(ushort, ushort, Guid)> _rudeEdits = []; private readonly HashSet _emitErrorIds = []; private readonly HashSet _projectsWithValidDelta = []; + private readonly HashSet _projectsWithUpdatedBaselines = []; private bool _hadCompilationErrors; private bool _hadRudeEdits; @@ -58,6 +60,7 @@ public Data GetDataAndClear() _rudeEdits.Clear(); _emitErrorIds.Clear(); _projectsWithValidDelta.Clear(); + _projectsWithUpdatedBaselines.Clear(); _hadCompilationErrors = false; _hadRudeEdits = false; _hadValidChanges = false; @@ -145,4 +148,7 @@ public void LogRuntimeCapabilities(EditAndContinueCapabilities capabilities) public void LogCommitted() => _committed = true; + + public void LogUpdatedBaseline(Guid projectTelemetryId) + => _projectsWithUpdatedBaselines.Add(projectTelemetryId); } diff --git a/src/Features/Core/Portable/EditAndContinue/EmitSolutionUpdateResults.cs b/src/Features/Core/Portable/EditAndContinue/EmitSolutionUpdateResults.cs index 7ca941eea35b5..bd159511970a6 100644 --- a/src/Features/Core/Portable/EditAndContinue/EmitSolutionUpdateResults.cs +++ b/src/Features/Core/Portable/EditAndContinue/EmitSolutionUpdateResults.cs @@ -2,16 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Globalization; using System.Linq; -using System.Runtime.ExceptionServices; using System.Runtime.Serialization; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.Contracts.EditAndContinue; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.PooledObjects; @@ -37,6 +32,12 @@ internal readonly struct Data [DataMember] public required DiagnosticData? SyntaxError { get; init; } + [DataMember] + public required ImmutableArray ProjectsToRestart { get; init; } + + [DataMember] + public required ImmutableArray ProjectsToRebuild { get; init; } + internal ImmutableArray GetAllDiagnostics() { using var _ = ArrayBuilder.GetInstance(out var builder); @@ -82,12 +83,14 @@ internal ImmutableArray GetAllDiagnostics() ModuleUpdates = new ModuleUpdates(ModuleUpdateStatus.None, []), Diagnostics = [], RudeEdits = [], - SyntaxError = null + SyntaxError = null, + ProjectsToRestart = [], + ProjectsToRebuild = [], }; /// /// Solution snapshot to resolve diagnostics in. - /// Note that this might be a different snapshot from the one passed to , + /// Note that this might be a different snapshot from the one passed to EmitSolutionUpdateAsync, /// with source generator files refreshed. /// /// Null only for empty results. @@ -99,6 +102,9 @@ internal ImmutableArray GetAllDiagnostics() public required ImmutableArray RudeEdits { get; init; } public required Diagnostic? SyntaxError { get; init; } + public required ImmutableArray ProjectsToRestart { get; init; } + public required ImmutableArray ProjectsToRebuild { get; init; } + public Data Dehydrate() => Solution == null ? new() @@ -106,14 +112,18 @@ public Data Dehydrate() ModuleUpdates = ModuleUpdates, Diagnostics = [], RudeEdits = [], - SyntaxError = null + SyntaxError = null, + ProjectsToRestart = [], + ProjectsToRebuild = [], } : new() { ModuleUpdates = ModuleUpdates, Diagnostics = Diagnostics.ToDiagnosticData(Solution), RudeEdits = RudeEdits.ToDiagnosticData(Solution), - SyntaxError = GetSyntaxErrorData() + SyntaxError = GetSyntaxErrorData(), + ProjectsToRestart = ProjectsToRestart, + ProjectsToRebuild = ProjectsToRebuild, }; private DiagnosticData? GetSyntaxErrorData() @@ -128,25 +138,19 @@ public Data Dehydrate() return DiagnosticData.Create(SyntaxError, Solution.GetRequiredDocument(SyntaxError.Location.SourceTree)); } - private IEnumerable GetProjectsContainingBlockingRudeEdits(Solution solution) - => RudeEdits - .Where(static e => e.Diagnostics.HasBlockingRudeEdits()) - .Select(static e => e.ProjectId) - .Distinct() - .OrderBy(static id => id) - .Select(solution.GetRequiredProject); - /// /// Returns projects that need to be rebuilt and/or restarted due to blocking rude edits in order to apply changes. /// - /// Identifies projects that have been launched. + /// Identifies projects that have been launched. /// Running projects that have to be restarted. /// Projects whose source have been updated and need to be rebuilt. - public void GetProjectsToRebuildAndRestart( + internal static void GetProjectsToRebuildAndRestart( Solution solution, - Func isRunningProject, - ISet projectsToRestart, - ISet projectsToRebuild) + ModuleUpdates moduleUpdates, + IEnumerable rudeEdits, + IImmutableSet runningProjects, + out ImmutableArray projectsToRestart, + out ImmutableArray projectsToRebuild) { var graph = solution.GetProjectDependencyGraph(); @@ -157,23 +161,24 @@ public void GetProjectsToRebuildAndRestart( // We need to repeat this process until we find a fixed point. using var _1 = ArrayBuilder.GetInstance(out var traversalStack); - - projectsToRestart.Clear(); - projectsToRebuild.Clear(); + using var _2 = PooledHashSet.GetInstance(out var projectsToRestartBuilder); + using var _3 = ArrayBuilder.GetInstance(out var projectsToRebuildBuilder); foreach (var projectWithRudeEdit in GetProjectsContainingBlockingRudeEdits(solution)) { - if (AddImpactedRunningProjects(projectsToRestart, projectWithRudeEdit)) + if (AddImpactedRunningProjects(projectsToRestartBuilder, projectWithRudeEdit)) { - projectsToRebuild.Add(projectWithRudeEdit); + projectsToRebuildBuilder.Add(projectWithRudeEdit.Id); } } // At this point the restart set contains all running projects directly affected by rude edits. // Next, find projects that were successfully updated and affect running projects. - if (ModuleUpdates.Updates.IsEmpty || projectsToRestart.IsEmpty()) + if (moduleUpdates.Updates.IsEmpty || projectsToRestartBuilder.Count == 0) { + projectsToRestart = [.. projectsToRestartBuilder]; + projectsToRebuild = [.. projectsToRebuildBuilder]; return; } @@ -185,14 +190,15 @@ public void GetProjectsToRebuildAndRestart( // If an updated project does not affect reset set in a given iteration, it stays in the set // because it may affect reset set later on, after another running project is added to it. - using var _2 = PooledHashSet.GetInstance(out var updatedProjects); - using var _3 = ArrayBuilder.GetInstance(out var updatedProjectsToRemove); - foreach (var update in ModuleUpdates.Updates) + using var _4 = PooledHashSet.GetInstance(out var updatedProjects); + using var _5 = ArrayBuilder.GetInstance(out var updatedProjectsToRemove); + + foreach (var update in moduleUpdates.Updates) { updatedProjects.Add(solution.GetRequiredProject(update.ProjectId)); } - using var _4 = ArrayBuilder.GetInstance(out var impactedProjects); + using var _6 = ArrayBuilder.GetInstance(out var impactedProjects); while (true) { @@ -201,11 +207,11 @@ public void GetProjectsToRebuildAndRestart( foreach (var updatedProject in updatedProjects) { if (AddImpactedRunningProjects(impactedProjects, updatedProject) && - impactedProjects.Any(projectsToRestart.Contains)) + impactedProjects.Any(projectsToRestartBuilder.Contains)) { - projectsToRestart.AddRange(impactedProjects); + projectsToRestartBuilder.AddRange(impactedProjects); updatedProjectsToRemove.Add(updatedProject); - projectsToRebuild.Add(updatedProject); + projectsToRebuildBuilder.Add(updatedProject.Id); } impactedProjects.Clear(); @@ -221,9 +227,11 @@ public void GetProjectsToRebuildAndRestart( updatedProjectsToRemove.Clear(); } + projectsToRestart = [.. projectsToRestartBuilder]; + projectsToRebuild = [.. projectsToRebuildBuilder]; return; - bool AddImpactedRunningProjects(ICollection impactedProjects, Project initialProject) + bool AddImpactedRunningProjects(ICollection impactedProjects, Project initialProject) { Debug.Assert(traversalStack.Count == 0); traversalStack.Push(initialProject); @@ -233,9 +241,9 @@ bool AddImpactedRunningProjects(ICollection impactedProjects, Project i while (traversalStack.Count > 0) { var project = traversalStack.Pop(); - if (isRunningProject(project)) + if (runningProjects.Contains(project.Id)) { - impactedProjects.Add(project); + impactedProjects.Add(project.Id); added = true; } @@ -247,6 +255,14 @@ bool AddImpactedRunningProjects(ICollection impactedProjects, Project i return added; } + + IEnumerable GetProjectsContainingBlockingRudeEdits(Solution solution) + => rudeEdits + .Where(static e => e.Diagnostics.HasBlockingRudeEdits()) + .Select(static e => e.ProjectId) + .Distinct() + .OrderBy(static id => id) + .Select(solution.GetRequiredProject); } public ImmutableArray GetAllDiagnostics() diff --git a/src/Features/Core/Portable/EditAndContinue/IEditAndContinueService.cs b/src/Features/Core/Portable/EditAndContinue/IEditAndContinueService.cs index 32c1360a2d976..cc39e13802a7f 100644 --- a/src/Features/Core/Portable/EditAndContinue/IEditAndContinueService.cs +++ b/src/Features/Core/Portable/EditAndContinue/IEditAndContinueService.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -19,10 +20,11 @@ internal interface IEditAndContinueWorkspaceService : IWorkspaceService internal interface IEditAndContinueService { ValueTask> GetDocumentDiagnosticsAsync(Document document, ActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken); - ValueTask EmitSolutionUpdateAsync(DebuggingSessionId sessionId, Solution solution, ActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken); + ValueTask EmitSolutionUpdateAsync(DebuggingSessionId sessionId, Solution solution, IImmutableSet runningProjects, ActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken); void CommitSolutionUpdate(DebuggingSessionId sessionId); void DiscardSolutionUpdate(DebuggingSessionId sessionId); + void UpdateBaselines(DebuggingSessionId sessionId, Solution solution, ImmutableArray rebuiltProjects); ValueTask StartDebuggingSessionAsync(Solution solution, IManagedHotReloadService debuggerService, IPdbMatchingSourceTextProvider sourceTextProvider, ImmutableArray captureMatchingDocuments, bool captureAllMatchingDocuments, bool reportDiagnostics, CancellationToken cancellationToken); void BreakStateOrCapabilitiesChanged(DebuggingSessionId sessionId, bool? inBreakState); diff --git a/src/Features/Core/Portable/EditAndContinue/Remote/IRemoteEditAndContinueService.cs b/src/Features/Core/Portable/EditAndContinue/Remote/IRemoteEditAndContinueService.cs index be246e9a748e4..7b60868698964 100644 --- a/src/Features/Core/Portable/EditAndContinue/Remote/IRemoteEditAndContinueService.cs +++ b/src/Features/Core/Portable/EditAndContinue/Remote/IRemoteEditAndContinueService.cs @@ -27,13 +27,14 @@ internal interface ICallback } ValueTask> GetDocumentDiagnosticsAsync(Checksum solutionChecksum, RemoteServiceCallbackId callbackId, DocumentId documentId, CancellationToken cancellationToken); - ValueTask EmitSolutionUpdateAsync(Checksum solutionChecksum, RemoteServiceCallbackId callbackId, DebuggingSessionId sessionId, CancellationToken cancellationToken); + ValueTask EmitSolutionUpdateAsync(Checksum solutionChecksum, RemoteServiceCallbackId callbackId, DebuggingSessionId sessionId, IImmutableSet runningProjects, CancellationToken cancellationToken); /// /// Returns ids of documents for which diagnostics need to be refreshed in-proc. /// ValueTask CommitSolutionUpdateAsync(DebuggingSessionId sessionId, CancellationToken cancellationToken); ValueTask DiscardSolutionUpdateAsync(DebuggingSessionId sessionId, CancellationToken cancellationToken); + ValueTask UpdateBaselinesAsync(Checksum solutionInfo, DebuggingSessionId sessionId, ImmutableArray rebuiltProjects, CancellationToken cancellationToken); ValueTask StartDebuggingSessionAsync(Checksum solutionChecksum, RemoteServiceCallbackId callbackId, ImmutableArray captureMatchingDocuments, bool captureAllMatchingDocuments, bool reportDiagnostics, CancellationToken cancellationToken); diff --git a/src/Features/Core/Portable/EditAndContinue/Remote/RemoteDebuggingSessionProxy.cs b/src/Features/Core/Portable/EditAndContinue/Remote/RemoteDebuggingSessionProxy.cs index dfb2d42af2407..376c21819f30c 100644 --- a/src/Features/Core/Portable/EditAndContinue/Remote/RemoteDebuggingSessionProxy.cs +++ b/src/Features/Core/Portable/EditAndContinue/Remote/RemoteDebuggingSessionProxy.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -55,6 +56,7 @@ await client.TryInvokeAsync( public async ValueTask EmitSolutionUpdateAsync( Solution solution, + IImmutableSet runningProjects, ActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken) { @@ -63,12 +65,12 @@ await client.TryInvokeAsync( var client = await RemoteHostClient.TryGetClientAsync(services, cancellationToken).ConfigureAwait(false); if (client == null) { - return (await GetLocalService().EmitSolutionUpdateAsync(sessionId, solution, activeStatementSpanProvider, cancellationToken).ConfigureAwait(false)).Dehydrate(); + return (await GetLocalService().EmitSolutionUpdateAsync(sessionId, solution, runningProjects, activeStatementSpanProvider, cancellationToken).ConfigureAwait(false)).Dehydrate(); } var result = await client.TryInvokeAsync( solution, - (service, solutionInfo, callbackId, cancellationToken) => service.EmitSolutionUpdateAsync(solutionInfo, callbackId, sessionId, cancellationToken), + (service, solutionInfo, callbackId, cancellationToken) => service.EmitSolutionUpdateAsync(solutionInfo, callbackId, sessionId, runningProjects, cancellationToken), callbackTarget: new ActiveStatementSpanProviderCallback(activeStatementSpanProvider), cancellationToken).ConfigureAwait(false); @@ -78,6 +80,8 @@ await client.TryInvokeAsync( Diagnostics = [], RudeEdits = [], SyntaxError = null, + ProjectsToRebuild = [], + ProjectsToRestart = [], }; } catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) @@ -88,6 +92,8 @@ await client.TryInvokeAsync( Diagnostics = GetInternalErrorDiagnosticData(solution, e), RudeEdits = [], SyntaxError = null, + ProjectsToRebuild = [], + ProjectsToRestart = [], }; } } @@ -133,6 +139,22 @@ await client.TryInvokeAsync( cancellationToken).ConfigureAwait(false); } + public async ValueTask UpdateBaselinesAsync(Solution solution, ImmutableArray rebuiltProjects, CancellationToken cancellationToken) + { + var client = await RemoteHostClient.TryGetClientAsync(services, cancellationToken).ConfigureAwait(false); + if (client == null) + { + GetLocalService().UpdateBaselines(sessionId, solution, rebuiltProjects); + } + else + { + var result = await client.TryInvokeAsync( + solution, + (service, solutionInfo, cancellationToken) => service.UpdateBaselinesAsync(solutionInfo, sessionId, rebuiltProjects, cancellationToken), + cancellationToken).ConfigureAwait(false); + } + } + public async ValueTask>> GetBaseActiveStatementSpansAsync(Solution solution, ImmutableArray documentIds, CancellationToken cancellationToken) { var client = await RemoteHostClient.TryGetClientAsync(services, cancellationToken).ConfigureAwait(false); diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingHotReloadService.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingHotReloadService.cs index d09a0aec08776..dc3c06712f871 100644 --- a/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingHotReloadService.cs +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingHotReloadService.cs @@ -95,7 +95,7 @@ public async Task StartSessionAsync(Solution solution, ImmutableArray ca Contract.ThrowIfFalse(sessionId != default, "Session has not started"); var results = await _encService - .EmitSolutionUpdateAsync(sessionId, solution, s_solutionActiveStatementSpanProvider, cancellationToken) + .EmitSolutionUpdateAsync(sessionId, solution, runningProjects: [], s_solutionActiveStatementSpanProvider, cancellationToken) .ConfigureAwait(false); if (results.ModuleUpdates.Status == ModuleUpdateStatus.Ready) diff --git a/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs b/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs index 323402fe384fd..2173339e29bba 100644 --- a/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs +++ b/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs @@ -90,12 +90,24 @@ public readonly struct Updates( /// /// Running projects that need to be restarted due to rude edits in order to apply changes. /// + [Obsolete("Use ProjectIdsToRestart")] public IReadOnlySet ProjectsToRestart { get; } = projectsToRestart; /// /// Projects with changes that need to be rebuilt in order to apply changes. /// + [Obsolete("Use ProjectIdsToRebuild")] public IReadOnlySet ProjectsToRebuild { get; } = projectsToRebuild; + + /// + /// Running projects that need to be restarted due to rude edits in order to apply changes. + /// + public ImmutableArray ProjectIdsToRestart { get; } = projectsToRestart.SelectAsArray(p => p.Id); + + /// + /// Projects with changes that need to be rebuilt in order to apply changes. + /// + public ImmutableArray ProjectIdsToRebuild { get; } = projectsToRebuild.SelectAsArray(p => p.Id); } private static readonly ActiveStatementSpanProvider s_solutionActiveStatementSpanProvider = @@ -151,27 +163,32 @@ public void CapabilitiesChanged() _encService.BreakStateOrCapabilitiesChanged(GetDebuggingSession(), inBreakState: null); } + [Obsolete] public async Task<(ImmutableArray updates, ImmutableArray diagnostics)> EmitSolutionUpdateAsync(Solution solution, CancellationToken cancellationToken) { var result = await GetUpdatesAsync(solution, isRunningProject: static _ => false, cancellationToken).ConfigureAwait(false); return (result.ProjectUpdates, result.Diagnostics); } + [Obsolete] + public Task GetUpdatesAsync(Solution solution, Func isRunningProject, CancellationToken cancellationToken) + => GetUpdatesAsync(solution, solution.Projects.Where(isRunningProject).Select(static p => p.Id).ToImmutableHashSet(), cancellationToken); + /// /// Emits updates for all projects that differ between the given snapshot and the one given to the previous successful call or /// the one passed to for the first invocation. /// /// Solution snapshot. - /// Identifies projects that launched a process. + /// Identifies projects that launched a process. /// Cancellation token. /// /// Updates (one for each changed project) and Rude Edit diagnostics. Does not include syntax or semantic diagnostics. /// - public async Task GetUpdatesAsync(Solution solution, Func isRunningProject, CancellationToken cancellationToken) + public async Task GetUpdatesAsync(Solution solution, IImmutableSet runningProjects, CancellationToken cancellationToken) { var sessionId = GetDebuggingSession(); - var results = await _encService.EmitSolutionUpdateAsync(sessionId, solution, s_solutionActiveStatementSpanProvider, cancellationToken).ConfigureAwait(false); + var results = await _encService.EmitSolutionUpdateAsync(sessionId, solution, runningProjects, s_solutionActiveStatementSpanProvider, cancellationToken).ConfigureAwait(false); if (results.ModuleUpdates.Status == ModuleUpdateStatus.Ready) { @@ -180,14 +197,10 @@ public async Task GetUpdatesAsync(Solution solution, Func(); - var projectsToRebuild = new HashSet(); - results.GetProjectsToRebuildAndRestart(solution, isRunningProject, projectsToRestart, projectsToRebuild); - var projectUpdates = from update in results.ModuleUpdates.Updates let project = solution.GetRequiredProject(update.ProjectId) - where !projectsToRestart.Contains(project) + where !results.ProjectsToRestart.Contains(project.Id) select new Update( update.Module, project.Id, @@ -197,7 +210,18 @@ from update in results.ModuleUpdates.Updates update.UpdatedTypes, update.RequiredCapabilities); - return new Updates(results.ModuleUpdates.Status, diagnostics, projectUpdates.ToImmutableArray(), projectsToRestart, projectsToRebuild); + return new Updates( + results.ModuleUpdates.Status, + diagnostics, + projectUpdates.ToImmutableArray(), + results.ProjectsToRestart.Select(solution.GetRequiredProject).ToImmutableHashSet(), + results.ProjectsToRebuild.Select(solution.GetRequiredProject).ToImmutableHashSet()); + } + + public void UpdateBaselines(Solution solution, ImmutableArray projectIds) + { + var sessionId = GetDebuggingSession(); + _encService.UpdateBaselines(sessionId, solution, projectIds); } public void EndSession() diff --git a/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index c185b9d7660eb..cbe55ba773831 100644 --- a/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs +++ b/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs @@ -537,19 +537,19 @@ public async Task ErrorReadingModuleFile(bool breakMode) if (breakMode) { - AssertEx.Equal( + AssertEx.SequenceEqual( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=1|EmptySessionCount=0|HotReloadSessionCount=0|EmptyHotReloadSessionCount=3", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=1|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges={6A6F7270-0000-4000-8000-000000000000}", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=1|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges={6A6F7270-0000-4000-8000-000000000000}|ProjectIdsWithUpdatedBaselines=", "Debugging_EncSession_EditSession_EmitDeltaErrorId: SessionId=1|EditSessionId=2|ErrorId=ENC1001" ], _telemetryLog); } else { - AssertEx.Equal( + AssertEx.SequenceEqual( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=0|EmptySessionCount=0|HotReloadSessionCount=1|EmptyHotReloadSessionCount=1", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=1|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges={6A6F7270-0000-4000-8000-000000000000}", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=1|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges={6A6F7270-0000-4000-8000-000000000000}|ProjectIdsWithUpdatedBaselines=", "Debugging_EncSession_EditSession_EmitDeltaErrorId: SessionId=1|EditSessionId=2|ErrorId=ENC1001" ], _telemetryLog); } @@ -659,7 +659,7 @@ public async Task ErrorReadingSourceFile() AssertEx.Equal( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=1|EmptySessionCount=0|HotReloadSessionCount=0|EmptyHotReloadSessionCount=1", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=" + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines=" ], _telemetryLog); } @@ -717,7 +717,7 @@ public async Task FileAdded(bool breakMode) AssertEx.Equal( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=1|EmptySessionCount=0|HotReloadSessionCount=0|EmptyHotReloadSessionCount=2", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=" + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines=" ], _telemetryLog); } else @@ -725,7 +725,7 @@ public async Task FileAdded(bool breakMode) AssertEx.Equal( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=0|EmptySessionCount=0|HotReloadSessionCount=1|EmptyHotReloadSessionCount=0", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges=" + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines=" ], _telemetryLog); } } @@ -893,7 +893,7 @@ void M() AssertEx.Equal( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=1|EmptySessionCount=0|HotReloadSessionCount=0|EmptyHotReloadSessionCount=1", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=1|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=1|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines=", "Debugging_EncSession_EditSession_EmitDeltaErrorId: SessionId=1|EditSessionId=2|ErrorId=ENC2016" ], _telemetryLog); } @@ -1002,7 +1002,7 @@ public async Task RudeEdits(bool breakMode) AssertEx.Equal( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=1|EmptySessionCount=0|HotReloadSessionCount=0|EmptyHotReloadSessionCount=2", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=1|EmitDeltaErrorIdCount=0|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=1|EmitDeltaErrorIdCount=0|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines=", "Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=2|RudeEditKind=110|RudeEditSyntaxKind=8910|RudeEditBlocking=True|RudeEditProjectId={6A6F7270-0000-4000-8000-000000000000}" ], _telemetryLog); } @@ -1011,7 +1011,7 @@ public async Task RudeEdits(bool breakMode) AssertEx.Equal( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=0|EmptySessionCount=0|HotReloadSessionCount=1|EmptyHotReloadSessionCount=0", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=1|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges=", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=1|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines=", "Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=2|RudeEditKind=110|RudeEditSyntaxKind=8910|RudeEditBlocking=True|RudeEditProjectId={6A6F7270-0000-4000-8000-000000000000}" ], _telemetryLog); } @@ -1214,7 +1214,7 @@ public async Task RudeEdits_DocumentOutOfSync(bool breakMode) AssertEx.Equal( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=1|EmptySessionCount=0|HotReloadSessionCount=0|EmptyHotReloadSessionCount=2", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=1|EmitDeltaErrorIdCount=0|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=1|EmitDeltaErrorIdCount=0|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines=", "Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=2|RudeEditKind=110|RudeEditSyntaxKind=8875|RudeEditBlocking=True|RudeEditProjectId={6A6F7270-0000-4000-8000-000000000000}" ], _telemetryLog); } @@ -1223,7 +1223,7 @@ public async Task RudeEdits_DocumentOutOfSync(bool breakMode) AssertEx.Equal( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=0|EmptySessionCount=0|HotReloadSessionCount=1|EmptyHotReloadSessionCount=0", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=1|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges=", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=1|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines=", "Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=2|RudeEditKind=110|RudeEditSyntaxKind=8875|RudeEditBlocking=True|RudeEditProjectId={6A6F7270-0000-4000-8000-000000000000}" ], _telemetryLog); } @@ -1328,6 +1328,92 @@ public async Task RudeEdits_DelayLoadedModule() EndDebuggingSession(debuggingSession); } + [Fact] + public async Task RudeEdits_UpdateBaseline() + { + var source1 = "abstract class C { }"; + var source2 = "abstract class C { void F() {} }"; + var source3 = "abstract class C { void F() {} public abstract void G(); }"; + var source4 = "abstract class C { void F() {} public abstract void G(); void H() {} }"; + + var dir = Temp.CreateDirectory(); + var sourceFile = dir.CreateFile("a.cs").WriteAllText(source1, Encoding.UTF8); + + using var _ = CreateWorkspace(out var solution, out var service); + (solution, var document) = AddDefaultTestProject(solution, source1); + + var documentId = document.Id; + var projectId = document.Project.Id; + var moduleId = EmitAndLoadLibraryToDebuggee(source1, sourceFilePath: sourceFile.Path); + + var debuggingSession = await StartDebuggingSessionAsync(service, solution); + + // change the source (valid edit): + solution = solution.WithDocumentText(documentId, CreateText(source2)); + + var result = await debuggingSession.EmitSolutionUpdateAsync(solution, runningProjects: [projectId], s_noActiveSpans, CancellationToken.None); + Assert.Equal(ModuleUpdateStatus.Ready, result.ModuleUpdates.Status); + Assert.Empty(result.ProjectsToRebuild); + Assert.Empty(result.ProjectsToRestart); + + // baseline should be present: + var readers = debuggingSession.GetTestAccessor().GetBaselineModuleReaders(); + Assert.NotEmpty(readers); + Assert.True(debuggingSession.GetTestAccessor().HasProjectEmitBaseline(projectId)); + + // change the source (rude edit): + solution = solution.WithDocumentText(documentId, CreateText(source3)); + + // Rude Edits reported: + var diagnostics = await service.GetDocumentDiagnosticsAsync(solution.GetRequiredDocument(documentId), s_noActiveSpans, CancellationToken.None); + AssertEx.Equal( + ["ENC0023: " + string.Format(FeaturesResources.Adding_an_abstract_0_or_overriding_an_inherited_0_requires_restarting_the_application, FeaturesResources.method)], + diagnostics.Select(d => $"{d.Id}: {d.GetMessage()}")); + + // validate solution update status and emit: + result = await debuggingSession.EmitSolutionUpdateAsync(solution, runningProjects: [projectId], s_noActiveSpans, CancellationToken.None); + Assert.Equal(ModuleUpdateStatus.RestartRequired, result.ModuleUpdates.Status); + AssertEx.Equal([projectId], result.ProjectsToRebuild); + AssertEx.Equal([projectId], result.ProjectsToRestart); + + DiscardSolutionUpdate(debuggingSession); + + // restart: + _debuggerService.LoadedModules.Remove(moduleId); + moduleId = EmitAndLoadLibraryToDebuggee(source3, sourceFilePath: sourceFile.Path); + debuggingSession.UpdateBaselines(solution, result.ProjectsToRebuild); + + // baseline should be removed: + Assert.False(debuggingSession.GetTestAccessor().HasProjectEmitBaseline(projectId)); + + // all readers created for the module must be disposed, so we can rebuild the module: + VerifyReadersDisposed(readers); + + // no rude edits reported: + Assert.Empty(await service.GetDocumentDiagnosticsAsync(solution.GetRequiredDocument(documentId), s_noActiveSpans, CancellationToken.None)); + + // change the source (valid edit): + solution = solution.WithDocumentText(documentId, CreateText(source4)); + + // no rude edits: + Assert.Empty(await service.GetDocumentDiagnosticsAsync(solution.GetRequiredDocument(documentId), s_noActiveSpans, CancellationToken.None)); + + // apply valid change: + result = await debuggingSession.EmitSolutionUpdateAsync(solution, runningProjects: [projectId], s_noActiveSpans, CancellationToken.None); + Assert.Equal(ModuleUpdateStatus.Ready, result.ModuleUpdates.Status); + CommitSolutionUpdate(debuggingSession); + + EndDebuggingSession(debuggingSession); + + AssertEx.SequenceEqual( + [ + "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=0|EmptySessionCount=0|HotReloadSessionCount=2|EmptyHotReloadSessionCount=1", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=1|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines={6A6F7270-0000-4000-8000-000000000000}", + "Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=2|RudeEditKind=23|RudeEditSyntaxKind=8875|RudeEditBlocking=True|RudeEditProjectId={6A6F7270-0000-4000-8000-000000000000}", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=3|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges={6A6F7270-0000-4000-8000-000000000000}|ProjectIdsWithUpdatedBaselines=" + ], _telemetryLog); + } + [Fact] public async Task SyntaxError() { @@ -1364,7 +1450,7 @@ public async Task SyntaxError() AssertEx.Equal( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=1|EmptySessionCount=0|HotReloadSessionCount=0|EmptyHotReloadSessionCount=1", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=True|HadRudeEdits=False|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=" + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=True|HadRudeEdits=False|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines=" ], _telemetryLog); } @@ -1406,10 +1492,10 @@ public async Task SemanticError() AssertEx.SetEqual([moduleId], debuggingSession.GetTestAccessor().GetModulesPreparedForUpdate()); - AssertEx.Equal( + AssertEx.SequenceEqual( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=1|EmptySessionCount=0|HotReloadSessionCount=0|EmptyHotReloadSessionCount=1", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=1|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=1|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines=", "Debugging_EncSession_EditSession_EmitDeltaErrorId: SessionId=1|EditSessionId=2|ErrorId=CS0266" ], _telemetryLog); } @@ -2057,7 +2143,7 @@ public async Task ValidSignificantChange_EmitError() AssertEx.Equal( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=1|EmptySessionCount=0|HotReloadSessionCount=0|EmptyHotReloadSessionCount=1", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=1|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=1|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines=", "Debugging_EncSession_EditSession_EmitDeltaErrorId: SessionId=1|EditSessionId=2|ErrorId=CS8055" ], _telemetryLog); } @@ -2457,18 +2543,18 @@ void ValidateDelta(ManagedHotReloadUpdate delta) if (breakMode) { - AssertEx.Equal( + AssertEx.SequenceEqual( [ $"Debugging_EncSession: SolutionSessionId={{00000000-AAAA-AAAA-AAAA-000000000000}}|SessionId=1|SessionCount=1|EmptySessionCount=0|HotReloadSessionCount=0|EmptyHotReloadSessionCount={(commitUpdate ? 3 : 2)}", - $"Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges={(commitUpdate ? "{6A6F7270-0000-4000-8000-000000000000}" : "")}", + $"Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges={(commitUpdate ? "{6A6F7270-0000-4000-8000-000000000000}" : "")}|ProjectIdsWithUpdatedBaselines=", ], _telemetryLog); } else { - AssertEx.Equal( + AssertEx.SequenceEqual( [ $"Debugging_EncSession: SolutionSessionId={{00000000-AAAA-AAAA-AAAA-000000000000}}|SessionId=1|SessionCount=0|EmptySessionCount=0|HotReloadSessionCount=1|EmptyHotReloadSessionCount={(commitUpdate ? 1 : 0)}", - $"Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges={(commitUpdate ? "{6A6F7270-0000-4000-8000-000000000000}" : "")}" + $"Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges={(commitUpdate ? "{6A6F7270-0000-4000-8000-000000000000}" : "")}|ProjectIdsWithUpdatedBaselines=" ], _telemetryLog); } } @@ -2741,7 +2827,7 @@ class C { int Y => 2; } solution = solution.WithDocumentText(document1.Id, CreateText(sourceV2)); // validate solution update status and emit: - var results = (await debuggingSession.EmitSolutionUpdateAsync(solution, s_noActiveSpans, CancellationToken.None).ConfigureAwait(false)).Dehydrate(); + var results = (await debuggingSession.EmitSolutionUpdateAsync(solution, runningProjects: [], s_noActiveSpans, CancellationToken.None).ConfigureAwait(false)).Dehydrate(); var diagnostics = results.GetAllDiagnostics(); var generatedFilePath = Path.Combine( @@ -3258,7 +3344,7 @@ public async Task ValidSignificantChange_BaselineCreationFailed_AssemblyReadErro AssertEx.Equal( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=1|EmptySessionCount=0|HotReloadSessionCount=0|EmptyHotReloadSessionCount=1", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=1|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=1|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines=", "Debugging_EncSession_EditSession_EmitDeltaErrorId: SessionId=1|EditSessionId=2|ErrorId=ENC1001" ], _telemetryLog); } @@ -3939,6 +4025,7 @@ static void F() ], InspectNonRemappableRegions(debuggingSession.EditSession.NonRemappableRegions)); ExitBreakState(debuggingSession); + EndDebuggingSession(debuggingSession); } /// @@ -4058,6 +4145,7 @@ static void F() ], InspectNonRemappableRegions(debuggingSession.EditSession.NonRemappableRegions)); ExitBreakState(debuggingSession); + EndDebuggingSession(debuggingSession); } /// @@ -4135,6 +4223,7 @@ static void F() ], spans); ExitBreakState(debuggingSession); + EndDebuggingSession(debuggingSession); } [Fact] @@ -4175,14 +4264,14 @@ public async Task MultiSession() var solution1 = solution.WithDocumentText(documentIdA, CreateText("class C { void M() { System.Console.WriteLine(" + i + "); } }")); - var result1 = await encService.EmitSolutionUpdateAsync(sessionId, solution1, s_noActiveSpans, CancellationToken.None); + var result1 = await encService.EmitSolutionUpdateAsync(sessionId, solution1, runningProjects: [], s_noActiveSpans, CancellationToken.None); Assert.Empty(result1.Diagnostics); Assert.Equal(1, result1.ModuleUpdates.Updates.Length); encService.DiscardSolutionUpdate(sessionId); var solution2 = solution1.WithDocumentText(documentIdA, CreateText(source3)); - var result2 = await encService.EmitSolutionUpdateAsync(sessionId, solution2, s_noActiveSpans, CancellationToken.None); + var result2 = await encService.EmitSolutionUpdateAsync(sessionId, solution2, runningProjects: [], s_noActiveSpans, CancellationToken.None); Assert.Equal("CS0103", result2.Diagnostics.Single().Diagnostics.Single().Id); Assert.Empty(result2.ModuleUpdates.Updates); @@ -4205,7 +4294,7 @@ public async Task Disposal() EndDebuggingSession(debuggingSession); // The folling methods shall not be called after the debugging session ended. - await Assert.ThrowsAsync(async () => await debuggingSession.EmitSolutionUpdateAsync(solution, s_noActiveSpans, CancellationToken.None)); + await Assert.ThrowsAsync(async () => await debuggingSession.EmitSolutionUpdateAsync(solution, runningProjects: [], s_noActiveSpans, CancellationToken.None)); Assert.Throws(() => debuggingSession.BreakStateOrCapabilitiesChanged(inBreakState: true)); Assert.Throws(() => debuggingSession.DiscardSolutionUpdate()); Assert.Throws(() => debuggingSession.CommitSolutionUpdate()); diff --git a/src/Features/Test/EditAndContinue/EmitSolutionUpdateResultsTests.cs b/src/Features/Test/EditAndContinue/EmitSolutionUpdateResultsTests.cs index 7a79ae129f31d..9f52adbc0541c 100644 --- a/src/Features/Test/EditAndContinue/EmitSolutionUpdateResultsTests.cs +++ b/src/Features/Test/EditAndContinue/EmitSolutionUpdateResultsTests.cs @@ -54,6 +54,8 @@ private static EmitSolutionUpdateResults CreateMockResults(Solution solution, IE RudeEdits = [.. rudeEdits.Select(id => new ProjectDiagnostics(id, [Diagnostic.Create(EditAndContinueDiagnosticDescriptors.GetDescriptor(RudeEditKind.InternalError), location: null)]))], Diagnostics = [], SyntaxError = null, + ProjectsToRebuild = [], + ProjectsToRestart = [], }; [Fact] @@ -135,7 +137,9 @@ public async Task GetHotReloadDiagnostics() Diagnostics = diagnostics, RudeEdits = rudeEdits, SyntaxError = syntaxError, - ModuleUpdates = new ModuleUpdates(ModuleUpdateStatus.Blocked, Updates: []) + ModuleUpdates = new ModuleUpdates(ModuleUpdateStatus.Blocked, Updates: []), + ProjectsToRebuild = [], + ProjectsToRestart = [], }; var actual = data.GetAllDiagnostics(); @@ -161,13 +165,16 @@ public void RunningProjects_Updates() .AddTestProject("A", out var a).AddProjectReferences([new(c)]).Solution .AddTestProject("B", out var b).AddProjectReferences([new(c), new(d)]).Solution; - var runningProjects = new[] { a, b }; + var runningProjects = new[] { a, b }.ToImmutableHashSet(); var results = CreateMockResults(solution, updates: [c, d], rudeEdits: []); - var projectsToRestart = new HashSet(); - var projectsToRebuild = new HashSet(); - - results.GetProjectsToRebuildAndRestart(solution, p => runningProjects.Contains(p.Id), projectsToRestart, projectsToRebuild); + EmitSolutionUpdateResults.GetProjectsToRebuildAndRestart( + solution, + results.ModuleUpdates, + results.RudeEdits, + runningProjects, + out var projectsToRestart, + out var projectsToRebuild); Assert.Empty(projectsToRestart); Assert.Empty(projectsToRebuild); @@ -184,19 +191,22 @@ public void RunningProjects_RudeEdits() .AddTestProject("A", out var a).AddProjectReferences([new(c)]).Solution .AddTestProject("B", out var b).AddProjectReferences([new(c), new(d)]).Solution; - var runningProjects = new[] { a, b }; + var runningProjects = new[] { a, b }.ToImmutableHashSet(); var results = CreateMockResults(solution, updates: [], rudeEdits: [d]); - var projectsToRestart = new HashSet(); - var projectsToRebuild = new HashSet(); - - results.GetProjectsToRebuildAndRestart(solution, p => runningProjects.Contains(p.Id), projectsToRestart, projectsToRebuild); + EmitSolutionUpdateResults.GetProjectsToRebuildAndRestart( + solution, + results.ModuleUpdates, + results.RudeEdits, + runningProjects, + out var projectsToRestart, + out var projectsToRebuild); // D has rude edit ==> B has to restart - AssertEx.SetEqual([b], projectsToRestart.Select(p => p.Id)); + AssertEx.SetEqual([b], projectsToRestart); // D has rude edit: - AssertEx.SetEqual([d], projectsToRebuild.Select(p => p.Id)); + AssertEx.SetEqual([d], projectsToRebuild); } [Fact] @@ -210,13 +220,16 @@ public void RunningProjects_RudeEdits_NotImpactingRunningProjects() .AddTestProject("A", out var a).AddProjectReferences([new(c)]).Solution .AddTestProject("B", out var b).AddProjectReferences([new(c), new(d)]).Solution; - var runningProjects = new[] { a }; + var runningProjects = new[] { a }.ToImmutableHashSet(); var results = CreateMockResults(solution, updates: [], rudeEdits: [d]); - var projectsToRestart = new HashSet(); - var projectsToRebuild = new HashSet(); - - results.GetProjectsToRebuildAndRestart(solution, p => runningProjects.Contains(p.Id), projectsToRestart, projectsToRebuild); + EmitSolutionUpdateResults.GetProjectsToRebuildAndRestart( + solution, + results.ModuleUpdates, + results.RudeEdits, + runningProjects, + out var projectsToRestart, + out var projectsToRebuild); Assert.Empty(projectsToRestart); Assert.Empty(projectsToRebuild); @@ -233,20 +246,23 @@ public void RunningProjects_RudeEditAndUpdate_Dependent() .AddTestProject("A", out var a).AddProjectReferences([new(c)]).Solution .AddTestProject("B", out var b).AddProjectReferences([new(c), new(d)]).Solution; - var runningProjects = new[] { a, b }; + var runningProjects = new[] { a, b }.ToImmutableHashSet(); var results = CreateMockResults(solution, updates: [c], rudeEdits: [d]); - var projectsToRestart = new HashSet(); - var projectsToRebuild = new HashSet(); - - results.GetProjectsToRebuildAndRestart(solution, p => runningProjects.Contains(p.Id), projectsToRestart, projectsToRebuild); + EmitSolutionUpdateResults.GetProjectsToRebuildAndRestart( + solution, + results.ModuleUpdates, + results.RudeEdits, + runningProjects, + out var projectsToRestart, + out var projectsToRebuild); // D has rude edit => B has to restart // C has update, B -> C and A -> C ==> A has to restart - AssertEx.SetEqual([a, b], projectsToRestart.Select(p => p.Id)); + AssertEx.SetEqual([a, b], projectsToRestart); // D has rude edit, C has update that impacts restart set: - AssertEx.SetEqual([c, d], projectsToRebuild.Select(p => p.Id)); + AssertEx.SetEqual([c, d], projectsToRebuild); } [Fact] @@ -260,19 +276,22 @@ public void RunningProjects_RudeEditAndUpdate_Independent() .AddTestProject("A", out var a).AddProjectReferences([new(c)]).Solution .AddTestProject("B", out var b).AddProjectReferences([new(d)]).Solution; - var runningProjects = new[] { a, b }; + var runningProjects = new[] { a, b }.ToImmutableHashSet(); var results = CreateMockResults(solution, updates: [c], rudeEdits: [d]); - var projectsToRestart = new HashSet(); - var projectsToRebuild = new HashSet(); - - results.GetProjectsToRebuildAndRestart(solution, p => runningProjects.Contains(p.Id), projectsToRestart, projectsToRebuild); + EmitSolutionUpdateResults.GetProjectsToRebuildAndRestart( + solution, + results.ModuleUpdates, + results.RudeEdits, + runningProjects, + out var projectsToRestart, + out var projectsToRebuild); // D has rude edit => B has to restart - AssertEx.SetEqual([b], projectsToRestart.Select(p => p.Id)); + AssertEx.SetEqual([b], projectsToRestart); // D has rude edit, C has update that does not impacts restart set: - AssertEx.SetEqual([d], projectsToRebuild.Select(p => p.Id)); + AssertEx.SetEqual([d], projectsToRebuild); } [Theory] @@ -291,16 +310,19 @@ public void RunningProjects_RudeEditAndUpdate_Chain(bool reverse) .AddTestProject("R3", out var r3).AddProjectReferences([new(p3), new(p4)]).Solution .AddTestProject("R4", out var r4).AddProjectReferences([new(p4)]).Solution; - var runningProjects = new[] { r1, r2, r3, r4 }; + var runningProjects = new[] { r1, r2, r3, r4 }.ToImmutableHashSet(); var results = CreateMockResults(solution, updates: reverse ? [p4, p3, p2] : [p2, p3, p4], rudeEdits: [p1]); - var projectsToRestart = new HashSet(); - var projectsToRebuild = new HashSet(); - - results.GetProjectsToRebuildAndRestart(solution, p => runningProjects.Contains(p.Id), projectsToRestart, projectsToRebuild); + EmitSolutionUpdateResults.GetProjectsToRebuildAndRestart( + solution, + results.ModuleUpdates, + results.RudeEdits, + runningProjects, + out var projectsToRestart, + out var projectsToRebuild); - AssertEx.SetEqual([r1, r2, r3, r4], projectsToRestart.Select(p => p.Id)); - AssertEx.SetEqual([p1, p2, p3, p4], projectsToRebuild.Select(p => p.Id)); + AssertEx.SetEqual([r1, r2, r3, r4], projectsToRestart); + AssertEx.SetEqual([p1, p2, p3, p4], projectsToRebuild); } } } diff --git a/src/Features/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs b/src/Features/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs index 472aef5156b4f..ab25d52016512 100644 --- a/src/Features/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs +++ b/src/Features/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs @@ -173,11 +173,12 @@ await localWorkspace.ChangeSolutionAsync(localWorkspace.CurrentSolution var diagnosticDescriptor1 = EditAndContinueDiagnosticDescriptors.GetDescriptor(EditAndContinueErrorCode.ErrorReadingFile); - mockEncService.EmitSolutionUpdateImpl = (solution, activeStatementSpanProvider) => + mockEncService.EmitSolutionUpdateImpl = (solution, runningProjects, activeStatementSpanProvider) => { var project = solution.GetRequiredProject(projectId); Assert.Equal("proj", project.Name); AssertEx.Equal(activeSpans1, activeStatementSpanProvider(documentId, "test.cs", CancellationToken.None).AsTask().Result); + AssertEx.Equal([project.Id], runningProjects); var deltas = ImmutableArray.Create(new ManagedHotReloadUpdate( module: moduleId1, @@ -209,11 +210,13 @@ await localWorkspace.ChangeSolutionAsync(localWorkspace.CurrentSolution ModuleUpdates = updates, Diagnostics = diagnostics, RudeEdits = [], - SyntaxError = syntaxError + SyntaxError = syntaxError, + ProjectsToRebuild = [project.Id], + ProjectsToRestart = [project.Id], }; }; - var results = await sessionProxy.EmitSolutionUpdateAsync(localWorkspace.CurrentSolution, activeStatementSpanProvider, CancellationToken.None); + var results = await sessionProxy.EmitSolutionUpdateAsync(localWorkspace.CurrentSolution, runningProjects: [project.Id], activeStatementSpanProvider, CancellationToken.None); AssertEx.Equal($"[{projectId}] Error ENC1001: test.cs(0, 1, 0, 2): {string.Format(FeaturesResources.ErrorReadingFile, "doc", "syntax error")}", Inspect(results.SyntaxError!)); Assert.Equal(ModuleUpdateStatus.Ready, results.ModuleUpdates.Status); diff --git a/src/Features/Test/EditAndContinue/WatchHotReloadServiceTests.cs b/src/Features/Test/EditAndContinue/WatchHotReloadServiceTests.cs index 364197f4a8a09..60062cf1b21f2 100644 --- a/src/Features/Test/EditAndContinue/WatchHotReloadServiceTests.cs +++ b/src/Features/Test/EditAndContinue/WatchHotReloadServiceTests.cs @@ -74,7 +74,7 @@ public async Task Test() // Valid update: solution = solution.WithDocumentText(documentIdA, CreateText(source2)); - var result = await hotReload.GetUpdatesAsync(solution, isRunningProject: _ => false, CancellationToken.None); + var result = await hotReload.GetUpdatesAsync(solution, runningProjects: [], CancellationToken.None); Assert.Empty(result.Diagnostics); Assert.Equal(1, result.ProjectUpdates.Length); AssertEx.Equal([0x02000002], result.ProjectUpdates[0].UpdatedTypes); @@ -82,35 +82,35 @@ public async Task Test() // Rude edit: solution = solution.WithDocumentText(documentIdA, CreateText(source3)); - result = await hotReload.GetUpdatesAsync(solution, isRunningProject: _ => true, CancellationToken.None); + result = await hotReload.GetUpdatesAsync(solution, runningProjects: solution.ProjectIds.ToImmutableHashSet(), CancellationToken.None); AssertEx.Equal( ["ENC0110: " + string.Format(FeaturesResources.Changing_the_signature_of_0_requires_restarting_the_application_because_it_is_not_supported_by_the_runtime, FeaturesResources.method)], result.Diagnostics.Select(d => $"{d.Id}: {d.GetMessage()}")); Assert.Empty(result.ProjectUpdates); - AssertEx.SetEqual(["P"], result.ProjectsToRestart.Select(p => p.Name)); - AssertEx.SetEqual(["P"], result.ProjectsToRebuild.Select(p => p.Name)); + AssertEx.SetEqual(["P"], result.ProjectIdsToRestart.Select(p => solution.GetRequiredProject(p).Name)); + AssertEx.SetEqual(["P"], result.ProjectIdsToRebuild.Select(p => solution.GetRequiredProject(p).Name)); // Syntax error: solution = solution.WithDocumentText(documentIdA, CreateText(source4)); - result = await hotReload.GetUpdatesAsync(solution, isRunningProject: _ => true, CancellationToken.None); + result = await hotReload.GetUpdatesAsync(solution, runningProjects: solution.ProjectIds.ToImmutableHashSet(), CancellationToken.None); AssertEx.Equal( ["CS1002: " + CSharpResources.ERR_SemicolonExpected], result.Diagnostics.Select(d => $"{d.Id}: {d.GetMessage()}")); Assert.Empty(result.ProjectUpdates); - Assert.Empty(result.ProjectsToRestart); - Assert.Empty(result.ProjectsToRebuild); + Assert.Empty(result.ProjectIdsToRestart); + Assert.Empty(result.ProjectIdsToRebuild); // Semantic error: solution = solution.WithDocumentText(documentIdA, CreateText(source5)); - result = await hotReload.GetUpdatesAsync(solution, isRunningProject: _ => true, CancellationToken.None); + result = await hotReload.GetUpdatesAsync(solution, runningProjects: solution.ProjectIds.ToImmutableHashSet(), CancellationToken.None); AssertEx.Equal( ["CS0103: " + string.Format(CSharpResources.ERR_NameNotInContext, "Unknown")], result.Diagnostics.Select(d => $"{d.Id}: {d.GetMessage()}")); Assert.Empty(result.ProjectUpdates); - Assert.Empty(result.ProjectsToRestart); - Assert.Empty(result.ProjectsToRebuild); + Assert.Empty(result.ProjectIdsToRestart); + Assert.Empty(result.ProjectIdsToRebuild); hotReload.EndSession(); } @@ -162,7 +162,7 @@ public async Task SourceGeneratorFailure() solution = solution.WithAdditionalDocumentText(aId, CreateText("updated text")); - var result = await hotReload.GetUpdatesAsync(solution, isRunningProject: _ => true, CancellationToken.None); + var result = await hotReload.GetUpdatesAsync(solution, runningProjects: solution.ProjectIds.ToImmutableHashSet(), CancellationToken.None); var diagnostic = result.Diagnostics.Single(); Assert.Equal("CS8785", diagnostic.Id); Assert.Contains("Source generator failed", diagnostic.GetMessage()); diff --git a/src/Features/TestUtilities/EditAndContinue/EditAndContinueWorkspaceTestBase.cs b/src/Features/TestUtilities/EditAndContinue/EditAndContinueWorkspaceTestBase.cs index 83dedbd3788c9..fc40635903865 100644 --- a/src/Features/TestUtilities/EditAndContinue/EditAndContinueWorkspaceTestBase.cs +++ b/src/Features/TestUtilities/EditAndContinue/EditAndContinueWorkspaceTestBase.cs @@ -31,7 +31,7 @@ namespace Microsoft.CodeAnalysis.EditAndContinue.UnitTests; -public abstract class EditAndContinueWorkspaceTestBase : TestBase +public abstract class EditAndContinueWorkspaceTestBase : TestBase, IDisposable { private protected static readonly Guid s_solutionTelemetryId = Guid.Parse("00000000-AAAA-AAAA-AAAA-000000000000"); private protected static readonly Guid s_defaultProjectTelemetryId = Guid.Parse("00000000-AAAA-AAAA-AAAA-111111111111"); @@ -51,6 +51,21 @@ public abstract class EditAndContinueWorkspaceTestBase : TestBase LoadedModules = [] }; + /// + /// Streams that are verified to be disposed at the end of the debug session (by default). + /// + public List<(Guid mvid, Stream stream)> DisposalVerifiedStreams = []; + + public override void Dispose() + { + base.Dispose(); + + foreach (var (_, stream) in DisposalVerifiedStreams) + { + Assert.False(stream.CanRead); + } + } + internal TestWorkspace CreateWorkspace(out Solution solution, out EditAndContinueService service, Type[]? additionalParts = null) { var composition = FeaturesTestCompositions.Features @@ -185,6 +200,9 @@ internal static void CapabilitiesChanged(DebuggingSession session) internal static void CommitSolutionUpdate(DebuggingSession session) => session.CommitSolutionUpdate(); + internal static void DiscardSolutionUpdate(DebuggingSession session) + => session.DiscardSolutionUpdate(); + internal static void EndDebuggingSession(DebuggingSession session) => session.EndSession(out _); @@ -193,7 +211,7 @@ internal static void EndDebuggingSession(DebuggingSession session) Solution solution, ActiveStatementSpanProvider? activeStatementSpanProvider = null) { - var result = await session.EmitSolutionUpdateAsync(solution, activeStatementSpanProvider ?? s_noActiveSpans, CancellationToken.None); + var result = await session.EmitSolutionUpdateAsync(solution, runningProjects: [], activeStatementSpanProvider ?? s_noActiveSpans, CancellationToken.None); return (result.ModuleUpdates, result.Diagnostics.ToDiagnosticData(solution)); } @@ -290,11 +308,13 @@ internal Guid EmitLibrary(Compilation compilation, DebugInformationFormat pdbFor var moduleId = moduleMetadata.GetModuleVersionId(); // associate the binaries with the project (assumes a single project) + _mockCompilationOutputsProvider = _ => new MockCompilationOutputs(moduleId) { OpenAssemblyStreamImpl = () => { var stream = new MemoryStream(); + DisposalVerifiedStreams.Add((moduleId, stream)); peImage.WriteToStream(stream); stream.Position = 0; return stream; @@ -302,6 +322,7 @@ internal Guid EmitLibrary(Compilation compilation, DebugInformationFormat pdbFor OpenPdbStreamImpl = () => { var stream = new MemoryStream(); + DisposalVerifiedStreams.Add((moduleId, stream)); pdbImage.WriteToStream(stream); stream.Position = 0; return stream; diff --git a/src/Features/TestUtilities/EditAndContinue/Extensions.cs b/src/Features/TestUtilities/EditAndContinue/Extensions.cs index ac5df24da0eb4..d8903edb98394 100644 --- a/src/Features/TestUtilities/EditAndContinue/Extensions.cs +++ b/src/Features/TestUtilities/EditAndContinue/Extensions.cs @@ -85,7 +85,7 @@ public static Guid CreateProjectTelemetryId(string projectName) public static ProjectInfo CreateProjectInfo(string projectName, string language = LanguageNames.CSharp) => ProjectInfo.Create( - ProjectId.CreateNewId(), + ProjectId.CreateNewId(debugName: projectName), VersionStamp.Create(), name: projectName, assemblyName: projectName, @@ -97,6 +97,7 @@ public static ProjectInfo CreateProjectInfo(string projectName, string language NoCompilationConstants.LanguageName => null, _ => throw ExceptionUtilities.UnexpectedValue(language) }, + compilationOptions: TestOptions.DebugDll, filePath: projectName + language switch { LanguageNames.CSharp => ".csproj", diff --git a/src/Features/TestUtilities/EditAndContinue/MockEditAndContinueService.cs b/src/Features/TestUtilities/EditAndContinue/MockEditAndContinueService.cs index 2128da2192701..27423d63fc4ff 100644 --- a/src/Features/TestUtilities/EditAndContinue/MockEditAndContinueService.cs +++ b/src/Features/TestUtilities/EditAndContinue/MockEditAndContinueService.cs @@ -23,9 +23,10 @@ internal class MockEditAndContinueService() : IEditAndContinueService public Func, bool, bool, DebuggingSessionId>? StartDebuggingSessionImpl; public Action? EndDebuggingSessionImpl; - public Func? EmitSolutionUpdateImpl; + public Func, ActiveStatementSpanProvider, EmitSolutionUpdateResults>? EmitSolutionUpdateImpl; public Action? OnSourceFileUpdatedImpl; public Action? CommitSolutionUpdateImpl; + public Action>? UpdateBaselinesImpl; public Action? BreakStateOrCapabilitiesChangedImpl; public Action? DiscardSolutionUpdateImpl; public Func>? GetDocumentDiagnosticsImpl; @@ -39,8 +40,11 @@ public void CommitSolutionUpdate(DebuggingSessionId sessionId) public void DiscardSolutionUpdate(DebuggingSessionId sessionId) => DiscardSolutionUpdateImpl?.Invoke(); - public ValueTask EmitSolutionUpdateAsync(DebuggingSessionId sessionId, Solution solution, ActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken) - => new((EmitSolutionUpdateImpl ?? throw new NotImplementedException()).Invoke(solution, activeStatementSpanProvider)); + public void UpdateBaselines(DebuggingSessionId sessionId, Solution solution, ImmutableArray rebuiltProjects) + => UpdateBaselinesImpl?.Invoke(solution, rebuiltProjects); + + public ValueTask EmitSolutionUpdateAsync(DebuggingSessionId sessionId, Solution solution, IImmutableSet runningProjects, ActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken) + => new((EmitSolutionUpdateImpl ?? throw new NotImplementedException()).Invoke(solution, runningProjects, activeStatementSpanProvider)); public void EndDebuggingSession(DebuggingSessionId sessionId) => EndDebuggingSessionImpl?.Invoke(); diff --git a/src/VisualStudio/DevKit/Impl/EditAndContinue/ManagedHotReloadLanguageServiceBridge.cs b/src/VisualStudio/DevKit/Impl/EditAndContinue/ManagedHotReloadLanguageServiceBridge.cs index e9cb2154246b9..21f848e25a5ab 100644 --- a/src/VisualStudio/DevKit/Impl/EditAndContinue/ManagedHotReloadLanguageServiceBridge.cs +++ b/src/VisualStudio/DevKit/Impl/EditAndContinue/ManagedHotReloadLanguageServiceBridge.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Immutable; using System.ComponentModel.Composition; using System.Threading; using System.Threading.Tasks; @@ -17,7 +18,7 @@ namespace Microsoft.CodeAnalysis.EditAndContinue; [ExportBrokeredService(ManagedHotReloadLanguageServiceDescriptor.MonikerName, ManagedHotReloadLanguageServiceDescriptor.ServiceVersion, Audience = ServiceAudience.Local)] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal sealed partial class ManagedHotReloadLanguageServiceBridge(InternalContracts.IManagedHotReloadLanguageService service) : IManagedHotReloadLanguageService, IExportedBrokeredService +internal sealed partial class ManagedHotReloadLanguageServiceBridge(InternalContracts.IManagedHotReloadLanguageService2 service) : IManagedHotReloadLanguageService2, IExportedBrokeredService { ServiceRpcDescriptor IExportedBrokeredService.Descriptor => ManagedHotReloadLanguageServiceDescriptor.Descriptor; @@ -43,9 +44,15 @@ public ValueTask OnCapabilitiesChangedAsync(CancellationToken cancellationToken) public async ValueTask GetUpdatesAsync(CancellationToken cancellationToken) => (await service.GetUpdatesAsync(cancellationToken).ConfigureAwait(false)).FromContract(); + public async ValueTask GetUpdatesAsync(ImmutableArray runningProjects, CancellationToken cancellationToken) + => (await service.GetUpdatesAsync(runningProjects, cancellationToken).ConfigureAwait(false)).FromContract(); + public ValueTask CommitUpdatesAsync(CancellationToken cancellationToken) => service.CommitUpdatesAsync(cancellationToken); + public ValueTask UpdateBaselinesAsync(ImmutableArray projectPaths, CancellationToken cancellationToken) + => service.UpdateBaselinesAsync(projectPaths, cancellationToken); + public ValueTask DiscardUpdatesAsync(CancellationToken cancellationToken) => service.DiscardUpdatesAsync(cancellationToken); diff --git a/src/Workspaces/Remote/Core/EditAndContinue/ManagedHotReloadLanguageService.cs b/src/Workspaces/Remote/Core/EditAndContinue/ManagedHotReloadLanguageService.cs index b3bc1cd42fbe9..2e7d6487c75d7 100644 --- a/src/Workspaces/Remote/Core/EditAndContinue/ManagedHotReloadLanguageService.cs +++ b/src/Workspaces/Remote/Core/EditAndContinue/ManagedHotReloadLanguageService.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Immutable; using System.ComponentModel.Composition; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.BrokeredServices; @@ -13,18 +14,20 @@ using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.EditAndContinue; [Export(typeof(IManagedHotReloadLanguageService))] +[Export(typeof(IManagedHotReloadLanguageService2))] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] internal sealed partial class ManagedHotReloadLanguageService( IServiceBrokerProvider serviceBrokerProvider, IEditAndContinueService encService, - SolutionSnapshotRegistry solutionSnapshotRegistry) : IManagedHotReloadLanguageService + SolutionSnapshotRegistry solutionSnapshotRegistry) : IManagedHotReloadLanguageService2 { private sealed class PdbMatchingSourceTextProvider : IPdbMatchingSourceTextProvider { @@ -152,6 +155,35 @@ public ValueTask CommitUpdatesAsync(CancellationToken cancellationToken) return ValueTaskFactory.CompletedTask; } + public async ValueTask UpdateBaselinesAsync(ImmutableArray projectPaths, CancellationToken cancellationToken) + { + if (_disabled) + { + return; + } + + try + { + Contract.ThrowIfNull(_debuggingSession); + + var currentDesignTimeSolution = await GetCurrentDesignTimeSolutionAsync(cancellationToken).ConfigureAwait(false); + var currentCompileTimeSolution = GetCurrentCompileTimeSolution(currentDesignTimeSolution); + + _committedDesignTimeSolution = currentDesignTimeSolution; + + var projectIds = from path in projectPaths + let projectId = currentCompileTimeSolution.Projects.FirstOrDefault(project => project.FilePath == path)?.Id + where projectId != null + select projectId; + + encService.UpdateBaselines(_debuggingSession.Value, currentCompileTimeSolution, projectIds.ToImmutableArray()); + } + catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) + { + Disable(); + } + } + public ValueTask DiscardUpdatesAsync(CancellationToken cancellationToken) { if (_disabled) @@ -236,7 +268,10 @@ public async ValueTask HasChangesAsync(string? sourceFilePath, Cancellatio } } - public async ValueTask GetUpdatesAsync(CancellationToken cancellationToken) + public ValueTask GetUpdatesAsync(CancellationToken cancellationToken) + => GetUpdatesAsync(runningProjects: [], cancellationToken); + + public async ValueTask GetUpdatesAsync(ImmutableArray runningProjects, CancellationToken cancellationToken) { if (_disabled) { @@ -254,7 +289,11 @@ public async ValueTask GetUpdatesAsync(CancellationToke try { - results = (await encService.EmitSolutionUpdateAsync(_debuggingSession.Value, solution, s_emptyActiveStatementProvider, cancellationToken).ConfigureAwait(false)).Dehydrate(); + using var _ = PooledHashSet.GetInstance(out var runningProjectPaths); + runningProjectPaths.AddAll(runningProjects); + var runningProjectIds = solution.Projects.Where(p => p.FilePath != null && runningProjectPaths.Contains(p.FilePath)).Select(static p => p.Id).ToImmutableHashSet(); + + results = (await encService.EmitSolutionUpdateAsync(_debuggingSession.Value, solution, runningProjectIds, s_emptyActiveStatementProvider, cancellationToken).ConfigureAwait(false)).Dehydrate(); } catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) { @@ -270,7 +309,9 @@ public async ValueTask GetUpdatesAsync(CancellationToke Diagnostics = [DiagnosticData.Create(designTimeSolution, diagnostic, project: null)], RudeEdits = [], ModuleUpdates = new ModuleUpdates(ModuleUpdateStatus.RestartRequired, []), - SyntaxError = null + SyntaxError = null, + ProjectsToRebuild = [], + ProjectsToRestart = [], }; } diff --git a/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/RemoteEditAndContinueService.cs b/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/RemoteEditAndContinueService.cs index 1f57c7803b213..d53a007a69e39 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/RemoteEditAndContinueService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/RemoteEditAndContinueService.cs @@ -141,7 +141,7 @@ public ValueTask> GetDocumentDiagnosticsAsync(Che /// Remote API. /// public ValueTask EmitSolutionUpdateAsync( - Checksum solutionChecksum, RemoteServiceCallbackId callbackId, DebuggingSessionId sessionId, CancellationToken cancellationToken) + Checksum solutionChecksum, RemoteServiceCallbackId callbackId, DebuggingSessionId sessionId, IImmutableSet runningProjects, CancellationToken cancellationToken) { return RunServiceAsync(solutionChecksum, async solution => { @@ -149,7 +149,7 @@ public ValueTask> GetDocumentDiagnosticsAsync(Che try { - return (await service.EmitSolutionUpdateAsync(sessionId, solution, CreateActiveStatementSpanProvider(callbackId), cancellationToken).ConfigureAwait(false)).Dehydrate(); + return (await service.EmitSolutionUpdateAsync(sessionId, solution, runningProjects, CreateActiveStatementSpanProvider(callbackId), cancellationToken).ConfigureAwait(false)).Dehydrate(); } catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) { @@ -159,6 +159,8 @@ public ValueTask> GetDocumentDiagnosticsAsync(Che Diagnostics = GetUnexpectedUpdateError(solution, e), RudeEdits = [], SyntaxError = null, + ProjectsToRebuild = [], + ProjectsToRestart = [], }; } }, cancellationToken); @@ -195,6 +197,18 @@ public ValueTask DiscardSolutionUpdateAsync(DebuggingSessionId sessionId, Cancel }, cancellationToken); } + /// + /// Remote API. + /// + public ValueTask UpdateBaselinesAsync(Checksum solutionChecksum, DebuggingSessionId sessionId, ImmutableArray rebuiltProjects, CancellationToken cancellationToken) + { + return RunServiceAsync(solutionChecksum, solution => + { + GetService().UpdateBaselines(sessionId, solution, rebuiltProjects); + return default; + }, cancellationToken); + } + /// /// Remote API. /// From 5900c2f671c16f9d850a6d622c31f9ece964adc7 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Wed, 13 Nov 2024 16:07:58 -0800 Subject: [PATCH 339/508] Ensure NFW gets reported before result is reported to client and remove async listener in queue --- ...TypeScriptRequestExecutionQueueProvider.cs | 4 +-- .../RequestExecutionQueue.cs | 8 ++--- .../LspServices/RequestTelemetryScope.cs | 15 ++++++++ .../Protocol/RequestExecutionQueueProvider.cs | 5 ++- .../Protocol/RoslynRequestExecutionQueue.cs | 35 ++++--------------- .../ProtocolUnitTests/HandlerTests.cs | 27 +++----------- 6 files changed, 34 insertions(+), 60 deletions(-) diff --git a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptRequestExecutionQueueProvider.cs b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptRequestExecutionQueueProvider.cs index e2097d53c6631..d4c90787529e1 100644 --- a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptRequestExecutionQueueProvider.cs +++ b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptRequestExecutionQueueProvider.cs @@ -15,11 +15,11 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript; [ExportStatelessLspService(typeof(IRequestExecutionQueueProvider), ProtocolConstants.TypeScriptLanguageContract), Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, true)] -internal sealed class VSTypeScriptRequestExecutionQueueProvider(IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider) : IRequestExecutionQueueProvider +internal sealed class VSTypeScriptRequestExecutionQueueProvider() : IRequestExecutionQueueProvider { public IRequestExecutionQueue CreateRequestExecutionQueue(AbstractLanguageServer languageServer, ILspLogger logger, AbstractHandlerProvider handlerProvider) { - var queue = new RoslynRequestExecutionQueue(languageServer, logger, handlerProvider, asynchronousOperationListenerProvider); + var queue = new RoslynRequestExecutionQueue(languageServer, logger, handlerProvider); queue.Start(); return queue; } diff --git a/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs b/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs index 0f987aa808ff2..6da78c6e625f2 100644 --- a/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs +++ b/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs @@ -424,12 +424,12 @@ protected internal virtual void BeforeRequest(TRequest request) /// Provides an extensibility point to log or otherwise inspect errors thrown from non-mutating requests, /// which would otherwise be lost to the fire-and-forget task in the queue. /// - /// The task to be inspected. + /// The task to be inspected. /// If exceptions should be re-thrown. - /// The task from , to allow chained calls if needed. - public virtual Task WrapStartRequestTaskAsync(Task nonMutatingRequestTask, bool rethrowExceptions) + /// The task from , to allow chained calls if needed. + public virtual Task WrapStartRequestTaskAsync(Task requestTask, bool rethrowExceptions) { - return nonMutatingRequestTask; + return requestTask; } /// diff --git a/src/LanguageServer/Protocol/LspServices/RequestTelemetryScope.cs b/src/LanguageServer/Protocol/LspServices/RequestTelemetryScope.cs index 7cc25cf9e74ba..66d1071293ea7 100644 --- a/src/LanguageServer/Protocol/LspServices/RequestTelemetryScope.cs +++ b/src/LanguageServer/Protocol/LspServices/RequestTelemetryScope.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CommonLanguageServerProtocol.Framework; using Roslyn.Utilities; @@ -29,6 +30,9 @@ public override void RecordCancellation() public override void RecordException(Exception exception) { + // Report a NFW report for the request failure, as well as recording statistics on the failure. + ReportNonFatalError(exception); + _result = RequestTelemetryLogger.Result.Failed; } @@ -43,4 +47,15 @@ public override void Dispose() _telemetryLogger.UpdateTelemetryData(Name, Language, _queuedDuration, requestDuration, _result); } + + private static void ReportNonFatalError(Exception exception) + { + if (exception is StreamJsonRpc.LocalRpcException localRpcException && localRpcException.ErrorCode == LspErrorCodes.ContentModified) + { + // Content modified exceptions are expected and should not be reported as NFWs. + return; + } + + FatalError.ReportAndPropagateUnlessCanceled(exception, ErrorSeverity.Critical); + } } diff --git a/src/LanguageServer/Protocol/RequestExecutionQueueProvider.cs b/src/LanguageServer/Protocol/RequestExecutionQueueProvider.cs index 66bbad16e4f66..7e39bbc1dbc40 100644 --- a/src/LanguageServer/Protocol/RequestExecutionQueueProvider.cs +++ b/src/LanguageServer/Protocol/RequestExecutionQueueProvider.cs @@ -6,7 +6,6 @@ using System.Composition; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler; -using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CommonLanguageServerProtocol.Framework; namespace Microsoft.CodeAnalysis.LanguageServer; @@ -14,11 +13,11 @@ namespace Microsoft.CodeAnalysis.LanguageServer; [ExportCSharpVisualBasicStatelessLspService(typeof(IRequestExecutionQueueProvider)), Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, true)] -internal sealed class RequestExecutionQueueProvider(IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider) : IRequestExecutionQueueProvider +internal sealed class RequestExecutionQueueProvider() : IRequestExecutionQueueProvider { public IRequestExecutionQueue CreateRequestExecutionQueue(AbstractLanguageServer languageServer, ILspLogger logger, AbstractHandlerProvider handlerProvider) { - var queue = new RoslynRequestExecutionQueue(languageServer, logger, handlerProvider, asynchronousOperationListenerProvider); + var queue = new RoslynRequestExecutionQueue(languageServer, logger, handlerProvider); queue.Start(); return queue; } diff --git a/src/LanguageServer/Protocol/RoslynRequestExecutionQueue.cs b/src/LanguageServer/Protocol/RoslynRequestExecutionQueue.cs index e12fced425f83..c0e28138c3197 100644 --- a/src/LanguageServer/Protocol/RoslynRequestExecutionQueue.cs +++ b/src/LanguageServer/Protocol/RoslynRequestExecutionQueue.cs @@ -5,63 +5,40 @@ using System; using System.Globalization; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.LanguageServer.Handler; -using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CommonLanguageServerProtocol.Framework; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer { internal sealed class RoslynRequestExecutionQueue : RequestExecutionQueue { private readonly IInitializeManager _initializeManager; - private readonly IAsynchronousOperationListener _listener; /// /// Serial access is guaranteed by the queue. /// private CultureInfo? _cultureInfo; - public RoslynRequestExecutionQueue(AbstractLanguageServer languageServer, ILspLogger logger, AbstractHandlerProvider handlerProvider, IAsynchronousOperationListenerProvider provider) + public RoslynRequestExecutionQueue(AbstractLanguageServer languageServer, ILspLogger logger, AbstractHandlerProvider handlerProvider) : base(languageServer, logger, handlerProvider) { _initializeManager = languageServer.GetLspServices().GetRequiredService(); - _listener = provider.GetListener(FeatureAttribute.LanguageServer); } - public override async Task WrapStartRequestTaskAsync(Task nonMutatingRequestTask, bool rethrowExceptions) + public override async Task WrapStartRequestTaskAsync(Task requestTask, bool rethrowExceptions) { - using var token = _listener.BeginAsyncOperation(nameof(WrapStartRequestTaskAsync)); if (rethrowExceptions) { - try - { - await nonMutatingRequestTask.ConfigureAwait(false); - } - catch (StreamJsonRpc.LocalRpcException localRpcException) when (localRpcException.ErrorCode == LspErrorCodes.ContentModified) - { - // Content modified exceptions are expected and should not be reported as NFWs. - throw; - } - // If we had an exception, we want to record a NFW for it AND propogate it out to the queue so it can be handled appropriately. - catch (Exception ex) when (FatalError.ReportAndPropagateUnlessCanceled(ex, ErrorSeverity.Critical)) - { - throw ExceptionUtilities.Unreachable(); - } + await requestTask.ConfigureAwait(false); } else { - // The caller has asked us to not rethrow, so record a NFW and swallow. + // The caller has asked us to not rethrow, so swallow the exception. try { - await nonMutatingRequestTask.ConfigureAwait(false); - } - catch (StreamJsonRpc.LocalRpcException localRpcException) when (localRpcException.ErrorCode == LspErrorCodes.ContentModified) - { - // Content modified exceptions are expected and should not be reported as NFWs. + await requestTask.ConfigureAwait(false); } - catch (Exception ex) when (FatalError.ReportAndCatchUnlessCanceled(ex, ErrorSeverity.Critical)) + catch (Exception) { // Swallow the exception so it does not bubble up into the queue. } diff --git a/src/LanguageServer/ProtocolUnitTests/HandlerTests.cs b/src/LanguageServer/ProtocolUnitTests/HandlerTests.cs index 402b78d7ab003..3787a64d7a2d8 100644 --- a/src/LanguageServer/ProtocolUnitTests/HandlerTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/HandlerTests.cs @@ -10,14 +10,12 @@ using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler; -using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CommonLanguageServerProtocol.Framework; using Roslyn.LanguageServer.Protocol; using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; -using static Microsoft.CodeAnalysis.LanguageServer.UnitTests.LocaleTests; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests { @@ -152,7 +150,7 @@ public async Task NonMutatingHandlerExceptionNFWIsReported(bool mutatingLspWorks var didReport = false; FatalError.OverwriteHandler((exception, severity, dumps) => { - if (exception.Message == nameof(HandlerTests) || exception.InnerException.Message == nameof(HandlerTests)) + if (exception.Message == nameof(HandlerTests) || exception.InnerException?.Message == nameof(HandlerTests)) { didReport = true; } @@ -164,11 +162,6 @@ public async Task NonMutatingHandlerExceptionNFWIsReported(bool mutatingLspWorks await Assert.ThrowsAnyAsync(async () => await server.ExecuteRequestAsync(TestConfigurableDocumentHandler.MethodName, request, CancellationToken.None)); - var provider = server.TestWorkspace.ExportProvider.GetExportedValue(); - await provider.WaitAllDispatcherOperationAndTasksAsync( - server.TestWorkspace, - FeatureAttribute.LanguageServer); - Assert.True(didReport); } @@ -185,7 +178,7 @@ public async Task NonMutatingHandlerExceptionNFWIsNotReportedForLocalRpcExceptio var didReport = false; FatalError.OverwriteHandler((exception, severity, dumps) => { - if (exception.Message == nameof(HandlerTests) || exception.InnerException.Message == nameof(HandlerTests)) + if (exception.Message == nameof(HandlerTests) || exception.InnerException?.Message == nameof(HandlerTests)) { didReport = true; } @@ -197,11 +190,6 @@ public async Task NonMutatingHandlerExceptionNFWIsNotReportedForLocalRpcExceptio await Assert.ThrowsAnyAsync(async () => await server.ExecuteRequestAsync(TestConfigurableDocumentHandler.MethodName, request, CancellationToken.None)); - var provider = server.TestWorkspace.ExportProvider.GetExportedValue(); - await provider.WaitAllDispatcherOperationAndTasksAsync( - server.TestWorkspace, - FeatureAttribute.LanguageServer); - Assert.False(didReport); } @@ -218,7 +206,7 @@ public async Task MutatingHandlerExceptionNFWIsReported(bool mutatingLspWorkspac var didReport = false; FatalError.OverwriteHandler((exception, severity, dumps) => { - if (exception.Message == nameof(HandlerTests) || exception.InnerException.Message == nameof(HandlerTests)) + if (exception.Message == nameof(HandlerTests) || exception.InnerException?.Message == nameof(HandlerTests)) { didReport = true; } @@ -248,7 +236,7 @@ public async Task NonMutatingHandlerCancellationExceptionNFWIsNotReported(bool m var didReport = false; FatalError.OverwriteHandler((exception, severity, dumps) => { - if (exception.Message == nameof(HandlerTests) || exception.InnerException.Message == nameof(HandlerTests)) + if (exception.Message == nameof(HandlerTests) || exception.InnerException?.Message == nameof(HandlerTests)) { didReport = true; } @@ -260,11 +248,6 @@ public async Task NonMutatingHandlerCancellationExceptionNFWIsNotReported(bool m await Assert.ThrowsAnyAsync(async () => await server.ExecuteRequestAsync(TestConfigurableDocumentHandler.MethodName, request, CancellationToken.None)); - var provider = server.TestWorkspace.ExportProvider.GetExportedValue(); - await provider.WaitAllDispatcherOperationAndTasksAsync( - server.TestWorkspace, - FeatureAttribute.LanguageServer); - Assert.False(didReport); } @@ -281,7 +264,7 @@ public async Task MutatingHandlerCancellationExceptionNFWIsNotReported(bool muta var didReport = false; FatalError.OverwriteHandler((exception, severity, dumps) => { - if (exception.Message == nameof(HandlerTests) || exception.InnerException.Message == nameof(HandlerTests)) + if (exception.Message == nameof(HandlerTests) || exception.InnerException?.Message == nameof(HandlerTests)) { didReport = true; } From 857ad2c64e47f0e6f803f70d103e0d757e6a941a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 13 Nov 2024 16:52:00 -0800 Subject: [PATCH 340/508] Simplify --- .../CSharp/Portable/Parser/LanguageParser.cs | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 9423cff2c3945..37d5e9817022e 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -9231,21 +9231,9 @@ private ForStatementSyntax ParseForStatement(SyntaxList att } SyntaxToken eatCommaOrSemicolon() - { - if (this.CurrentToken.Kind is SyntaxKind.CommaToken) - { - // Still parse out a semicolon so we give an appropriate error that the comma is unexpected, and so - // we have the correct token kind. - var semicolon = this.EatToken(SyntaxKind.SemicolonToken); - - // Now skip past the comma - return AddTrailingSkippedSyntax(semicolon, this.EatToken()); - } - else - { - return this.EatToken(SyntaxKind.SemicolonToken); - } - } + => this.CurrentToken.Kind is SyntaxKind.CommaToken + ? this.EatTokenAsKind(SyntaxKind.SemicolonToken) + : this.EatToken(SyntaxKind.SemicolonToken); SyntaxToken eatUnexpectedTokensAndCloseParenToken() { From 37aba0747838eb7084dceb0f96f0aa6917cb9fd6 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Thu, 14 Nov 2024 13:22:44 +1100 Subject: [PATCH 341/508] Dont map spans for aspx files --- .../Workspace/VisualStudioDocumentNavigationService.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/VisualStudio/Core/Def/Workspace/VisualStudioDocumentNavigationService.cs b/src/VisualStudio/Core/Def/Workspace/VisualStudioDocumentNavigationService.cs index 5d1a9ca5d5377..97ec5a757a3ab 100644 --- a/src/VisualStudio/Core/Def/Workspace/VisualStudioDocumentNavigationService.cs +++ b/src/VisualStudio/Core/Def/Workspace/VisualStudioDocumentNavigationService.cs @@ -228,10 +228,12 @@ await getTextSpanForMappingAsync(document).ConfigureAwait(false), // If the mapped file maps to the same document that was passed in, then re-use the documentId to preserve context. // Otherwise, just pick one of the ids to use for navigation. var documentIdToNavigate = documentIdsForFilePath.Contains(documentId) ? documentId : documentIdsForFilePath.First(); - return GetNavigationCallback( - documentIdToNavigate, - workspace, - sourceText => getVsTextSpanForMapping(sourceText, mappedSpan.Span)); + + // For Venus documents, further mapping is done in the callback, so we don't want to do it here via getVsTextSpanForMapping + var getSpanForCallback = IsSecondaryBuffer(documentIdToNavigate) + ? getVsTextSpan + : sourceText => getVsTextSpanForMapping(sourceText, mappedSpan.Span); + return GetNavigationCallback(documentIdToNavigate, workspace, getSpanForCallback); } return await GetNavigableLocationForMappedFileAsync( From 44b9b569c99f7cc8cda4870a91f2064db83e0a16 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 13 Nov 2024 20:54:18 -0800 Subject: [PATCH 342/508] Improve go-to-derived with error conditions --- .../GoToImplementationTests.vb | 26 +++++- ...ctFindUsagesService_FindImplementations.cs | 11 +-- .../FindReferences/BaseTypeFinder.cs | 79 ++++++++++--------- .../FindSymbols/SymbolFinder_Hierarchy.cs | 40 ++++++---- 4 files changed, 100 insertions(+), 56 deletions(-) diff --git a/src/EditorFeatures/Test2/GoToImplementation/GoToImplementationTests.vb b/src/EditorFeatures/Test2/GoToImplementation/GoToImplementationTests.vb index 3f745a8900df5..86ca0d5c2262c 100644 --- a/src/EditorFeatures/Test2/GoToImplementation/GoToImplementationTests.vb +++ b/src/EditorFeatures/Test2/GoToImplementation/GoToImplementationTests.vb @@ -11,7 +11,6 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.GoToImplementation <[UseExportProvider]> Public Class GoToImplementationTests - Private Shared Async Function TestAsync(workspaceDefinition As XElement, host As TestHost, Optional shouldSucceed As Boolean = True, Optional metadataDefinitions As String() = Nothing) As Task Await GoToHelpers.TestAsync( workspaceDefinition, @@ -787,5 +786,30 @@ class D : C { public override void [|M|]() { } }} Await TestAsync(workspace, host) End Function + + + + Public Async Function FindLooseMatch1(host As TestHost) As Task + Dim workspace = + + + +class C +{ + public abstract void $$Foo() { } +} + +class D : C +{ + public override void [|Foo|](int i) + { + base.Foo(); + } +} + + + + Await TestAsync(workspace, host) + End Function End Class End Namespace diff --git a/src/Features/Core/Portable/FindUsages/AbstractFindUsagesService_FindImplementations.cs b/src/Features/Core/Portable/FindUsages/AbstractFindUsagesService_FindImplementations.cs index 990298e58bd3d..94210eeb81a9d 100644 --- a/src/Features/Core/Portable/FindUsages/AbstractFindUsagesService_FindImplementations.cs +++ b/src/Features/Core/Portable/FindUsages/AbstractFindUsagesService_FindImplementations.cs @@ -23,18 +23,17 @@ public async Task FindImplementationsAsync( IFindUsagesContext context, Document document, int position, OptionsProvider classificationOptions, CancellationToken cancellationToken) { // If this is a symbol from a metadata-as-source project, then map that symbol back to a symbol in the primary workspace. - var symbolAndProjectOpt = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync( + var symbolAndProject = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync( document, position, cancellationToken).ConfigureAwait(false); - if (symbolAndProjectOpt == null) + if (symbolAndProject is not var (symbol, project)) { await context.ReportNoResultsAsync( FeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret, cancellationToken).ConfigureAwait(false); return; } - var symbolAndProject = symbolAndProjectOpt.Value; await FindImplementationsAsync( - context, symbolAndProject.symbol, symbolAndProject.project, classificationOptions, cancellationToken).ConfigureAwait(false); + context, symbol, project, classificationOptions, cancellationToken).ConfigureAwait(false); } public static async Task FindImplementationsAsync( @@ -145,7 +144,9 @@ private static async Task> FindImplementationsWorkerAsyn var overrides = result.Where(s => s.IsOverride).ToImmutableArray(); foreach (var ov in overrides) { - for (var overridden = ov.GetOverriddenMember(); overridden != null; overridden = overridden.GetOverriddenMember()) + for (var overridden = ov.GetOverriddenMember(allowLooseMatch: true); + overridden != null; + overridden = overridden.GetOverriddenMember(allowLooseMatch: true)) { if (overridden.IsAbstract) result.Remove(overridden.OriginalDefinition); diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/BaseTypeFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/BaseTypeFinder.cs index 71ab652187128..210e509272513 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/BaseTypeFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/BaseTypeFinder.cs @@ -19,52 +19,59 @@ public static ImmutableArray FindOverriddenAndImplementedMembers( { using var _ = ArrayBuilder.GetInstance(out var results); + AddOverrides(allowLooseMatch: false); + if (results.Count == 0) + AddOverrides(allowLooseMatch: true); + // This is called for all: class, struct or interface member. results.AddRange(symbol.ExplicitOrImplicitInterfaceImplementations()); - // The type scenario. Iterate over all base classes to find overridden and hidden (new/Shadows) methods. - foreach (var type in FindBaseTypes(symbol.ContainingType)) + // Remove duplicates from interface implementations before adding their projects. + results.RemoveDuplicates(); + return results.ToImmutableAndClear(); + + void AddOverrides(bool allowLooseMatch) { - foreach (var member in type.GetMembers(symbol.Name)) + // The type scenario. Iterate over all base classes to find overridden and hidden (new/Shadows) methods. + foreach (var type in FindBaseTypes(symbol.ContainingType)) { - cancellationToken.ThrowIfCancellationRequested(); - - // Add to results overridden members only. Do not add hidden members. - if (SymbolFinder.IsOverride(solution, symbol, member)) + foreach (var member in type.GetMembers(symbol.Name)) { - results.Add(member); + cancellationToken.ThrowIfCancellationRequested(); - // We should add implementations only for overridden members but not for hidden ones. - // In the following example: - // - // interface I { void M(); } - // class A : I { public void M(); } - // class B : A { public new void M(); } - // - // we should not find anything for B.M() because it does not implement the interface: - // - // I i = new B(); i.M(); - // - // will call the method from A. - // However, if we change the code to - // - // class B : A, I { public new void M(); } - // - // then - // - // I i = new B(); i.M(); - // - // will call the method from B. We should find the base for B.M in this case. - // And if we change 'new' to 'override' in the original code and add 'virtual' where needed, - // we should find I.M as a base for B.M(). And the next line helps with this scenario. - results.AddRange(member.ExplicitOrImplicitInterfaceImplementations()); + // Add to results overridden members only. Do not add hidden members. + if (SymbolFinder.IsOverride(solution, symbol, member, allowLooseMatch)) + { + results.Add(member); + + // We should add implementations only for overridden members but not for hidden ones. + // In the following example: + // + // interface I { void M(); } + // class A : I { public void M(); } + // class B : A { public new void M(); } + // + // we should not find anything for B.M() because it does not implement the interface: + // + // I i = new B(); i.M(); + // + // will call the method from A. + // However, if we change the code to + // + // class B : A, I { public new void M(); } + // + // then + // + // I i = new B(); i.M(); + // + // will call the method from B. We should find the base for B.M in this case. + // And if we change 'new' to 'override' in the original code and add 'virtual' where needed, + // we should find I.M as a base for B.M(). And the next line helps with this scenario. + results.AddRange(member.ExplicitOrImplicitInterfaceImplementations()); + } } } } - - // Remove duplicates from interface implementations before adding their projects. - results.RemoveDuplicates(); - return results.ToImmutableAndClear(); } private static ImmutableArray FindBaseTypes(INamedTypeSymbol type) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Hierarchy.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Hierarchy.cs index ad70664b74512..b00c9dcb4738a 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Hierarchy.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Hierarchy.cs @@ -36,17 +36,29 @@ public static async Task> FindOverridesAsync( internal static async Task> FindOverridesArrayAsync( ISymbol symbol, Solution solution, IImmutableSet? projects = null, CancellationToken cancellationToken = default) { - var results = ArrayBuilder.GetInstance(); symbol = symbol.OriginalDefinition; - if (symbol.IsOverridable()) - { - // To find the overrides, we need to walk down the type hierarchy and check all - // derived types. - var containingType = symbol.ContainingType; - var derivedTypes = await FindDerivedClassesAsync( - containingType, solution, projects, cancellationToken).ConfigureAwait(false); + if (!symbol.IsOverridable()) + return []; + + using var _ = ArrayBuilder.GetInstance(out var results); + + // To find the overrides, we need to walk down the type hierarchy and check all + // derived types. + var containingType = symbol.ContainingType; + var derivedTypes = await FindDerivedClassesAsync( + containingType, solution, projects, cancellationToken).ConfigureAwait(false); + // First try finding exact overrides. If that fails to find anything, look for overrides that loosely + // match due to errors. + await FindOverridesAsync(allowLooseMatch: false).ConfigureAwait(false); + if (results.Count == 0) + await FindOverridesAsync(allowLooseMatch: true).ConfigureAwait(false); + + return results.ToImmutableAndClear(); + + async Task FindOverridesAsync(bool allowLooseMatch) + { foreach (var type in derivedTypes) { foreach (var m in type.GetMembers(symbol.Name)) @@ -54,20 +66,20 @@ internal static async Task> FindOverridesArrayAsync( var sourceMember = await FindSourceDefinitionAsync(m, solution, cancellationToken).ConfigureAwait(false); var bestMember = sourceMember ?? m; - if (IsOverride(solution, bestMember, symbol)) + if (IsOverride(solution, bestMember, symbol, allowLooseMatch)) results.Add(bestMember); } } } - - return results.ToImmutableAndFree(); } - internal static bool IsOverride(Solution solution, ISymbol member, ISymbol symbol) + internal static bool IsOverride(Solution solution, ISymbol member, ISymbol symbol, bool allowLooseMatch) { - for (var current = member; current != null; current = current.GetOverriddenMember()) + for (var current = member; + current != null; + current = current.GetOverriddenMember(allowLooseMatch)) { - if (OriginalSymbolsMatch(solution, current.GetOverriddenMember(), symbol.OriginalDefinition)) + if (OriginalSymbolsMatch(solution, current.GetOverriddenMember(allowLooseMatch), symbol.OriginalDefinition)) return true; } From 5edfd8ae8fb5a7a29fb6b5bd2fccd4942e4dacf7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 13 Nov 2024 20:54:58 -0800 Subject: [PATCH 343/508] Use collection expr --- .../InheritanceMarginTests.cs | 657 ++++++++++-------- 1 file changed, 350 insertions(+), 307 deletions(-) diff --git a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs index 6639877600f7e..6035adf0ba04b 100644 --- a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs +++ b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs @@ -248,7 +248,7 @@ public TargetInfo( string? projectName = null) { TargetSymbolDisplayName = targetSymbolDisplayName; - LocationTags = ImmutableArray.Create(locationTag); + LocationTags = [locationTag]; Relationship = relationship; LanguageGlyph = languageGlyph; InMetadata = false; @@ -375,18 +375,18 @@ public class Bar : IEnumerable var itemForBar = new TestInheritanceMemberItem( lineNumber: 3, memberName: "class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IEnumerable", relationship: InheritanceRelationship.ImplementedInterface, - inMetadata: true))); + inMetadata: true)]); var itemForGetEnumerator = new TestInheritanceMemberItem( lineNumber: 5, memberName: "IEnumerator Bar.GetEnumerator()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IEnumerable.GetEnumerator", relationship: InheritanceRelationship.ImplementedMember, - inMetadata: true))); + inMetadata: true)]); return VerifyInSingleDocumentAsync(markup, LanguageNames.CSharp, testHost, itemForBar, itemForGetEnumerator); } @@ -404,18 +404,18 @@ public class {|target2:Bar|} : IBar var itemOnLine2 = new TestInheritanceMemberItem( lineNumber: 2, memberName: "interface IBar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemOnLine3 = new TestInheritanceMemberItem( lineNumber: 3, memberName: "class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); return VerifyInSingleDocumentAsync( markup, @@ -436,10 +436,10 @@ interface {|target2:IBar2|} : IBar { } var itemOnLine2 = new TestInheritanceMemberItem( lineNumber: 2, memberName: "interface IBar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar2", locationTag: "target2", - relationship: InheritanceRelationship.ImplementingType)) + relationship: InheritanceRelationship.ImplementingType)] ); var itemOnLine3 = new TestInheritanceMemberItem( lineNumber: 3, @@ -470,18 +470,18 @@ class {|target1:B|} : A { } var itemOnLine2 = new TestInheritanceMemberItem( lineNumber: 2, memberName: "class A", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "B", locationTag: "target1", - relationship: InheritanceRelationship.DerivedType)) + relationship: InheritanceRelationship.DerivedType)] ); var itemOnLine3 = new TestInheritanceMemberItem( lineNumber: 3, memberName: "class B", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "A", locationTag: "target2", - relationship: InheritanceRelationship.BaseType)) + relationship: InheritanceRelationship.BaseType)] ); return VerifyInSingleDocumentAsync( @@ -535,11 +535,10 @@ public class {{|{SearchAreaTag}:Bar : Bar1 new TestInheritanceMemberItem( lineNumber: 4, memberName: "class Bar", - targets: ImmutableArray.Create( - new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar1", locationTag: "target1", - relationship: InheritanceRelationship.BaseType)))); + relationship: InheritanceRelationship.BaseType)])); } [Theory, CombinatorialData] @@ -561,34 +560,34 @@ public class {|target1:Bar|} : IBar var itemForIBar = new TestInheritanceMemberItem( lineNumber: 3, memberName: "interface IBar", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemForBar = new TestInheritanceMemberItem( lineNumber: 7, memberName: "class Bar", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); var itemForEventInInterface = new TestInheritanceMemberItem( lineNumber: 5, memberName: "event EventHandler IBar.e", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "Bar.e", locationTag: "target3", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); var itemForEventInClass = new TestInheritanceMemberItem( lineNumber: 9, memberName: "event EventHandler Bar.e", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "IBar.e", locationTag: "target4", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); return VerifyInSingleDocumentAsync( markup, @@ -615,50 +614,50 @@ public class {|target1:Bar|} : IBar var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, memberName: "interface IBar", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemForBar = new TestInheritanceMemberItem( lineNumber: 6, memberName: "class Bar", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); var itemForE1InInterface = new TestInheritanceMemberItem( lineNumber: 4, memberName: "event EventHandler IBar.e1", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "Bar.e1", locationTag: "target3", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); var itemForE2InInterface = new TestInheritanceMemberItem( lineNumber: 4, memberName: "event EventHandler IBar.e2", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "Bar.e2", locationTag: "target4", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); var itemForE1InClass = new TestInheritanceMemberItem( lineNumber: 8, memberName: "event EventHandler Bar.e1", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "IBar.e1", locationTag: "target5", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); var itemForE2InClass = new TestInheritanceMemberItem( lineNumber: 8, memberName: "event EventHandler Bar.e2", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "IBar.e2", locationTag: "target6", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); return VerifyInSingleDocumentAsync( markup, @@ -693,91 +692,91 @@ public class {|target2:Bar|} : IBar var itemForEooInClass = new TestInheritanceMemberItem( lineNumber: 13, memberName: "event EventHandler Bar.Eoo", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar.Eoo", locationTag: "target8", - relationship: InheritanceRelationship.ImplementedMember)) + relationship: InheritanceRelationship.ImplementedMember)] ); var itemForEooInInterface = new TestInheritanceMemberItem( lineNumber: 6, memberName: "event EventHandler IBar.Eoo", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar.Eoo", locationTag: "target7", - relationship: InheritanceRelationship.ImplementingMember)) + relationship: InheritanceRelationship.ImplementingMember)] ); var itemForPooInInterface = new TestInheritanceMemberItem( lineNumber: 5, memberName: "int IBar.Poo { get; set; }", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar.Poo", locationTag: "target5", - relationship: InheritanceRelationship.ImplementingMember)) + relationship: InheritanceRelationship.ImplementingMember)] ); var itemForPooInClass = new TestInheritanceMemberItem( lineNumber: 12, memberName: "int Bar.Poo { get; set; }", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar.Poo", locationTag: "target6", - relationship: InheritanceRelationship.ImplementedMember)) + relationship: InheritanceRelationship.ImplementedMember)] ); var itemForFooInInterface = new TestInheritanceMemberItem( lineNumber: 4, memberName: "void IBar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar.Foo", locationTag: "target3", - relationship: InheritanceRelationship.ImplementingMember)) + relationship: InheritanceRelationship.ImplementingMember)] ); var itemForFooInClass = new TestInheritanceMemberItem( lineNumber: 11, memberName: "void Bar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar.Foo", locationTag: "target4", - relationship: InheritanceRelationship.ImplementedMember)) + relationship: InheritanceRelationship.ImplementedMember)] ); var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, memberName: "interface IBar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementingType)) + relationship: InheritanceRelationship.ImplementingType)] ); var itemForBar = new TestInheritanceMemberItem( lineNumber: 9, memberName: "class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementedInterface)) + relationship: InheritanceRelationship.ImplementedInterface)] ); var itemForIndexerInClass = new TestInheritanceMemberItem( lineNumber: 14, memberName: "int Bar.this[int] { get; set; }", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar.this", locationTag: "target9", - relationship: InheritanceRelationship.ImplementedMember)) + relationship: InheritanceRelationship.ImplementedMember)] ); var itemForIndexerInInterface = new TestInheritanceMemberItem( lineNumber: 7, memberName: "int IBar.this[int] { get; set; }", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar.this", locationTag: "target10", - relationship: InheritanceRelationship.ImplementingMember)) + relationship: InheritanceRelationship.ImplementingMember)] ); return VerifyInSingleDocumentAsync( @@ -821,66 +820,66 @@ public class {{|target1:Bar2|}} : Bar var itemForEooInClass = new TestInheritanceMemberItem( lineNumber: 12, memberName: "override event EventHandler Bar2.Eoo", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: $"Bar.Eoo", locationTag: "target8", - relationship: InheritanceRelationship.OverriddenMember))); + relationship: InheritanceRelationship.OverriddenMember)]); var itemForEooInAbstractClass = new TestInheritanceMemberItem( lineNumber: 6, memberName: $"{modifier} event EventHandler Bar.Eoo", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar2.Eoo", locationTag: "target7", - relationship: InheritanceRelationship.OverridingMember))); + relationship: InheritanceRelationship.OverridingMember)]); var itemForPooInClass = new TestInheritanceMemberItem( lineNumber: 11, memberName: "override int Bar2.Poo { get; set; }", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: $"Bar.Poo", locationTag: "target6", - relationship: InheritanceRelationship.OverriddenMember))); + relationship: InheritanceRelationship.OverriddenMember)]); var itemForPooInAbstractClass = new TestInheritanceMemberItem( lineNumber: 5, memberName: $"{modifier} int Bar.Poo {{ get; set; }}", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar2.Poo", locationTag: "target5", - relationship: InheritanceRelationship.OverridingMember))); + relationship: InheritanceRelationship.OverridingMember)]); var itemForFooInAbstractClass = new TestInheritanceMemberItem( lineNumber: 4, memberName: $"{modifier} void Bar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar2.Foo", locationTag: "target3", - relationship: InheritanceRelationship.OverridingMember))); + relationship: InheritanceRelationship.OverridingMember)]); var itemForFooInClass = new TestInheritanceMemberItem( lineNumber: 10, memberName: "override void Bar2.Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: $"Bar.Foo", locationTag: "target4", - relationship: InheritanceRelationship.OverriddenMember))); + relationship: InheritanceRelationship.OverriddenMember)]); var itemForBar = new TestInheritanceMemberItem( lineNumber: 2, memberName: "class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar2", locationTag: "target1", - relationship: InheritanceRelationship.DerivedType))); + relationship: InheritanceRelationship.DerivedType)]); var itemForBar2 = new TestInheritanceMemberItem( lineNumber: 8, memberName: "class Bar2", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target2", - relationship: InheritanceRelationship.BaseType))); + relationship: InheritanceRelationship.BaseType)]); return VerifyInSingleDocumentAsync( markup, @@ -930,75 +929,92 @@ public class {|target5:Bar2|} : Bar1, IBar var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, memberName: "interface IBar", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Bar1", - locationTag: "target1", + targets: + [ + new TargetInfo( + targetSymbolDisplayName: "Bar1", + locationTag: "target1", + relationship: InheritanceRelationship.ImplementingType), + new TargetInfo( + targetSymbolDisplayName: "Bar2", + locationTag: "target5", relationship: InheritanceRelationship.ImplementingType), - new TargetInfo( - targetSymbolDisplayName: "Bar2", - locationTag: "target5", - relationship: InheritanceRelationship.ImplementingType))); + ]); var itemForFooInIBar = new TestInheritanceMemberItem( lineNumber: 4, memberName: "void IBar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Bar1.Foo", - locationTag: "target2", - relationship: InheritanceRelationship.ImplementingMember), + targets: + [ + new TargetInfo( + targetSymbolDisplayName: "Bar1.Foo", + locationTag: "target2", + relationship: InheritanceRelationship.ImplementingMember), new TargetInfo( targetSymbolDisplayName: "Bar2.Foo", locationTag: "target3", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember), + ]); var itemForBar1 = new TestInheritanceMemberItem( lineNumber: 6, memberName: "class Bar1", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "IBar", - locationTag: "target4", - relationship: InheritanceRelationship.ImplementedInterface), + targets: + [ + new TargetInfo( + targetSymbolDisplayName: "IBar", + locationTag: "target4", + relationship: InheritanceRelationship.ImplementedInterface), new TargetInfo( targetSymbolDisplayName: "Bar2", locationTag: "target5", - relationship: InheritanceRelationship.DerivedType))); + relationship: InheritanceRelationship.DerivedType), + ]); var itemForFooInBar1 = new TestInheritanceMemberItem( lineNumber: 8, memberName: "virtual void Bar1.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "IBar.Foo", - locationTag: "target6", - relationship: InheritanceRelationship.ImplementedMember), + targets: + [ + new TargetInfo( + targetSymbolDisplayName: "IBar.Foo", + locationTag: "target6", + relationship: InheritanceRelationship.ImplementedMember), new TargetInfo( targetSymbolDisplayName: "Bar2.Foo", locationTag: "target3", - relationship: InheritanceRelationship.OverridingMember))); + relationship: InheritanceRelationship.OverridingMember), + ]); var itemForBar2 = new TestInheritanceMemberItem( lineNumber: 10, memberName: "class Bar2", - targets: ImmutableArray.Create( + targets: + [ new TargetInfo( - targetSymbolDisplayName: "Bar1", - locationTag: "target1", - relationship: InheritanceRelationship.BaseType), + targetSymbolDisplayName: "Bar1", + locationTag: "target1", + relationship: InheritanceRelationship.BaseType), new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target4", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface), + ]); var itemForFooInBar2 = new TestInheritanceMemberItem( lineNumber: 12, memberName: "override void Bar2.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "IBar.Foo", - locationTag: "target6", - relationship: InheritanceRelationship.ImplementedMember), + targets: + [ + new TargetInfo( + targetSymbolDisplayName: "IBar.Foo", + locationTag: "target6", + relationship: InheritanceRelationship.ImplementedMember), new TargetInfo( targetSymbolDisplayName: "Bar1.Foo", locationTag: "target2", - relationship: InheritanceRelationship.OverriddenMember))); + relationship: InheritanceRelationship.OverriddenMember), + ]); return VerifyInSingleDocumentAsync( testDuplicate ? markup2 : markup1, @@ -1029,36 +1045,36 @@ public class {|target1:Bar2|} : IBar, IBar var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, memberName: "interface IBar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar2", locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemForFooInIBar = new TestInheritanceMemberItem( lineNumber: 4, memberName: "void IBar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar2.Foo", locationTag: "target3", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); // Only have one IBar item var itemForBar2 = new TestInheritanceMemberItem( lineNumber: 7, memberName: "class Bar2", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); // Only have one IBar.Foo item var itemForFooInBar2 = new TestInheritanceMemberItem( lineNumber: 9, memberName: "void Bar2.Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar.Foo", locationTag: "target4", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); return VerifyInSingleDocumentAsync( markup, @@ -1089,35 +1105,34 @@ abstract class {|target1:AbsBar|} : IBar var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, memberName: "interface IBar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "AbsBar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemForFooInIBar = new TestInheritanceMemberItem( lineNumber: 4, memberName: "void IBar.Foo(T)", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "AbsBar.IBar.Foo", locationTag: "target4", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); var itemForAbsBar = new TestInheritanceMemberItem( lineNumber: 7, memberName: "class AbsBar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); var itemForFooInAbsBar = new TestInheritanceMemberItem( lineNumber: 9, memberName: "void AbsBar.IBar.Foo(int)", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar.Foo", locationTag: "target3", - relationship: InheritanceRelationship.ImplementedMember) - )); + relationship: InheritanceRelationship.ImplementedMember)]); return VerifyInSingleDocumentAsync( markup, @@ -1153,98 +1168,98 @@ public class {|target1:Class1|} : I1 var itemForI1 = new TestInheritanceMemberItem( lineNumber: 2, memberName: "interface I1", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Class1", locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemForM1InI1 = new TestInheritanceMemberItem( lineNumber: 4, memberName: "void I1.M1()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Class1.M1", locationTag: "target2", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); var itemForAbsClass1 = new TestInheritanceMemberItem( lineNumber: 11, memberName: "class Class1", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "I1", locationTag: "target5", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); var itemForM1InClass1 = new TestInheritanceMemberItem( lineNumber: 13, memberName: "static void Class1.M1()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "I1.M1", locationTag: "target4", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); var itemForP1InI1 = new TestInheritanceMemberItem( lineNumber: 5, memberName: "int I1.P1 { get; set; }", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Class1.P1", locationTag: "target6", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); var itemForP1InClass1 = new TestInheritanceMemberItem( lineNumber: 14, memberName: "static int Class1.P1 { get; set; }", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "I1.P1", locationTag: "target7", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); var itemForE1InI1 = new TestInheritanceMemberItem( lineNumber: 6, memberName: "event EventHandler I1.e1", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Class1.e1", locationTag: "target8", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); var itemForE1InClass1 = new TestInheritanceMemberItem( lineNumber: 15, memberName: "static event EventHandler Class1.e1", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "I1.e1", locationTag: "target9", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); var itemForPlusOperatorInI1 = new TestInheritanceMemberItem( lineNumber: 7, memberName: "int I1.operator +(T)", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Class1.operator +", locationTag: "target10", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); var itemForPlusOperatorInClass1 = new TestInheritanceMemberItem( lineNumber: 16, memberName: "static int Class1.operator +(Class1)", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "I1.operator +", locationTag: "target11", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); var itemForIntOperatorInI1 = new TestInheritanceMemberItem( lineNumber: 8, memberName: "I1.implicit operator int(T)", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Class1.implicit operator int", locationTag: "target13", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); var itemForIntOperatorInClass1 = new TestInheritanceMemberItem( lineNumber: 17, memberName: "static Class1.implicit operator int(Class1)", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "I1.implicit operator int", locationTag: "target12", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); return VerifyInSingleDocumentAsync( markup, @@ -1284,26 +1299,26 @@ public partial class {|target3:Bar|} var itemOnLine2 = new TestInheritanceMemberItem( lineNumber: 2, memberName: "interface IBar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar", relationship: InheritanceRelationship.ImplementingType, - "target2", "target3"))); + "target2", "target3")]); var itemOnLine6 = new TestInheritanceMemberItem( lineNumber: 6, memberName: "class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); var itemOnLine10 = new TestInheritanceMemberItem( lineNumber: 10, memberName: "class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); return VerifyInSingleDocumentAsync( markup, @@ -1326,9 +1341,9 @@ public Task TestEmptyFileSingleGlobalImportInOtherFile(TestHost testHost) new TestInheritanceMemberItem( lineNumber: 0, memberName: string.Format(FeaturesResources.Directives_from_0, "Test2.cs"), - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "System", - relationship: InheritanceRelationship.InheritedImport, "target1")))); + relationship: InheritanceRelationship.InheritedImport, "target1")])); } [Theory, CombinatorialData] @@ -1345,13 +1360,15 @@ public Task TestEmptyFileMultipleGlobalImportInOtherFile(TestHost testHost) new TestInheritanceMemberItem( lineNumber: 0, memberName: string.Format(FeaturesResources.Directives_from_0, "Test2.cs"), - targets: ImmutableArray.Create( + targets: + [ new TargetInfo( - targetSymbolDisplayName: "System", - relationship: InheritanceRelationship.InheritedImport, "target1"), + targetSymbolDisplayName: "System", + relationship: InheritanceRelationship.InheritedImport, "target1"), new TargetInfo( targetSymbolDisplayName: "System.Collections", - relationship: InheritanceRelationship.InheritedImport, "target2")))); + relationship: InheritanceRelationship.InheritedImport, "target2"), + ])); } [Theory, CombinatorialData] @@ -1367,9 +1384,9 @@ public Task TestFileWithUsing_SingleGlobalImportInOtherFile(TestHost testHost) new TestInheritanceMemberItem( lineNumber: 1, memberName: string.Format(FeaturesResources.Directives_from_0, "Test2.cs"), - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "System", - relationship: InheritanceRelationship.InheritedImport, "target1")))); + relationship: InheritanceRelationship.InheritedImport, "target1")])); } [Theory, CombinatorialData] @@ -1386,9 +1403,9 @@ public Task TestIgnoreGlobalImportFromSameFile(TestHost testHost) new TestInheritanceMemberItem( lineNumber: 1, memberName: string.Format(FeaturesResources.Directives_from_0, "Test2.cs"), - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "System", - relationship: InheritanceRelationship.InheritedImport, "target1")))); + relationship: InheritanceRelationship.InheritedImport, "target1")])); } #endregion @@ -1423,18 +1440,18 @@ End Class var itemForBar = new TestInheritanceMemberItem( lineNumber: 3, memberName: "Class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IEnumerable", relationship: InheritanceRelationship.ImplementedInterface, - inMetadata: true))); + inMetadata: true)]); var itemForGetEnumerator = new TestInheritanceMemberItem( lineNumber: 5, memberName: "Function Bar.GetEnumerator() As IEnumerator", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IEnumerable.GetEnumerator", relationship: InheritanceRelationship.ImplementedMember, - inMetadata: true))); + inMetadata: true)]); return VerifyInSingleDocumentAsync(markup, LanguageNames.VisualBasic, testHost, itemForBar, itemForGetEnumerator); } @@ -1451,18 +1468,18 @@ Implements IBar var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, memberName: "Interface IBar", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemForBar = new TestInheritanceMemberItem( lineNumber: 4, memberName: "Class Bar", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); return VerifyInSingleDocumentAsync( markup, @@ -1485,18 +1502,18 @@ Inherits IBar2 var itemForIBar2 = new TestInheritanceMemberItem( lineNumber: 2, memberName: "Interface IBar2", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemForIBar = new TestInheritanceMemberItem( lineNumber: 4, memberName: "Interface IBar", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "IBar2", locationTag: "target2", - relationship: InheritanceRelationship.InheritedInterface))); + relationship: InheritanceRelationship.InheritedInterface)]); return VerifyInSingleDocumentAsync(markup, LanguageNames.VisualBasic, testHost, itemForIBar2, itemForIBar); } @@ -1513,18 +1530,18 @@ Inherits Bar2 var itemForBar2 = new TestInheritanceMemberItem( lineNumber: 2, memberName: "Class Bar2", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target1", - relationship: InheritanceRelationship.DerivedType))); + relationship: InheritanceRelationship.DerivedType)]); var itemForBar = new TestInheritanceMemberItem( lineNumber: 4, memberName: "Class Bar", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "Bar2", locationTag: "target2", - relationship: InheritanceRelationship.BaseType))); + relationship: InheritanceRelationship.BaseType)]); return VerifyInSingleDocumentAsync(markup, LanguageNames.VisualBasic, testHost, itemForBar2, itemForBar); } @@ -1561,18 +1578,19 @@ Implements IEnumerable new TestInheritanceMemberItem( lineNumber: 2, memberName: VBFeaturesResources.Project_level_Imports, - targets: ImmutableArray.Create( + targets: + [ new TargetInfo("System", InheritanceRelationship.InheritedImport), new TargetInfo("System.Collections.Generic", InheritanceRelationship.InheritedImport), - new TargetInfo("System.Linq", InheritanceRelationship.InheritedImport))), + new TargetInfo("System.Linq", InheritanceRelationship.InheritedImport), + ]), new TestInheritanceMemberItem( lineNumber: 3, memberName: "Class Bar", - targets: ImmutableArray.Create( - new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IEnumerable", relationship: InheritanceRelationship.ImplementedInterface, - inMetadata: true)))); + inMetadata: true)])); } [Theory, CombinatorialData] @@ -1590,34 +1608,34 @@ Implements IBar var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, memberName: "Interface IBar", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemForBar = new TestInheritanceMemberItem( lineNumber: 5, memberName: "Class Bar", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); var itemForEventInInterface = new TestInheritanceMemberItem( lineNumber: 3, memberName: "Event IBar.e As EventHandler", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "Bar.e", locationTag: "target3", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); var itemForEventInClass = new TestInheritanceMemberItem( lineNumber: 7, memberName: "Event Bar.e As EventHandler", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "IBar.e", locationTag: "target4", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); return VerifyInSingleDocumentAsync( markup, @@ -1644,34 +1662,34 @@ End Event var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, memberName: "Interface IBar", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemForBar = new TestInheritanceMemberItem( lineNumber: 5, memberName: "Class Bar", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); var itemForEventInInterface = new TestInheritanceMemberItem( lineNumber: 3, memberName: "Event IBar.e As EventHandler", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "Bar.e", locationTag: "target3", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); var itemForEventInClass = new TestInheritanceMemberItem( lineNumber: 7, memberName: "Event Bar.e As EventHandler", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "IBar.e", locationTag: "target4", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); return VerifyInSingleDocumentAsync( markup, @@ -1708,50 +1726,50 @@ End Function var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, memberName: "Interface IBar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemForBar = new TestInheritanceMemberItem( lineNumber: 7, memberName: "Class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); var itemForPooInInterface = new TestInheritanceMemberItem( lineNumber: 3, memberName: "Property IBar.Poo As Integer", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar.Poo", locationTag: "target3", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); var itemForPooInClass = new TestInheritanceMemberItem( lineNumber: 9, memberName: "Property Bar.Poo As Integer", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar.Poo", locationTag: "target4", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); var itemForFooInInterface = new TestInheritanceMemberItem( lineNumber: 4, memberName: "Function IBar.Foo() As Integer", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar.Foo", locationTag: "target5", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); var itemForFooInClass = new TestInheritanceMemberItem( lineNumber: 16, memberName: "Function Bar.Foo() As Integer", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar.Foo", locationTag: "target6", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); return VerifyInSingleDocumentAsync( markup, @@ -1781,34 +1799,34 @@ End Sub var itemForBar1 = new TestInheritanceMemberItem( lineNumber: 2, memberName: "Class Bar1", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: $"Bar", locationTag: "target1", - relationship: InheritanceRelationship.DerivedType))); + relationship: InheritanceRelationship.DerivedType)]); var itemForBar = new TestInheritanceMemberItem( lineNumber: 6, memberName: "Class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar1", locationTag: "target2", - relationship: InheritanceRelationship.BaseType))); + relationship: InheritanceRelationship.BaseType)]); var itemForFooInBar1 = new TestInheritanceMemberItem( lineNumber: 3, memberName: "MustOverride Sub Bar1.Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar.Foo", locationTag: "target3", - relationship: InheritanceRelationship.OverridingMember))); + relationship: InheritanceRelationship.OverridingMember)]); var itemForFooInBar = new TestInheritanceMemberItem( lineNumber: 8, memberName: "Overrides Sub Bar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar1.Foo", locationTag: "target4", - relationship: InheritanceRelationship.OverriddenMember))); + relationship: InheritanceRelationship.OverriddenMember)]); return VerifyInSingleDocumentAsync( markup, @@ -1859,77 +1877,93 @@ End Sub var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, memberName: "Interface IBar", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Bar1", - locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType), + targets: + [ + new TargetInfo( + targetSymbolDisplayName: "Bar1", + locationTag: "target1", + relationship: InheritanceRelationship.ImplementingType), new TargetInfo( targetSymbolDisplayName: "Bar2", locationTag: "target5", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType), + ]); var itemForFooInIBar = new TestInheritanceMemberItem( lineNumber: 3, memberName: "Sub IBar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Bar1.Foo", - locationTag: "target2", - relationship: InheritanceRelationship.ImplementingMember), + targets: + [ + new TargetInfo( + targetSymbolDisplayName: "Bar1.Foo", + locationTag: "target2", + relationship: InheritanceRelationship.ImplementingMember), new TargetInfo( targetSymbolDisplayName: "Bar2.Foo", locationTag: "target3", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember), + ]); var itemForBar1 = new TestInheritanceMemberItem( lineNumber: 6, memberName: "Class Bar1", - targets: ImmutableArray.Create( + targets: + [ new TargetInfo( - targetSymbolDisplayName: "Bar2", - locationTag: "target5", - relationship: InheritanceRelationship.DerivedType), + targetSymbolDisplayName: "Bar2", + locationTag: "target5", + relationship: InheritanceRelationship.DerivedType), new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target4", relationship: InheritanceRelationship.ImplementedInterface) - )); +, + ]); var itemForFooInBar1 = new TestInheritanceMemberItem( lineNumber: 8, memberName: "Overridable Sub Bar1.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "IBar.Foo", - locationTag: "target6", - relationship: InheritanceRelationship.ImplementedMember), + targets: + [ + new TargetInfo( + targetSymbolDisplayName: "IBar.Foo", + locationTag: "target6", + relationship: InheritanceRelationship.ImplementedMember), new TargetInfo( targetSymbolDisplayName: "Bar2.Foo", locationTag: "target3", - relationship: InheritanceRelationship.OverridingMember))); + relationship: InheritanceRelationship.OverridingMember), + ]); var itemForBar2 = new TestInheritanceMemberItem( lineNumber: 12, memberName: "Class Bar2", - targets: ImmutableArray.Create( + targets: + [ new TargetInfo( - targetSymbolDisplayName: "Bar1", - locationTag: "target1", - relationship: InheritanceRelationship.BaseType), + targetSymbolDisplayName: "Bar1", + locationTag: "target1", + relationship: InheritanceRelationship.BaseType), new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target4", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface), + ]); var itemForFooInBar2 = new TestInheritanceMemberItem( lineNumber: 14, memberName: "Overrides Sub Bar2.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "IBar.Foo", - locationTag: "target6", - relationship: InheritanceRelationship.ImplementedMember), + targets: + [ + new TargetInfo( + targetSymbolDisplayName: "IBar.Foo", + locationTag: "target6", + relationship: InheritanceRelationship.ImplementedMember), new TargetInfo( targetSymbolDisplayName: "Bar1.Foo", locationTag: "target2", - relationship: InheritanceRelationship.OverriddenMember))); + relationship: InheritanceRelationship.OverriddenMember), + ]); return VerifyInSingleDocumentAsync( testDuplicate ? markup2 : markup1, @@ -1967,46 +2001,49 @@ End Sub var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, memberName: "Interface IBar(Of T)", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemForFooInIBar = new TestInheritanceMemberItem( lineNumber: 3, memberName: "Sub IBar(Of T).Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Bar.Foo", - locationTag: "target3", + targets: + [ + new TargetInfo( + targetSymbolDisplayName: "Bar.Foo", + locationTag: "target3", + relationship: InheritanceRelationship.ImplementingMember), + new TargetInfo( + targetSymbolDisplayName: "Bar.IBar_Foo", + locationTag: "target4", relationship: InheritanceRelationship.ImplementingMember), - new TargetInfo( - targetSymbolDisplayName: "Bar.IBar_Foo", - locationTag: "target4", - relationship: InheritanceRelationship.ImplementingMember))); + ]); var itemForBar = new TestInheritanceMemberItem( lineNumber: 6, memberName: "Class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar(Of T)", locationTag: "target5", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); var itemForFooInBar = new TestInheritanceMemberItem( lineNumber: 10, memberName: "Sub Bar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar(Of T).Foo", locationTag: "target6", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); var itemForIBar_FooInBar = new TestInheritanceMemberItem( lineNumber: 14, memberName: "Sub Bar.IBar_Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar(Of T).Foo", locationTag: "target6", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); return VerifyInSingleDocumentAsync( markup, @@ -2044,34 +2081,34 @@ End Interface var itemForBar = new TestInheritanceMemberItem( lineNumber: 5, memberName: "class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); var itemForFooInMarkup1 = new TestInheritanceMemberItem( lineNumber: 7, memberName: "void Bar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar.Foo", locationTag: "target3", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); var itemForIBar = new TestInheritanceMemberItem( lineNumber: 3, memberName: "Interface IBar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemForFooInMarkup2 = new TestInheritanceMemberItem( lineNumber: 4, memberName: "Sub IBar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar.Foo", locationTag: "target4", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); return VerifyInDifferentProjectsAsync( (markup1, LanguageNames.CSharp), @@ -2107,42 +2144,44 @@ public interface {|target1:IBar|} new TestInheritanceMemberItem( lineNumber: 2, memberName: VBFeaturesResources.Project_level_Imports, - targets: ImmutableArray.Create( + targets: + [ new TargetInfo("System", InheritanceRelationship.InheritedImport), new TargetInfo("System.Collections.Generic", InheritanceRelationship.InheritedImport), - new TargetInfo("System.Linq", InheritanceRelationship.InheritedImport))); + new TargetInfo("System.Linq", InheritanceRelationship.InheritedImport), + ]); var itemForBar44 = new TestInheritanceMemberItem( lineNumber: 4, memberName: "Class Bar44", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); var itemForFooInMarkup1 = new TestInheritanceMemberItem( lineNumber: 7, memberName: "Sub Bar44.Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar.Foo", locationTag: "target3", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); var itemForIBar = new TestInheritanceMemberItem( lineNumber: 4, memberName: "interface IBar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar44", locationTag: "target2", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemForFooInMarkup2 = new TestInheritanceMemberItem( lineNumber: 6, memberName: "void IBar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar44.Foo", locationTag: "target4", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); return VerifyInDifferentProjectsAsync( (markup1, LanguageNames.VisualBasic), @@ -2177,35 +2216,37 @@ End Class var itemForBarInMarkup1 = new TestInheritanceMemberItem( lineNumber: 5, memberName: "class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); var itemForIBar = new TestInheritanceMemberItem( lineNumber: 3, memberName: "Interface IBar", - targets: ImmutableArray.Create( + targets: + [ new TargetInfo( - targetSymbolDisplayName: "Bar", - locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType, - languageGlyph: Glyph.CSharpFile, - projectName: "Assembly1"), + targetSymbolDisplayName: "Bar", + locationTag: "target1", + relationship: InheritanceRelationship.ImplementingType, + languageGlyph: Glyph.CSharpFile, + projectName: "Assembly1"), new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target3", relationship: InheritanceRelationship.ImplementingType, languageGlyph: Glyph.BasicFile, - projectName: "Assembly2"))); + projectName: "Assembly2"), + ]); var itemForBarInMarkup2 = new TestInheritanceMemberItem( lineNumber: 6, memberName: "Class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); return VerifyInDifferentProjectsAsync( (markup1, LanguageNames.CSharp), @@ -2240,35 +2281,37 @@ public class {|target3:Bar|} var itemForBarInMarkup1 = new TestInheritanceMemberItem( lineNumber: 5, memberName: "class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); var itemForIBar = new TestInheritanceMemberItem( lineNumber: 3, memberName: "interface IBar", - targets: ImmutableArray.Create( + targets: + [ new TargetInfo( - targetSymbolDisplayName: "Bar", - locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType, - languageGlyph: Glyph.CSharpFile, - projectName: "Assembly1"), + targetSymbolDisplayName: "Bar", + locationTag: "target1", + relationship: InheritanceRelationship.ImplementingType, + languageGlyph: Glyph.CSharpFile, + projectName: "Assembly1"), new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target3", relationship: InheritanceRelationship.ImplementingType, languageGlyph: Glyph.CSharpFile, - projectName: "Assembly2"))); + projectName: "Assembly2"), + ]); var itemForBarInMarkup2 = new TestInheritanceMemberItem( lineNumber: 6, memberName: "class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); return VerifyInDifferentProjectsAsync( (markup1, LanguageNames.CSharp), @@ -2342,9 +2385,9 @@ await VerifyInSingleDocumentAsync(correctSearchingCode, memberItems: [new TestInheritanceMemberItem( lineNumber: 2, memberName: "class B", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "C", locationTag: "target", - relationship: InheritanceRelationship.BaseType)))]); + relationship: InheritanceRelationship.BaseType)])]); } } From a8d68d84add1e398739c7275014f0c23bde01425 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 13 Nov 2024 21:12:16 -0800 Subject: [PATCH 344/508] Update inheritance margin as well --- .../InheritanceMarginTests.cs | 104 ++++++++++++++++++ ...bstractInheritanceMarginService_Helpers.cs | 24 ++-- .../InheritanceMarginItem.cs | 3 + 3 files changed, 117 insertions(+), 14 deletions(-) diff --git a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs index 6035adf0ba04b..b066d4445034e 100644 --- a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs +++ b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs @@ -229,6 +229,9 @@ public TestInheritanceMemberItem( MemberName = memberName; Targets = targets; } + + public override string ToString() + => MemberName; } private class TargetInfo @@ -1028,6 +1031,107 @@ public class {|target5:Bar2|} : Bar1, IBar itemForFooInBar2); } + + [Theory] + [InlineData("abstract", TestHost.InProcess)] + [InlineData("abstract", TestHost.OutOfProcess)] + [InlineData("virtual", TestHost.InProcess)] + [InlineData("virtual", TestHost.OutOfProcess)] + public Task TestCSharpAbstractClassMembers_LooseMatch(string modifier, TestHost testHost) + { + var markup = $@"using System; + public abstract class {{|target2:Bar|}} + {{ + public {modifier} void {{|target4:Foo|}}(); + public {modifier} int {{|target6:Poo|}} {{ get; set; }} + public {modifier} event EventHandler {{|target8:Eoo|}}; + }} + public class {{|target1:Bar2|}} : Bar + {{ + public override void {{|target3:Foo|}}(int i) {{ }} + public override string {{|target5:Poo|}} {{ get; set; }} + public override event Action {{|target7:Eoo|}}; + }} + "; + + var itemForEooInClass = new TestInheritanceMemberItem( + lineNumber: 12, + memberName: "override event Action Bar2.Eoo", + targets: [new TargetInfo( + targetSymbolDisplayName: $"Bar.Eoo", + locationTag: "target8", + relationship: InheritanceRelationship.OverriddenMember)]); + + var itemForEooInAbstractClass = new TestInheritanceMemberItem( + lineNumber: 6, + memberName: $"{modifier} event EventHandler Bar.Eoo", + targets: [new TargetInfo( + targetSymbolDisplayName: "Bar2.Eoo", + locationTag: "target7", + relationship: InheritanceRelationship.OverridingMember)]); + + var itemForPooInClass = new TestInheritanceMemberItem( + lineNumber: 11, + memberName: "override string Bar2.Poo { get; set; }", + targets: [new TargetInfo( + targetSymbolDisplayName: $"Bar.Poo", + locationTag: "target6", + relationship: InheritanceRelationship.OverriddenMember)]); + + var itemForPooInAbstractClass = new TestInheritanceMemberItem( + lineNumber: 5, + memberName: $"{modifier} int Bar.Poo {{ get; set; }}", + targets: [new TargetInfo( + targetSymbolDisplayName: "Bar2.Poo", + locationTag: "target5", + relationship: InheritanceRelationship.OverridingMember)]); + + var itemForFooInAbstractClass = new TestInheritanceMemberItem( + lineNumber: 4, + memberName: $"{modifier} void Bar.Foo()", + targets: [new TargetInfo( + targetSymbolDisplayName: "Bar2.Foo", + locationTag: "target3", + relationship: InheritanceRelationship.OverridingMember)]); + + var itemForFooInClass = new TestInheritanceMemberItem( + lineNumber: 10, + memberName: "override void Bar2.Foo(int)", + targets: [new TargetInfo( + targetSymbolDisplayName: $"Bar.Foo", + locationTag: "target4", + relationship: InheritanceRelationship.OverriddenMember)]); + + var itemForBar = new TestInheritanceMemberItem( + lineNumber: 2, + memberName: "class Bar", + targets: [new TargetInfo( + targetSymbolDisplayName: "Bar2", + locationTag: "target1", + relationship: InheritanceRelationship.DerivedType)]); + + var itemForBar2 = new TestInheritanceMemberItem( + lineNumber: 8, + memberName: "class Bar2", + targets: [new TargetInfo( + targetSymbolDisplayName: "Bar", + locationTag: "target2", + relationship: InheritanceRelationship.BaseType)]); + + return VerifyInSingleDocumentAsync( + markup, + LanguageNames.CSharp, + testHost, + itemForBar, + itemForBar2, + itemForFooInAbstractClass, + itemForFooInClass, + itemForPooInClass, + itemForPooInAbstractClass, + itemForEooInClass, + itemForEooInAbstractClass); + } + [Theory, CombinatorialData] public Task TestCSharpFindGenericsBaseType(TestHost testHost) { diff --git a/src/Features/Core/Portable/InheritanceMargin/AbstractInheritanceMarginService_Helpers.cs b/src/Features/Core/Portable/InheritanceMargin/AbstractInheritanceMarginService_Helpers.cs index e92451625a619..5257ed9709237 100644 --- a/src/Features/Core/Portable/InheritanceMargin/AbstractInheritanceMarginService_Helpers.cs +++ b/src/Features/Core/Portable/InheritanceMargin/AbstractInheritanceMarginService_Helpers.cs @@ -371,7 +371,7 @@ private static async ValueTask AddInheritanceMemberItemsForMembersAsync( var allOverridingSymbols = await SymbolFinder.FindOverridesArrayAsync(memberSymbol, solution, cancellationToken: cancellationToken).ConfigureAwait(false); // Go up the inheritance chain to find all overridden targets - var overriddenSymbols = GetOverriddenSymbols(memberSymbol); + var overriddenSymbols = GetOverriddenSymbols(memberSymbol, allowLooseMatch: true); // Go up the inheritance chain to find all the implemented targets. var implementedSymbols = GetImplementedSymbolsForTypeMember(memberSymbol, overriddenSymbols); @@ -650,24 +650,20 @@ private static async Task> GetImplementingSymbolsForType /// /// Get overridden members the . /// - private static ImmutableArray GetOverriddenSymbols(ISymbol memberSymbol) + private static ImmutableArray GetOverriddenSymbols(ISymbol memberSymbol, bool allowLooseMatch) { if (memberSymbol is INamedTypeSymbol) - { return []; - } - else - { - using var _ = ArrayBuilder.GetInstance(out var builder); - for (var overriddenMember = memberSymbol.GetOverriddenMember(); - overriddenMember != null; - overriddenMember = overriddenMember.GetOverriddenMember()) - { - builder.Add(overriddenMember.OriginalDefinition); - } - return builder.ToImmutableArray(); + using var _ = ArrayBuilder.GetInstance(out var builder); + for (var overriddenMember = memberSymbol.GetOverriddenMember(allowLooseMatch); + overriddenMember != null; + overriddenMember = overriddenMember.GetOverriddenMember(allowLooseMatch)) + { + builder.Add(overriddenMember.OriginalDefinition); } + + return builder.ToImmutableAndClear(); } /// diff --git a/src/Features/Core/Portable/InheritanceMargin/InheritanceMarginItem.cs b/src/Features/Core/Portable/InheritanceMargin/InheritanceMarginItem.cs index 4e17a45b3c04e..5cfa259e41311 100644 --- a/src/Features/Core/Portable/InheritanceMargin/InheritanceMarginItem.cs +++ b/src/Features/Core/Portable/InheritanceMargin/InheritanceMarginItem.cs @@ -55,6 +55,9 @@ public override int GetHashCode() public override bool Equals(object? obj) => obj is InheritanceMarginItem item && Equals(item); + public override string ToString() + => string.Join("", DisplayTexts.Select(d => d.Text)); + public bool Equals(InheritanceMarginItem other) => this.LineNumber == other.LineNumber && this.TopLevelDisplayText == other.TopLevelDisplayText && From e7cd138fceb3aab4364201b4590ad46d42f34c4d Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Thu, 14 Nov 2024 14:02:02 +0100 Subject: [PATCH 345/508] Parse directives in trailing trivia as skipped tokens trivia (#75724) * Handle directives in trailing trivia * Revert the change and improve tests * Attach trailing directives as skipped tokens trivia * Update tests * Parse as bad directive trivia * Simplify tests * Remove trailing parameter * Parse as skipped tokens trivia * Carry trivia over * Wrap trivia in a token * Put text into one token * Simplify width logic * Refactor to simplify the code * Add more tests * Pass `false` and explain --- .../CSharp/Portable/Parser/DirectiveParser.cs | 28 +- src/Compilers/CSharp/Portable/Parser/Lexer.cs | 67 +- .../Syntax/LexicalAndXml/PreprocessorTests.cs | 4 +- .../Parsing/ParsingErrorRecoveryTests.cs | 572 ++++++++++++++++++ .../Test/Syntax/Syntax/SyntaxNodeTests.cs | 49 +- .../SyntacticClassifierTests_Preprocessor.cs | 27 + .../DiagnosticAnalyzerQuickInfoSourceTests.cs | 12 +- .../RegionDirectiveStructureTests.cs | 14 + .../CSharpTest/Formatting/FormattingTests.cs | 3 +- 9 files changed, 720 insertions(+), 56 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Parser/DirectiveParser.cs b/src/Compilers/CSharp/Portable/Parser/DirectiveParser.cs index ef1f8ebe9cdf7..7c6641d1fd3a1 100644 --- a/src/Compilers/CSharp/Portable/Parser/DirectiveParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/DirectiveParser.cs @@ -31,15 +31,10 @@ public void ReInitialize(DirectiveStack context) public CSharpSyntaxNode ParseDirective( bool isActive, bool endIsActive, - bool isAfterFirstTokenInFile, - bool isAfterNonWhitespaceOnLine) + bool isFollowingToken) { var hashPosition = lexer.TextWindow.Position; var hash = this.EatToken(SyntaxKind.HashToken, false); - if (isAfterNonWhitespaceOnLine) - { - hash = this.AddError(hash, ErrorCode.ERR_BadDirectivePlacement); - } // The behavior of these directives when isActive is false is somewhat complicated. // The key functions in the native compiler are ScanPreprocessorIfSection and @@ -81,7 +76,7 @@ public CSharpSyntaxNode ParseDirective( case SyntaxKind.DefineKeyword: case SyntaxKind.UndefKeyword: - result = this.ParseDefineOrUndefDirective(hash, this.EatContextualToken(contextualKind), isActive, isAfterFirstTokenInFile && !isAfterNonWhitespaceOnLine); + result = this.ParseDefineOrUndefDirective(hash, this.EatContextualToken(contextualKind), isActive, isFollowingToken); break; case SyntaxKind.ErrorKeyword: @@ -101,11 +96,11 @@ public CSharpSyntaxNode ParseDirective( break; case SyntaxKind.ReferenceKeyword: - result = this.ParseReferenceDirective(hash, this.EatContextualToken(contextualKind), isActive, isAfterFirstTokenInFile && !isAfterNonWhitespaceOnLine); + result = this.ParseReferenceDirective(hash, this.EatContextualToken(contextualKind), isActive, isFollowingToken); break; case SyntaxKind.LoadKeyword: - result = this.ParseLoadDirective(hash, this.EatContextualToken(contextualKind), isActive, isAfterFirstTokenInFile && !isAfterNonWhitespaceOnLine); + result = this.ParseLoadDirective(hash, this.EatContextualToken(contextualKind), isActive, isFollowingToken); break; case SyntaxKind.NullableKeyword: @@ -121,16 +116,13 @@ public CSharpSyntaxNode ParseDirective( { var id = this.EatToken(SyntaxKind.IdentifierToken, false); var end = this.ParseEndOfDirective(ignoreErrors: true); - if (!isAfterNonWhitespaceOnLine) + if (!id.IsMissing) { - if (!id.IsMissing) - { - id = this.AddError(id, ErrorCode.ERR_PPDirectiveExpected); - } - else - { - hash = this.AddError(hash, ErrorCode.ERR_PPDirectiveExpected); - } + id = this.AddError(id, ErrorCode.ERR_PPDirectiveExpected); + } + else + { + hash = this.AddError(hash, ErrorCode.ERR_PPDirectiveExpected); } result = SyntaxFactory.BadDirectiveTrivia(hash, id, end, isActive); diff --git a/src/Compilers/CSharp/Portable/Parser/Lexer.cs b/src/Compilers/CSharp/Portable/Parser/Lexer.cs index a34f9cdd9b3a3..03b167ef6460f 100644 --- a/src/Compilers/CSharp/Portable/Parser/Lexer.cs +++ b/src/Compilers/CSharp/Portable/Parser/Lexer.cs @@ -303,7 +303,7 @@ private static int GetFullWidth(SyntaxListBuilder? builder) private SyntaxToken LexSyntaxToken() { _leadingTriviaCache.Clear(); - this.LexSyntaxTrivia(afterFirstToken: TextWindow.Position > 0, isTrailing: false, triviaList: ref _leadingTriviaCache); + this.LexSyntaxTrivia(isFollowingToken: TextWindow.Position > 0, isTrailing: false, triviaList: ref _leadingTriviaCache); var leading = _leadingTriviaCache; var tokenInfo = default(TokenInfo); @@ -313,7 +313,7 @@ private SyntaxToken LexSyntaxToken() var errors = this.GetErrors(GetFullWidth(leading)); _trailingTriviaCache.Clear(); - this.LexSyntaxTrivia(afterFirstToken: true, isTrailing: true, triviaList: ref _trailingTriviaCache); + this.LexSyntaxTrivia(isFollowingToken: true, isTrailing: true, triviaList: ref _trailingTriviaCache); var trailing = _trailingTriviaCache; return Create(in tokenInfo, leading, trailing, errors); @@ -322,7 +322,7 @@ private SyntaxToken LexSyntaxToken() internal SyntaxTriviaList LexSyntaxLeadingTrivia() { _leadingTriviaCache.Clear(); - this.LexSyntaxTrivia(afterFirstToken: TextWindow.Position > 0, isTrailing: false, triviaList: ref _leadingTriviaCache); + this.LexSyntaxTrivia(isFollowingToken: TextWindow.Position > 0, isTrailing: false, triviaList: ref _leadingTriviaCache); return new SyntaxTriviaList(default(Microsoft.CodeAnalysis.SyntaxToken), _leadingTriviaCache.ToListNode(), position: 0, index: 0); } @@ -330,7 +330,7 @@ internal SyntaxTriviaList LexSyntaxLeadingTrivia() internal SyntaxTriviaList LexSyntaxTrailingTrivia() { _trailingTriviaCache.Clear(); - this.LexSyntaxTrivia(afterFirstToken: true, isTrailing: true, triviaList: ref _trailingTriviaCache); + this.LexSyntaxTrivia(isFollowingToken: true, isTrailing: true, triviaList: ref _trailingTriviaCache); return new SyntaxTriviaList(default(Microsoft.CodeAnalysis.SyntaxToken), _trailingTriviaCache.ToListNode(), position: 0, index: 0); } @@ -1889,7 +1889,7 @@ private bool ScanIdentifierOrKeyword(ref TokenInfo info) } } - private void LexSyntaxTrivia(bool afterFirstToken, bool isTrailing, ref SyntaxListBuilder triviaList) + private void LexSyntaxTrivia(bool isFollowingToken, bool isTrailing, ref SyntaxListBuilder triviaList) { bool onlyWhitespaceOnLine = !isTrailing; @@ -1995,7 +1995,33 @@ private void LexSyntaxTrivia(bool afterFirstToken, bool isTrailing, ref SyntaxLi case '#': if (_allowPreprocessorDirectives) { - this.LexDirectiveAndExcludedTrivia(afterFirstToken, isTrailing || !onlyWhitespaceOnLine, ref triviaList); + if (isTrailing || !onlyWhitespaceOnLine) + { + // Directives cannot be in trailing trivia. + // We parse the directive (ignoring its effects like disabled text or defined symbols). + // We extract the text corresponding to the parsed directive + // and add it as a BadToken in SkippedTokensTrivia. + + var savePosition = TextWindow.Position; + + // All the `false` arguments affect only error reporting and the resulting node, both of which we discard. + // However, passing `false` can skip some unnecessary checks. + this.ParseDirective(isActive: false, endIsActive: false, isFollowingToken: false); + + var text = TextWindow.Text.GetSubText(TextSpan.FromBounds(savePosition, TextWindow.Position)); + + var error = new SyntaxDiagnosticInfo(offset: 0, width: 1, code: ErrorCode.ERR_BadDirectivePlacement); + + var token = SyntaxFactory.BadToken(null, text.ToString(), null).WithDiagnosticsGreen([error]); + + this.AddTrivia(SyntaxFactory.SkippedTokensTrivia(token), ref triviaList); + } + else + { + this.LexDirectiveAndExcludedTrivia(isFollowingToken, ref triviaList); + } + + onlyWhitespaceOnLine = true; break; } else @@ -2332,13 +2358,12 @@ private static SyntaxTrivia CreateWhitespaceTrivia(SlidingTextWindow textWindow) } private void LexDirectiveAndExcludedTrivia( - bool afterFirstToken, - bool afterNonWhitespaceOnLine, + bool isFollowingToken, ref SyntaxListBuilder triviaList) { - var directive = this.LexSingleDirective(true, true, afterFirstToken, afterNonWhitespaceOnLine, ref triviaList); + var directive = this.LexSingleDirective(true, true, isFollowingToken, ref triviaList); - // also lex excluded stuff + // also lex excluded stuff var branching = directive as BranchingDirectiveTriviaSyntax; if (branching != null && !branching.BranchTaken) { @@ -2362,7 +2387,7 @@ private void LexExcludedDirectivesAndTrivia(bool endIsActive, ref SyntaxListBuil break; } - var directive = this.LexSingleDirective(false, endIsActive, false, false, ref triviaList); + var directive = this.LexSingleDirective(false, endIsActive, false, ref triviaList); var branching = directive as BranchingDirectiveTriviaSyntax; if (directive.Kind == SyntaxKind.EndIfDirectiveTrivia || (branching != null && branching.BranchTaken)) { @@ -2378,8 +2403,7 @@ private void LexExcludedDirectivesAndTrivia(bool endIsActive, ref SyntaxListBuil private CSharpSyntaxNode LexSingleDirective( bool isActive, bool endIsActive, - bool afterFirstToken, - bool afterNonWhitespaceOnLine, + bool isFollowingToken, ref SyntaxListBuilder triviaList) { if (SyntaxFacts.IsWhitespace(TextWindow.PeekChar())) @@ -2388,16 +2412,25 @@ private CSharpSyntaxNode LexSingleDirective( this.AddTrivia(this.ScanWhitespace(), ref triviaList); } - CSharpSyntaxNode directive; + CSharpSyntaxNode directive = ParseDirective(isActive, endIsActive, isFollowingToken); + + this.AddTrivia(directive, ref triviaList); + _directives = directive.ApplyDirectives(_directives); + return directive; + } + + private CSharpSyntaxNode ParseDirective( + bool isActive, + bool endIsActive, + bool isFollowingToken) + { var saveMode = _mode; _directiveParser ??= new DirectiveParser(this); _directiveParser.ReInitialize(_directives); - directive = _directiveParser.ParseDirective(isActive, endIsActive, afterFirstToken, afterNonWhitespaceOnLine); + CSharpSyntaxNode directive = _directiveParser.ParseDirective(isActive, endIsActive, isFollowingToken); - this.AddTrivia(directive, ref triviaList); - _directives = directive.ApplyDirectives(_directives); _mode = saveMode; return directive; } diff --git a/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/PreprocessorTests.cs b/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/PreprocessorTests.cs index 152046ecb9111..939f4b350e8fe 100644 --- a/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/PreprocessorTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/PreprocessorTests.cs @@ -2426,7 +2426,7 @@ class A { } #define XXX var node = Parse(text); TestRoundTripping(node, text, false); VerifyErrorCode(node, (int)ErrorCode.ERR_BadDirectivePlacement); // CS1040 - VerifyDirectivesSpecial(node, new DirectiveInfo { Kind = SyntaxKind.DefineDirectiveTrivia, Status = NodeStatus.IsActive, Text = "XXX" }); + VerifyDirectivesSpecial(node); } [Fact] @@ -2587,7 +2587,7 @@ class A { } #undef XXX var node = Parse(text); TestRoundTripping(node, text, false); VerifyErrorCode(node, (int)ErrorCode.ERR_BadDirectivePlacement); - VerifyDirectivesSpecial(node, new DirectiveInfo { Kind = SyntaxKind.UndefDirectiveTrivia, Status = NodeStatus.IsActive, Text = "XXX" }); + VerifyDirectivesSpecial(node); } [Fact] diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingErrorRecoveryTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingErrorRecoveryTests.cs index 268b149eab6d4..608a21ab96e73 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingErrorRecoveryTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingErrorRecoveryTests.cs @@ -8110,5 +8110,577 @@ public void RazorCommentRecovery_NoStart() } EOF(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void PreprocessorDirective_Trailing_01() + { + UsingTree(""" + if (#if) + """, + // (1,5): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // if (#if) + Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(1, 5), + // (1,9): error CS1733: Expected expression + // if (#if) + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 9), + // (1,9): error CS1026: ) expected + // if (#if) + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(1, 9), + // (1,9): error CS1733: Expected expression + // if (#if) + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 9), + // (1,9): error CS1002: ; expected + // if (#if) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 9)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.IfStatement); + { + N(SyntaxKind.IfKeyword); + N(SyntaxKind.OpenParenToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void PreprocessorDirective_Trailing_01_WhitespaceBeforeHash() + { + UsingTree(""" + if ( #if) + """, + // (1,5): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // if ( #if) + Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(1, 6), + // (1,9): error CS1733: Expected expression + // if ( #if) + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 10), + // (1,9): error CS1026: ) expected + // if ( #if) + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(1, 10), + // (1,9): error CS1733: Expected expression + // if ( #if) + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 10), + // (1,9): error CS1002: ; expected + // if ( #if) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 10)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.IfStatement); + { + N(SyntaxKind.IfKeyword); + N(SyntaxKind.OpenParenToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void PreprocessorDirective_Trailing_01_WhitespaceAfterHash() + { + UsingTree(""" + if ( # if) + """, + // (1,5): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // if ( # if) + Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(1, 6), + // (1,9): error CS1733: Expected expression + // if ( # if) + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 11), + // (1,9): error CS1026: ) expected + // if ( # if) + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(1, 11), + // (1,9): error CS1733: Expected expression + // if ( # if) + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 11), + // (1,9): error CS1002: ; expected + // if ( # if) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 11)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.IfStatement); + { + N(SyntaxKind.IfKeyword); + N(SyntaxKind.OpenParenToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void PreprocessorDirective_Trailing_02() + { + UsingTree(""" + if (#if false + x + #else + y + #endif + """, + // (1,5): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // if (#if false + Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(1, 5), + // (2,2): error CS1026: ) expected + // x + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(2, 2), + // (3,1): error CS1028: Unexpected preprocessor directive + // #else + Diagnostic(ErrorCode.ERR_UnexpectedDirective, "#else").WithLocation(3, 1), + // (4,2): error CS1002: ; expected + // y + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 2), + // (5,1): error CS1028: Unexpected preprocessor directive + // #endif + Diagnostic(ErrorCode.ERR_UnexpectedDirective, "#endif").WithLocation(5, 1)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.IfStatement); + { + N(SyntaxKind.IfKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + M(SyntaxKind.CloseParenToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + M(SyntaxKind.SemicolonToken); + } + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void PreprocessorDirective_Trailing_03() + { + UsingTree(""" + a(); + #if false + b(); + /* comment */ #else + c(); + #endif + """); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void PreprocessorDirective_Trailing_04() + { + UsingTree(""" + a(); + #if true + b(); + /* comment */ #elif false + c(); + #endif + """, + // (4,15): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // /* comment */ #elif false + Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(4, 15)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void PreprocessorDirective_Trailing_05() + { + UsingTree(""" + a(); + #if true + b(); + /* comment */ #endif + #else + c(); + #endif + d(); + """, + // (4,15): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // /* comment */ #endif + Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(4, 15)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "d"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void PreprocessorDirective_Trailing_Define() + { + UsingTree(""" + /* comment */ #define ABC + #if ABC + x(); + #else + y(); + #endif + """, + // (1,15): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // /* comment */ #define ABC + Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(1, 15)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void PreprocessorDirective_Trailing_Undefine() + { + UsingTree(""" + #define ABC + /* comment */ #undefine ABC + #if ABC + x(); + #else + y(); + #endif + """, + // (2,15): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // /* comment */ #undefine ABC + Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(2, 15)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void PreprocessorDirective_Trailing_ErrorWarning() + { + UsingTree(""" + /* comment */ #error E1 + /* comment */ #warning W1 + #error E2 + #warning W2 + """, + // (1,15): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // /* comment */ #error E1 + Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(1, 15), + // (2,15): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // /* comment */ #warning W1 + Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(2, 15), + // (3,8): error CS1029: #error: 'E2' + // #error E2 + Diagnostic(ErrorCode.ERR_ErrorDirective, "E2").WithArguments("E2").WithLocation(3, 8), + // (4,10): warning CS1030: #warning: 'W2' + // #warning W2 + Diagnostic(ErrorCode.WRN_WarningDirective, "W2").WithArguments("W2").WithLocation(4, 10)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void PreprocessorDirective_Trailing_Line() + { + UsingTree(""" + #line 200 + /* comment */ #line 100 + #error E1 + """, + // (200,15): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // /* comment */ #line 100 + Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(200, 15), + // (201,8): error CS1029: #error: 'E1' + // #error E1 + Diagnostic(ErrorCode.ERR_ErrorDirective, "E1").WithArguments("E1").WithLocation(201, 8)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void PreprocessorDirective_Trailing_Pragma() + { + CreateCompilation(""" + #pragma warning disable 8321 + /* comment */ #pragma warning restore 8321 + void f() { } + #pragma warning restore 8321 + void g() { } + """).VerifyDiagnostics( + // (2,15): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // /* comment */ #pragma warning restore 8321 + Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(2, 15), + // (5,6): warning CS8321: The local function 'g' is declared but never used + // void g() { } + Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "g").WithArguments("g").WithLocation(5, 6)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void PreprocessorDirective_Trailing_Nullable() + { + CreateCompilation(""" + #nullable disable + /* comment */ #nullable enable + _ = (object)null; + #nullable enable + _ = (object)null; // 1 + """).VerifyDiagnostics( + // (2,15): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // /* comment */ #nullable enable + Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(2, 15), + // (5,5): warning CS8600: Converting null literal or possible null value to non-nullable type. + // _ = (object)null; // 1 + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(object)null").WithLocation(5, 5)); + } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNodeTests.cs b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNodeTests.cs index 71967a077afe7..f9c4a90d8d350 100644 --- a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNodeTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNodeTests.cs @@ -347,24 +347,21 @@ public void TestGetAllDirectivesUsingDescendantNodes() public void TestContainsDirective() { // Empty compilation unit shouldn't have any directives in it. - for (var kind = SyntaxKind.TildeToken; kind < SyntaxKind.ScopedKeyword; kind++) + for (var kind = SyntaxKind.TildeToken; kind < SyntaxKind.XmlElement; kind++) Assert.False(SyntaxFactory.ParseCompilationUnit("").ContainsDirective(kind)); // basic file shouldn't have any directives in it. - for (var kind = SyntaxKind.TildeToken; kind < SyntaxKind.ScopedKeyword; kind++) + for (var kind = SyntaxKind.TildeToken; kind < SyntaxKind.XmlElement; kind++) Assert.False(SyntaxFactory.ParseCompilationUnit("namespace N { }").ContainsDirective(kind)); // directive in trailing trivia is not a thing - for (var kind = SyntaxKind.TildeToken; kind < SyntaxKind.ScopedKeyword; kind++) + for (var kind = SyntaxKind.TildeToken; kind < SyntaxKind.XmlElement; kind++) { var compilationUnit = SyntaxFactory.ParseCompilationUnit("namespace N { } #if false"); compilationUnit.GetDiagnostics().Verify( // (1,17): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line // namespace N { } #if false - TestBase.Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(1, 17), - // (1,26): error CS1027: #endif directive expected - // namespace N { } #if false - TestBase.Diagnostic(ErrorCode.ERR_EndifDirectiveExpected, "").WithLocation(1, 26)); + TestBase.Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(1, 17)); Assert.False(compilationUnit.ContainsDirective(kind)); } @@ -386,14 +383,14 @@ public void TestContainsDirective() testContainsHelper1("#undef x", SyntaxKind.UndefDirectiveTrivia); testContainsHelper1("#warning", SyntaxKind.WarningDirectiveTrivia); - // !# is special and is only recognized at start of a script file and nowhere else. + // #! is special and is only recognized at start of a script file and nowhere else. testContainsHelper2(new[] { SyntaxKind.ShebangDirectiveTrivia }, SyntaxFactory.ParseCompilationUnit("#!command", options: TestOptions.Script)); testContainsHelper2(new[] { SyntaxKind.BadDirectiveTrivia }, SyntaxFactory.ParseCompilationUnit(" #!command", options: TestOptions.Script)); testContainsHelper2(new[] { SyntaxKind.BadDirectiveTrivia }, SyntaxFactory.ParseCompilationUnit("#!command", options: TestOptions.Regular)); return; - void testContainsHelper1(string directive, params SyntaxKind[] directiveKinds) + static void testContainsHelper1(string directive, params SyntaxKind[] directiveKinds) { Assert.True(directiveKinds.Length > 0); @@ -469,13 +466,13 @@ class D """)); } - void testContainsHelper2(SyntaxKind[] directiveKinds, CompilationUnitSyntax compilationUnit) + static void testContainsHelper2(SyntaxKind[] directiveKinds, CompilationUnitSyntax compilationUnit) { Assert.True(compilationUnit.ContainsDirectives); foreach (var directiveKind in directiveKinds) Assert.True(compilationUnit.ContainsDirective(directiveKind)); - for (var kind = SyntaxKind.TildeToken; kind < SyntaxKind.ScopedType; kind++) + for (var kind = SyntaxKind.TildeToken; kind < SyntaxKind.XmlElement; kind++) { if (!directiveKinds.Contains(kind)) Assert.False(compilationUnit.ContainsDirective(kind)); @@ -483,6 +480,32 @@ void testContainsHelper2(SyntaxKind[] directiveKinds, CompilationUnitSyntax comp } } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void TestContainsDirective_IfIf() + { + var compilationUnit = SyntaxFactory.ParseCompilationUnit(""" + if (#if) + """); + compilationUnit.GetDiagnostics().Verify( + // (1,5): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // if (#if) + TestBase.Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(1, 5), + // (1,9): error CS1733: Expected expression + // if (#if) + TestBase.Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 9), + // (1,9): error CS1026: ) expected + // if (#if) + TestBase.Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(1, 9), + // (1,9): error CS1733: Expected expression + // if (#if) + TestBase.Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 9), + // (1,9): error CS1002: ; expected + // if (#if) + TestBase.Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 9)); + Assert.False(compilationUnit.ContainsDirectives); + Assert.False(compilationUnit.ContainsDirective(SyntaxKind.IfDirectiveTrivia)); + } + [Fact] public void TestGetAllAnnotatedNodesUsingDescendantNodes() { @@ -2880,7 +2903,7 @@ public void TestRemoveBadDirectiveWithoutEOL_KeepEndOfLine_KeepDirectives() var text = cu2.ToFullString(); - Assert.Equal("class A { } \r\n#endregion", text); + Assert.Equal("class A { } ", text); } [Fact] @@ -3245,7 +3268,7 @@ class A { } #endregion"; var expectedText = @" #region A -#endregion"; +"; TestWithWindowsAndUnixEndOfLines(inputText, expectedText, (cu, expected) => { diff --git a/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests_Preprocessor.cs b/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests_Preprocessor.cs index e2ebf43e96a5c..5c944e8a456c0 100644 --- a/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests_Preprocessor.cs +++ b/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests_Preprocessor.cs @@ -1282,6 +1282,33 @@ await TestAsync(code, Number("102")); } + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public async Task PP_AfterNonWhiteSpaceOnLine(TestHost testHost) + { + var code = """ + if (#if false + true + #else + false + #endif + ) { } + """; + + await TestAsync(code, + testHost, + ControlKeyword("if"), + Punctuation.OpenParen, + Keyword("true"), + PPKeyword("#"), + PPKeyword("else"), + Keyword("false"), + PPKeyword("#"), + PPKeyword("endif"), + Punctuation.CloseParen, + Punctuation.OpenCurly, + Punctuation.CloseCurly); + } + [Theory, CombinatorialData] public async Task DiscardInOutDeclaration(TestHost testHost) { diff --git a/src/EditorFeatures/CSharpTest/QuickInfo/DiagnosticAnalyzerQuickInfoSourceTests.cs b/src/EditorFeatures/CSharpTest/QuickInfo/DiagnosticAnalyzerQuickInfoSourceTests.cs index e63654db9546d..983b7fa7327c6 100644 --- a/src/EditorFeatures/CSharpTest/QuickInfo/DiagnosticAnalyzerQuickInfoSourceTests.cs +++ b/src/EditorFeatures/CSharpTest/QuickInfo/DiagnosticAnalyzerQuickInfoSourceTests.cs @@ -239,15 +239,19 @@ private static string GetFormattedIDEAnalyzerTitle(int ideDiagnosticId, string n protected static Task TestInClassAsync(string code, string expectedDescription, params TextSpan[] relatedSpans) => TestAsync( - """ + $$""" class C { - """ + code + "}", expectedDescription, relatedSpans.ToImmutableArray()); + {{code}} + } + """, expectedDescription, relatedSpans.ToImmutableArray()); protected static Task TestInMethodAsync(string code, string expectedDescription, params TextSpan[] relatedSpans) => TestInClassAsync( - """ + $$""" void M() { - """ + code + "}", expectedDescription, relatedSpans); + {{code}} + } + """, expectedDescription, relatedSpans); } diff --git a/src/EditorFeatures/CSharpTest/Structure/RegionDirectiveStructureTests.cs b/src/EditorFeatures/CSharpTest/Structure/RegionDirectiveStructureTests.cs index 1453fbfc75817..80d95025a9fa7 100644 --- a/src/EditorFeatures/CSharpTest/Structure/RegionDirectiveStructureTests.cs +++ b/src/EditorFeatures/CSharpTest/Structure/RegionDirectiveStructureTests.cs @@ -115,4 +115,18 @@ static void Main(string[] args) await VerifyBlockSpansAsync(code, Region("span", "Region", autoCollapse: false, isDefaultCollapsed: true)); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public async Task Trailing() + { + var code = """ + {|span:#region R$$1 + /* comment */ #endregion + /* comment */ #region R2 + #endregion|} + """; + + await VerifyBlockSpansAsync(code, + Region("span", "R1", autoCollapse: false, isDefaultCollapsed: true)); + } } diff --git a/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs b/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs index 64164a2955b14..a6668e8b9a77c 100644 --- a/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs +++ b/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs @@ -5764,8 +5764,7 @@ public async Task PreprocessorOnSameLine() var expected = @"class C { -} -#line default +}#line default #line hidden"; From 66eaea60356d07d6de987a0fe499e8e0f4b8542b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 07:58:13 -0800 Subject: [PATCH 346/508] lint --- .../Test/InheritanceMargin/InheritanceMarginTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs index b066d4445034e..8ebab9e8a3091 100644 --- a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs +++ b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs @@ -1031,7 +1031,6 @@ public class {|target5:Bar2|} : Bar1, IBar itemForFooInBar2); } - [Theory] [InlineData("abstract", TestHost.InProcess)] [InlineData("abstract", TestHost.OutOfProcess)] From 9e2fda139cb7f7efea2677d6627b957e9c04a4aa Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 08:07:56 -0800 Subject: [PATCH 347/508] Only do loose check if we find nothing --- .../Core/Portable/GoToBase/AbstractGoToBaseService.cs | 6 ++---- .../FindSymbols/FindReferences/BaseTypeFinder.cs | 9 ++++++--- .../Compiler/Core/Extensions/ISymbolExtensions.cs | 3 ++- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Features/Core/Portable/GoToBase/AbstractGoToBaseService.cs b/src/Features/Core/Portable/GoToBase/AbstractGoToBaseService.cs index 028f320ab63c0..734402b53aad1 100644 --- a/src/Features/Core/Portable/GoToBase/AbstractGoToBaseService.cs +++ b/src/Features/Core/Portable/GoToBase/AbstractGoToBaseService.cs @@ -30,18 +30,16 @@ internal abstract class AbstractGoToBaseService : IGoToBaseService public async Task FindBasesAsync(IFindUsagesContext context, Document document, int position, OptionsProvider classificationOptions, CancellationToken cancellationToken) { - var symbolAndProjectOpt = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync( + var symbolAndProject = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync( document, position, cancellationToken).ConfigureAwait(false); - if (symbolAndProjectOpt == null) + if (symbolAndProject is not var (symbol, project)) { await context.ReportNoResultsAsync( FeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret, cancellationToken).ConfigureAwait(false); return; } - var (symbol, project) = symbolAndProjectOpt.Value; - var solution = project.Solution; var bases = FindBaseHelpers.FindBases(symbol, solution, cancellationToken); if (bases.Length == 0 && symbol is IMethodSymbol { MethodKind: MethodKind.Constructor } constructor) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/BaseTypeFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/BaseTypeFinder.cs index 210e509272513..8af2404b48e03 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/BaseTypeFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/BaseTypeFinder.cs @@ -19,13 +19,16 @@ public static ImmutableArray FindOverriddenAndImplementedMembers( { using var _ = ArrayBuilder.GetInstance(out var results); + // This is called for all: class, struct or interface member. + results.AddRange(symbol.ExplicitOrImplicitInterfaceImplementations()); + AddOverrides(allowLooseMatch: false); + + // If we've found nothing at all (either interface impls or exact override matches), then attempt a loose match + // to see if we can find something in an error condition. if (results.Count == 0) AddOverrides(allowLooseMatch: true); - // This is called for all: class, struct or interface member. - results.AddRange(symbol.ExplicitOrImplicitInterfaceImplementations()); - // Remove duplicates from interface implementations before adding their projects. results.RemoveDuplicates(); return results.ToImmutableAndClear(); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs index d4e7dbb5e49a9..86b2707c49309 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs @@ -88,7 +88,8 @@ public static SymbolVisibility GetResultantVisibility(this ISymbol symbol) if (exactMatch != null) return exactMatch; - if (allowLooseMatch) + if (allowLooseMatch && + (symbol.IsVirtual || symbol.IsAbstract || symbol.IsOverride)) { foreach (var baseType in symbol.ContainingType.GetBaseTypes()) { From 329f915ef85ba9b3982b73d0b493b14f02795e26 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 08:10:15 -0800 Subject: [PATCH 348/508] Simplify --- .../Compiler/Core/Extensions/ISymbolExtensions.cs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs index 86b2707c49309..6ab3df0c46d46 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs @@ -110,10 +110,7 @@ bool TryFindLooseMatch(ISymbol symbol, INamedTypeSymbol baseType, [NotNullWhen(t if (member.Kind != symbol.Kind) continue; - if (member.IsSealed) - continue; - - if (!member.IsVirtual && !member.IsOverride && !member.IsAbstract) + if (!member.IsOverridable()) continue; if (symbol.Kind is SymbolKind.Event or SymbolKind.Property) @@ -164,11 +161,9 @@ public static ImmutableArray ImplicitInterfaceImplementations(this ISym public static bool IsOverridable([NotNullWhen(true)] this ISymbol? symbol) { - // Members can only have overrides if they are virtual, abstract or override and is not - // sealed. - return symbol?.ContainingType?.TypeKind == TypeKind.Class && - (symbol.IsVirtual || symbol.IsAbstract || symbol.IsOverride) && - !symbol.IsSealed; + // Members can only have overrides if they are virtual, abstract or override and is not sealed. + return symbol is { ContainingType.TypeKind: TypeKind.Class, IsSealed: false } && + (symbol.IsVirtual || symbol.IsAbstract || symbol.IsOverride); } public static bool IsImplementableMember([NotNullWhen(true)] this ISymbol? symbol) From 826b0db12582b6b645ed1cf5e1565f767af31e53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Thu, 14 Nov 2024 08:20:51 -0800 Subject: [PATCH 349/508] Update Gladstone (#75888) --- eng/Directory.Packages.props | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/eng/Directory.Packages.props b/eng/Directory.Packages.props index e63fad5dc2001..962f5a6caca28 100644 --- a/eng/Directory.Packages.props +++ b/eng/Directory.Packages.props @@ -22,7 +22,6 @@ 8.0.10 <_xunitVersion>2.6.6 2.1.0 - 17.10.2079 From 30cd2b30bb844b10575c9371cef0283f6478bf6a Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Thu, 14 Nov 2024 09:40:02 -0800 Subject: [PATCH 350/508] Handle field reference from semantic model in non-field-backed property (#75904) --- .../Portable/Binder/Binder_Expressions.cs | 5 +- .../CSharp/Test/Emit3/FieldKeywordTests.cs | 68 +++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 9e19db67c51ab..248134bfb8d59 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -1465,9 +1465,12 @@ private BoundExpression BindFieldExpression(FieldExpressionSyntax node, BindingD } } + // Field will be null when binding a field expression in a speculative + // semantic model when the property does not have a backing field. if (field is null) { - throw ExceptionUtilities.UnexpectedValue(ContainingMember()); + diagnostics.Add(ErrorCode.ERR_NoSuchMember, node, ContainingMember(), "field"); + return BadExpression(node); } var implicitReceiver = field.IsStatic ? null : ThisReference(node, field.ContainingType, wasCompilerGenerated: true); diff --git a/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs b/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs index fb7367886e267..d95d92200b540 100644 --- a/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs @@ -10464,6 +10464,74 @@ .locals init (System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V VerifyMergedProperties(actualProperties, actualFields); } + [WorkItem("https://github.com/dotnet/roslyn/issues/75893")] + [Theory] + [CombinatorialData] + public void SpeculativeSemanticModel_01(bool includeLocal) + { + string source = $$""" + class C + { + object P + { + get + { + {{(includeLocal ? "object field = null;" : "")}} + return null; + } + } + } + """; + + var parseOptions = TestOptions.RegularPreview; + var comp = CreateCompilation(source, parseOptions: parseOptions); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var previousAccessor = tree.GetRoot().DescendantNodes().OfType().Single(); + + var modifiedTree = SyntaxFactory.ParseSyntaxTree(source.Replace("return null;", "return field;"), parseOptions); + var modifiedAccessor = modifiedTree.GetRoot().DescendantNodes().OfType().Single(); + Assert.True(model.TryGetSpeculativeSemanticModelForMethodBody(previousAccessor.Body.SpanStart, modifiedAccessor, out var speculativeModel)); + var expr = modifiedAccessor.DescendantNodes().OfType().Single(); + Assert.Equal("return field;", expr.Parent.ToString()); + var symbolInfo = speculativeModel.GetSymbolInfo(expr); + Assert.Null(symbolInfo.Symbol); + } + + [Theory] + [CombinatorialData] + public void SpeculativeSemanticModel_02(bool includeLocal) + { + string source = $$""" + class C + { + object P + { + get + { + {{(includeLocal ? "object field = null;" : "")}} + return null; + } + set; + } + } + """; + + var parseOptions = TestOptions.RegularPreview; + var comp = CreateCompilation(source, parseOptions: parseOptions); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var previousAccessor = tree.GetRoot().DescendantNodes().OfType().First(); + + var modifiedTree = SyntaxFactory.ParseSyntaxTree(source.Replace("return null;", "return field;"), parseOptions); + var modifiedAccessor = modifiedTree.GetRoot().DescendantNodes().OfType().First(); + Assert.True(model.TryGetSpeculativeSemanticModelForMethodBody(previousAccessor.Body.SpanStart, modifiedAccessor, out var speculativeModel)); + var expr = modifiedAccessor.DescendantNodes().OfType().Single(); + Assert.Equal("return field;", expr.Parent.ToString()); + var symbolInfo = speculativeModel.GetSymbolInfo(expr); + Assert.Equal("System.Object C.

k__BackingField", symbolInfo.Symbol.ToTestDisplayString()); + } + [Theory] [InlineData("{ get; }")] [InlineData("{ get; set; }")] From 455440930879ef41499cee433eb5e074d8f8d80f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 10:57:11 -0800 Subject: [PATCH 351/508] Update 'use simple using statement' to support global statements --- ...eSimpleUsingStatementDiagnosticAnalyzer.cs | 99 ++++++++----- .../UseSimpleUsingStatementCodeFixProvider.cs | 67 +++++---- .../UseSimpleUsingStatementTests.cs | 133 ++++++++++++++++++ .../Core/Extensions/ListExtensions.cs | 49 ++++++- 4 files changed, 281 insertions(+), 67 deletions(-) diff --git a/src/Analyzers/CSharp/Analyzers/UseSimpleUsingStatement/UseSimpleUsingStatementDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseSimpleUsingStatement/UseSimpleUsingStatementDiagnosticAnalyzer.cs index 6535e0e24d206..23876430f154e 100644 --- a/src/Analyzers/CSharp/Analyzers/UseSimpleUsingStatement/UseSimpleUsingStatementDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseSimpleUsingStatement/UseSimpleUsingStatementDiagnosticAnalyzer.cs @@ -2,16 +2,21 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; +using Microsoft.CodeAnalysis.CSharp.LanguageService; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Utilities; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UseSimpleUsingStatement; @@ -48,17 +53,14 @@ namespace Microsoft.CodeAnalysis.CSharp.UseSimpleUsingStatement; /// semantics will not change. ///

[DiagnosticAnalyzer(LanguageNames.CSharp)] -internal class UseSimpleUsingStatementDiagnosticAnalyzer : AbstractBuiltInCodeStyleDiagnosticAnalyzer +internal sealed class UseSimpleUsingStatementDiagnosticAnalyzer() + : AbstractBuiltInCodeStyleDiagnosticAnalyzer( + IDEDiagnosticIds.UseSimpleUsingStatementDiagnosticId, + EnforceOnBuildValues.UseSimpleUsingStatement, + CSharpCodeStyleOptions.PreferSimpleUsingStatement, + new LocalizableResourceString(nameof(CSharpAnalyzersResources.Use_simple_using_statement), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources)), + new LocalizableResourceString(nameof(CSharpAnalyzersResources.using_statement_can_be_simplified), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources))) { - public UseSimpleUsingStatementDiagnosticAnalyzer() - : base(IDEDiagnosticIds.UseSimpleUsingStatementDiagnosticId, - EnforceOnBuildValues.UseSimpleUsingStatement, - CSharpCodeStyleOptions.PreferSimpleUsingStatement, - new LocalizableResourceString(nameof(CSharpAnalyzersResources.Use_simple_using_statement), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources)), - new LocalizableResourceString(nameof(CSharpAnalyzersResources.using_statement_can_be_simplified), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources))) - { - } - public override DiagnosticAnalyzerCategory GetAnalyzerCategory() => DiagnosticAnalyzerCategory.SemanticSpanAnalysis; @@ -83,12 +85,14 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context) var outermostUsing = (UsingStatementSyntax)context.Node; var semanticModel = context.SemanticModel; - if (outermostUsing.Parent is not BlockSyntax parentBlock) - { - // Don't offer on a using statement that is parented by another using statement. We'll just offer on the - // topmost using statement. + var parentBlockLike = outermostUsing.Parent; + if (parentBlockLike is GlobalStatementSyntax) + parentBlockLike = parentBlockLike.Parent; + + // Don't offer on a using statement that is parented by another using statement. We'll just offer on the + // topmost using statement. + if (parentBlockLike is not BlockSyntax and not CompilationUnitSyntax) return; - } var innermostUsing = outermostUsing; @@ -102,7 +106,7 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context) } // Verify that changing this using-statement into a using-declaration will not change semantics. - if (!PreservesSemantics(semanticModel, parentBlock, outermostUsing, innermostUsing, cancellationToken)) + if (!PreservesSemantics(semanticModel, parentBlockLike, outermostUsing, innermostUsing, cancellationToken)) return; // Converting a using-statement to a using-variable-declaration will cause the using's variables to now be @@ -110,7 +114,7 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context) // block. These may then collide with other variables in the block, causing an error. Check for that and // bail if this happens. if (CausesVariableCollision( - context.SemanticModel, parentBlock, + context.SemanticModel, parentBlockLike, outermostUsing, innermostUsing, cancellationToken)) { return; @@ -122,44 +126,61 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context) outermostUsing.UsingKeyword.GetLocation(), option.Notification, context.Options, - additionalLocations: ImmutableArray.Create(outermostUsing.GetLocation()), + additionalLocations: [outermostUsing.GetLocation()], properties: null)); } private static bool CausesVariableCollision( - SemanticModel semanticModel, BlockSyntax parentBlock, - UsingStatementSyntax outermostUsing, UsingStatementSyntax innermostUsing, + SemanticModel semanticModel, + SyntaxNode parentBlockLike, + UsingStatementSyntax outermostUsing, + UsingStatementSyntax innermostUsing, CancellationToken cancellationToken) { - var symbolNameToExistingSymbol = semanticModel.GetExistingSymbols(parentBlock, cancellationToken).ToLookup(s => s.Name); + using var _ = PooledDictionary>.GetInstance(out var symbolNameToExistingSymbol); - for (var current = outermostUsing; current != null; current = current.Statement as UsingStatementSyntax) + try { - // Check if the using statement itself contains variables that will collide with other variables in the - // block. - var usingOperation = (IUsingOperation)semanticModel.GetRequiredOperation(current, cancellationToken); - if (DeclaredLocalCausesCollision(symbolNameToExistingSymbol, usingOperation.Locals)) - return true; + foreach (var statement in CSharpBlockFacts.Instance.GetExecutableBlockStatements(parentBlockLike)) + { + foreach (var symbol in semanticModel.GetExistingSymbols(statement, cancellationToken)) + symbolNameToExistingSymbol.MultiAdd(symbol.Name, symbol); + } + + for (var current = outermostUsing; current != null; current = current.Statement as UsingStatementSyntax) + { + // Check if the using statement itself contains variables that will collide with other variables in the + // block. + var usingOperation = (IUsingOperation)semanticModel.GetRequiredOperation(current, cancellationToken); + if (DeclaredLocalCausesCollision(symbolNameToExistingSymbol, usingOperation.Locals)) + return true; + } + + var innerUsingOperation = (IUsingOperation)semanticModel.GetRequiredOperation(innermostUsing, cancellationToken); + if (innerUsingOperation.Body is IBlockOperation innerUsingBlock) + return DeclaredLocalCausesCollision(symbolNameToExistingSymbol, innerUsingBlock.Locals); + + return false; + } + finally + { + symbolNameToExistingSymbol.FreeValues(); } - - var innerUsingOperation = (IUsingOperation)semanticModel.GetRequiredOperation(innermostUsing, cancellationToken); - if (innerUsingOperation.Body is IBlockOperation innerUsingBlock) - return DeclaredLocalCausesCollision(symbolNameToExistingSymbol, innerUsingBlock.Locals); - - return false; } - private static bool DeclaredLocalCausesCollision(ILookup symbolNameToExistingSymbol, ImmutableArray locals) - => locals.Any(static (local, symbolNameToExistingSymbol) => symbolNameToExistingSymbol[local.Name].Any(otherLocal => !local.Equals(otherLocal)), symbolNameToExistingSymbol); + private static bool DeclaredLocalCausesCollision(Dictionary> symbolNameToExistingSymbol, ImmutableArray locals) + => locals.Any(static (local, symbolNameToExistingSymbol) => + symbolNameToExistingSymbol.TryGetValue(local.Name, out var symbols) && + symbols.Any(otherLocal => !local.Equals(otherLocal)), symbolNameToExistingSymbol); private static bool PreservesSemantics( SemanticModel semanticModel, - BlockSyntax parentBlock, + SyntaxNode parentBlockLike, UsingStatementSyntax outermostUsing, UsingStatementSyntax innermostUsing, CancellationToken cancellationToken) { - var statements = parentBlock.Statements; + var statements = (IReadOnlyList)CSharpBlockFacts.Instance.GetExecutableBlockStatements(parentBlockLike); var index = statements.IndexOf(outermostUsing); return UsingValueDoesNotLeakToFollowingStatements(semanticModel, statements, index, cancellationToken) && @@ -167,7 +188,7 @@ private static bool PreservesSemantics( } private static bool UsingStatementDoesNotInvolveJumps( - SyntaxList parentStatements, int index, UsingStatementSyntax innermostUsing) + IReadOnlyList parentStatements, int index, UsingStatementSyntax innermostUsing) { // Jumps are not allowed to cross a using declaration in the forward direction, and can't go back unless // there is a curly brace between the using and the label. @@ -204,7 +225,7 @@ private static bool IsGotoOrLabeledStatement(StatementSyntax priorStatement) private static bool UsingValueDoesNotLeakToFollowingStatements( SemanticModel semanticModel, - SyntaxList statements, + IReadOnlyList statements, int index, CancellationToken cancellationToken) { diff --git a/src/Analyzers/CSharp/CodeFixes/UseSimpleUsingStatement/UseSimpleUsingStatementCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseSimpleUsingStatement/UseSimpleUsingStatementCodeFixProvider.cs index f2feec8761fe0..e50351d4186c8 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseSimpleUsingStatement/UseSimpleUsingStatementCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseSimpleUsingStatement/UseSimpleUsingStatementCodeFixProvider.cs @@ -2,13 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.Linq; +using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; @@ -21,6 +20,7 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Utilities; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UseSimpleUsingStatement; @@ -46,14 +46,15 @@ protected override Task FixAllAsync( Document document, ImmutableArray diagnostics, SyntaxEditor editor, CancellationToken cancellationToken) { - var topmostUsingStatements = diagnostics.Select(d => (UsingStatementSyntax)d.AdditionalLocations[0].FindNode(cancellationToken)).ToSet(); - var blocks = topmostUsingStatements.Select(u => (BlockSyntax)u.Parent); + var topmostUsingStatements = diagnostics.Select( + d => (UsingStatementSyntax)d.AdditionalLocations[0].FindNode(getInnermostNodeForTie: true, cancellationToken)).ToSet(); + var blockLikes = topmostUsingStatements.Select(u => u.Parent is GlobalStatementSyntax ? u.Parent.GetRequiredParent() : u.GetRequiredParent()).ToSet(); // Process blocks in reverse order so we rewrite from inside-to-outside with nested // usings. var root = editor.OriginalRoot; var updatedRoot = root.ReplaceNodes( - blocks.OrderByDescending(b => b.SpanStart), + blockLikes.OrderByDescending(b => b.SpanStart), (original, current) => RewriteBlock(original, current, topmostUsingStatements)); editor.ReplaceNode(root, updatedRoot); @@ -61,26 +62,46 @@ protected override Task FixAllAsync( return Task.CompletedTask; } - private static BlockSyntax RewriteBlock( - BlockSyntax originalBlock, BlockSyntax currentBlock, + private static SyntaxNode RewriteBlock( + SyntaxNode originalBlockLike, + SyntaxNode currentBlockLike, ISet topmostUsingStatements) { - if (originalBlock.Statements.Count == currentBlock.Statements.Count) + var originalBlockStatements = (IReadOnlyList)CSharpBlockFacts.Instance.GetExecutableBlockStatements(originalBlockLike); + var currentBlockStatements = (IReadOnlyList)CSharpBlockFacts.Instance.GetExecutableBlockStatements(currentBlockLike); + + if (originalBlockStatements.Count == currentBlockStatements.Count) { - var statementToUpdateIndex = originalBlock.Statements.IndexOf(s => topmostUsingStatements.Contains(s)); - var statementToUpdate = currentBlock.Statements[statementToUpdateIndex]; + var statementToUpdateIndex = originalBlockStatements.IndexOf(s => topmostUsingStatements.Contains(s)); + var statementToUpdate = currentBlockStatements[statementToUpdateIndex]; if (statementToUpdate is UsingStatementSyntax usingStatement && usingStatement.Declaration != null) { - var updatedStatements = currentBlock.Statements.ReplaceRange( - statementToUpdate, - Expand(usingStatement)); - return currentBlock.WithStatements(updatedStatements); + var expandedUsing = Expand(usingStatement); + + return WithStatements(currentBlockLike, usingStatement, expandedUsing); } } - return currentBlock; + return currentBlockLike; + } + + private static SyntaxNode WithStatements( + SyntaxNode currentBlockLike, + UsingStatementSyntax usingStatement, + ImmutableArray expandedUsingStatements) + { + return currentBlockLike switch + { + BlockSyntax currentBlock => currentBlock.WithStatements( + currentBlock.Statements.ReplaceRange(usingStatement, expandedUsingStatements)), + + CompilationUnitSyntax compilationUnit => compilationUnit.WithMembers( + compilationUnit.Members.ReplaceRange((GlobalStatementSyntax)usingStatement.GetRequiredParent(), expandedUsingStatements.Select(GlobalStatement))), + + _ => throw ExceptionUtilities.UnexpectedValue(currentBlockLike), + }; } private static ImmutableArray Expand(UsingStatementSyntax usingStatement) @@ -163,15 +184,13 @@ private static SyntaxTriviaList Expand(ArrayBuilder result, Usi } return default; - } - private static LocalDeclarationStatementSyntax Convert(UsingStatementSyntax usingStatement) - { - return LocalDeclarationStatement( - usingStatement.AwaitKeyword, - usingStatement.UsingKeyword.WithAppendedTrailingTrivia(ElasticMarker), - modifiers: default, - usingStatement.Declaration, - SemicolonToken).WithTrailingTrivia(usingStatement.CloseParenToken.TrailingTrivia); + static LocalDeclarationStatementSyntax Convert(UsingStatementSyntax usingStatement) + => LocalDeclarationStatement( + usingStatement.AwaitKeyword, + usingStatement.UsingKeyword.WithAppendedTrailingTrivia(ElasticMarker), + modifiers: default, + usingStatement.Declaration!, + SemicolonToken).WithTrailingTrivia(usingStatement.CloseParenToken.TrailingTrivia); } } diff --git a/src/Analyzers/CSharp/Tests/UseSimpleUsingStatement/UseSimpleUsingStatementTests.cs b/src/Analyzers/CSharp/Tests/UseSimpleUsingStatement/UseSimpleUsingStatementTests.cs index 283de4f8afb13..66f5bb741bf26 100644 --- a/src/Analyzers/CSharp/Tests/UseSimpleUsingStatement/UseSimpleUsingStatementTests.cs +++ b/src/Analyzers/CSharp/Tests/UseSimpleUsingStatement/UseSimpleUsingStatementTests.cs @@ -1920,4 +1920,137 @@ public static byte[] ComputeMD5Hash(byte[] source) } """); } + + [Fact] + public async Task TestGlobalStatement1() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + [|using|] (var c = (IDisposable)null) + { + } + + class C + { + } + """, + FixedCode = """ + using System; + + using var c = (IDisposable)null; + + class C + { + } + """, + LanguageVersion = LanguageVersion.CSharp9, + TestState = + { + OutputKind = OutputKind.ConsoleApplication, + } + }.RunAsync(); + } + + [Fact] + public async Task TestGlobalStatement2() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + [|using|] (var c = (IDisposable)null) + { + Console.WriteLine(); + } + + class C + { + } + """, + FixedCode = """ + using System; + + using var c = (IDisposable)null; + Console.WriteLine(); + + class C + { + } + """, + LanguageVersion = LanguageVersion.CSharp9, + TestState = + { + OutputKind = OutputKind.ConsoleApplication, + } + }.RunAsync(); + } + + [Fact] + public async Task TestGlobalStatement3() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + using (var c = (IDisposable)null) + { + } + + Console.WriteLine(); + + class C + { + } + """, + LanguageVersion = LanguageVersion.CSharp9, + TestState = + { + OutputKind = OutputKind.ConsoleApplication, + } + }.RunAsync(); + } + + [Fact] + public async Task TestGlobalStatement4() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + [|using|] (var c = (IDisposable)null) + { + Console.WriteLine(); + } + + int LocalFunction() => 0; + + class C + { + } + """, + FixedCode = """ + using System; + + using var c = (IDisposable)null; + Console.WriteLine(); + + int LocalFunction() => 0; + + class C + { + } + """, + LanguageVersion = LanguageVersion.CSharp9, + TestState = + { + OutputKind = OutputKind.ConsoleApplication, + } + }.RunAsync(); + } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs index 4a05b3ffbfe13..549ca2efceb6d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; @@ -102,14 +103,56 @@ public static bool TryRemoveFirst(this IList list, Func(this ImmutableArray list, Func predicate) + { + for (var i = 0; i < list.Length; i++) + { + if (predicate(list[i])) + return i; + } + + return -1; + } + + public static int IndexOf(this IReadOnlyList list, Func predicate) + { + for (var i = 0; i < list.Count; i++) + { + if (predicate(list[i])) + return i; + } + + return -1; + } + public static int IndexOf(this IList list, Func predicate) { for (var i = 0; i < list.Count; i++) { if (predicate(list[i])) - { return i; - } + } + + return -1; + } + + public static int IndexOf(this ImmutableArray list, Func predicate, TArg arg) + { + for (var i = 0; i < list.Length; i++) + { + if (predicate(list[i], arg)) + return i; + } + + return -1; + } + + public static int IndexOf(this IReadOnlyList list, Func predicate, TArg arg) + { + for (var i = 0; i < list.Count; i++) + { + if (predicate(list[i], arg)) + return i; } return -1; @@ -120,9 +163,7 @@ public static int IndexOf(this IList list, Func predi for (var i = 0; i < list.Count; i++) { if (predicate(list[i], arg)) - { return i; - } } return -1; From 4ff9a47fda9502af70a000077df80a09d684cd35 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 10:57:37 -0800 Subject: [PATCH 352/508] Work items --- .../UseSimpleUsingStatementTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Analyzers/CSharp/Tests/UseSimpleUsingStatement/UseSimpleUsingStatementTests.cs b/src/Analyzers/CSharp/Tests/UseSimpleUsingStatement/UseSimpleUsingStatementTests.cs index 66f5bb741bf26..4a978ddd352da 100644 --- a/src/Analyzers/CSharp/Tests/UseSimpleUsingStatement/UseSimpleUsingStatementTests.cs +++ b/src/Analyzers/CSharp/Tests/UseSimpleUsingStatement/UseSimpleUsingStatementTests.cs @@ -1921,7 +1921,7 @@ public static byte[] ComputeMD5Hash(byte[] source) """); } - [Fact] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75917")] public async Task TestGlobalStatement1() { await new VerifyCS.Test @@ -1954,7 +1954,7 @@ class C }.RunAsync(); } - [Fact] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75917")] public async Task TestGlobalStatement2() { await new VerifyCS.Test @@ -1989,7 +1989,7 @@ class C }.RunAsync(); } - [Fact] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75917")] public async Task TestGlobalStatement3() { await new VerifyCS.Test @@ -2015,7 +2015,7 @@ class C }.RunAsync(); } - [Fact] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75917")] public async Task TestGlobalStatement4() { await new VerifyCS.Test From 5ed5805a6d1ea31ce9c7545f4bffb8aa6aab81e4 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 10:58:27 -0800 Subject: [PATCH 353/508] lint --- .../UseSimpleUsingStatementDiagnosticAnalyzer.cs | 1 - .../UseSimpleUsingStatementTests.cs | 7 +------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/Analyzers/CSharp/Analyzers/UseSimpleUsingStatement/UseSimpleUsingStatementDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseSimpleUsingStatement/UseSimpleUsingStatementDiagnosticAnalyzer.cs index 23876430f154e..cf6e90058982d 100644 --- a/src/Analyzers/CSharp/Analyzers/UseSimpleUsingStatement/UseSimpleUsingStatementDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseSimpleUsingStatement/UseSimpleUsingStatementDiagnosticAnalyzer.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; diff --git a/src/Analyzers/CSharp/Tests/UseSimpleUsingStatement/UseSimpleUsingStatementTests.cs b/src/Analyzers/CSharp/Tests/UseSimpleUsingStatement/UseSimpleUsingStatementTests.cs index 4a978ddd352da..0e965310c2553 100644 --- a/src/Analyzers/CSharp/Tests/UseSimpleUsingStatement/UseSimpleUsingStatementTests.cs +++ b/src/Analyzers/CSharp/Tests/UseSimpleUsingStatement/UseSimpleUsingStatementTests.cs @@ -2,20 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.IO; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.UseSimpleUsingStatement; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; -using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseSimpleUsingStatement; @@ -24,7 +19,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseSimpleUsingStatement UseSimpleUsingStatementCodeFixProvider>; [Trait(Traits.Feature, Traits.Features.CodeActionsUseSimpleUsingStatement)] -public class UseSimpleUsingStatementTests +public sealed class UseSimpleUsingStatementTests { [Fact] public async Task TestAboveCSharp8() From 789a6550cd0de70781c5fb3982f4e45407342333 Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Thu, 14 Nov 2024 11:06:07 -0800 Subject: [PATCH 354/508] Reduce some allocations from remote classification serialization (#75911) SerializableClassifiedSpans.Dehydrate was previously taking in an ImmutableArray, causing several callers to allocate. Instead, have it take in a SegmentedList, which several of the callers already have, and if not, a pooled instance can be obtained and populated. ClassifiedSpan[] shows up as 5.4% of allocations in our CodeAnalysis process in the platform's scrolling speedometer test. --- .../FindUsages/IRemoteFindUsagesService.cs | 9 ++++++++- .../IRemoteSemanticClassificationService.cs | 4 ++-- ...RemoteSemanticClassificationService.Caching.cs | 15 ++++++++++++--- .../RemoteSemanticClassificationService.cs | 2 +- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/Features/Core/Portable/FindUsages/IRemoteFindUsagesService.cs b/src/Features/Core/Portable/FindUsages/IRemoteFindUsagesService.cs index 6a01b6efe873a..9e9b542e57027 100644 --- a/src/Features/Core/Portable/FindUsages/IRemoteFindUsagesService.cs +++ b/src/Features/Core/Portable/FindUsages/IRemoteFindUsagesService.cs @@ -259,7 +259,14 @@ internal readonly struct SerializableClassifiedSpansAndHighlightSpan( public readonly TextSpan HighlightSpan = highlightSpan; public static SerializableClassifiedSpansAndHighlightSpan Dehydrate(ClassifiedSpansAndHighlightSpan classifiedSpansAndHighlightSpan) - => new(SerializableClassifiedSpans.Dehydrate(classifiedSpansAndHighlightSpan.ClassifiedSpans), classifiedSpansAndHighlightSpan.HighlightSpan); + { + using var _ = Classifier.GetPooledList(out var temp); + + foreach (var span in classifiedSpansAndHighlightSpan.ClassifiedSpans) + temp.Add(span); + + return new(SerializableClassifiedSpans.Dehydrate(temp), classifiedSpansAndHighlightSpan.HighlightSpan); + } public ClassifiedSpansAndHighlightSpan Rehydrate() => new(this.ClassifiedSpans.Rehydrate(), this.HighlightSpan); diff --git a/src/Workspaces/Core/Portable/Classification/IRemoteSemanticClassificationService.cs b/src/Workspaces/Core/Portable/Classification/IRemoteSemanticClassificationService.cs index 878923fa5ba8a..70db1c75d1c19 100644 --- a/src/Workspaces/Core/Portable/Classification/IRemoteSemanticClassificationService.cs +++ b/src/Workspaces/Core/Portable/Classification/IRemoteSemanticClassificationService.cs @@ -69,11 +69,11 @@ internal sealed class SerializableClassifiedSpans(ImmutableArray classif [DataMember(Order = 1)] public readonly ImmutableArray ClassificationTriples = classificationTriples; - internal static SerializableClassifiedSpans Dehydrate(ImmutableArray classifiedSpans) + internal static SerializableClassifiedSpans Dehydrate(SegmentedList classifiedSpans) { using var _1 = PooledDictionary.GetInstance(out var classificationTypeToId); using var _2 = ArrayBuilder.GetInstance(out var classificationTypes); - var classificationTriples = new FixedSizeArrayBuilder(classifiedSpans.Length * 3); + var classificationTriples = new FixedSizeArrayBuilder(classifiedSpans.Count * 3); foreach (var classifiedSpan in classifiedSpans) { diff --git a/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.Caching.cs b/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.Caching.cs index 07e30f33df3e7..4374242bb0683 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.Caching.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.Caching.cs @@ -86,9 +86,18 @@ private static string GetPersistenceName(ClassificationType type) var classifiedSpans = await TryGetOrReadCachedSemanticClassificationsAsync( documentKey, type, checksum, cancellationToken).ConfigureAwait(false); var textSpanIntervalTree = new TextSpanMutableIntervalTree(textSpans); - return classifiedSpans.IsDefault - ? null - : SerializableClassifiedSpans.Dehydrate(classifiedSpans.WhereAsArray(c => textSpanIntervalTree.HasIntervalThatIntersectsWith(c.TextSpan))); + + if (classifiedSpans.IsDefault) + return null; + + using var _ = Classifier.GetPooledList(out var temp); + foreach (var span in classifiedSpans) + { + if (textSpanIntervalTree.HasIntervalThatIntersectsWith(span.TextSpan)) + temp.Add(span); + } + + return SerializableClassifiedSpans.Dehydrate(temp); } private static async ValueTask CacheClassificationsAsync( diff --git a/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs b/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs index 26c46f8ed5060..6369abb39128b 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs @@ -57,7 +57,7 @@ await classificationService.AddClassificationsAsync( _workQueue.AddWork((document, type, options)); } - return SerializableClassifiedSpans.Dehydrate([.. temp]); + return SerializableClassifiedSpans.Dehydrate(temp); }, cancellationToken); } } From 93378240f4a304330e8756c725c83fc9d4755957 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 11:07:59 -0800 Subject: [PATCH 355/508] Add tests --- .../UseSimpleUsingStatementTests.cs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/Analyzers/CSharp/Tests/UseSimpleUsingStatement/UseSimpleUsingStatementTests.cs b/src/Analyzers/CSharp/Tests/UseSimpleUsingStatement/UseSimpleUsingStatementTests.cs index 0e965310c2553..bcae949e0e205 100644 --- a/src/Analyzers/CSharp/Tests/UseSimpleUsingStatement/UseSimpleUsingStatementTests.cs +++ b/src/Analyzers/CSharp/Tests/UseSimpleUsingStatement/UseSimpleUsingStatementTests.cs @@ -2048,4 +2048,76 @@ class C } }.RunAsync(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75917")] + public async Task TestGlobalStatement5() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + [|using|] (var c = (IDisposable)null) + using (var d = (IDisposable)null) + { + } + + class C + { + } + """, + FixedCode = """ + using System; + + using var c = (IDisposable)null; + using var d = (IDisposable)null; + + class C + { + } + """, + LanguageVersion = LanguageVersion.CSharp9, + TestState = + { + OutputKind = OutputKind.ConsoleApplication, + } + }.RunAsync(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75917")] + public async Task TestGlobalStatement6() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + [|using|] (var c = (IDisposable)null) + { + [|using|] (var d = (IDisposable)null) + { + } + } + + class C + { + } + """, + FixedCode = """ + using System; + + using var c = (IDisposable)null; + using var d = (IDisposable)null; + + class C + { + } + """, + LanguageVersion = LanguageVersion.CSharp9, + TestState = + { + OutputKind = OutputKind.ConsoleApplication, + } + }.RunAsync(); + } } From f0933dc73d0fbf25f8a592054d0134a88181cf80 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 11:12:01 -0800 Subject: [PATCH 356/508] Remove asserts --- .../Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs index c2988d64b365a..40607160ecfcb 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs @@ -212,10 +212,6 @@ private bool IsEmbeddedLanguageStringLiteralToken_Direct( identifier = null; var syntaxFacts = Info.SyntaxFacts; - // Checked by caller - Debug.Assert(syntaxFacts.IsLiteralExpression(token.Parent)); - Debug.Assert(!HasLanguageComment(token, syntaxFacts, out identifier, out _)); - // If we're a string used in a collection initializer, treat this as a lang string if the collection itself // is properly annotated. This is for APIs that do things like DateTime.ParseExact(..., string[] formats, ...); var container = TryFindContainer(token); From 99cc6e3c6a6e8d728bf618aba26f1202a22ba1c6 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 11:45:01 -0800 Subject: [PATCH 357/508] Fix --- .../UseSimpleUsingStatementCodeFixProvider.cs | 13 +++++- .../Core/Extensions/ListExtensions.cs | 44 ------------------- 2 files changed, 12 insertions(+), 45 deletions(-) diff --git a/src/Analyzers/CSharp/CodeFixes/UseSimpleUsingStatement/UseSimpleUsingStatementCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseSimpleUsingStatement/UseSimpleUsingStatementCodeFixProvider.cs index e50351d4186c8..2baa187fe874e 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseSimpleUsingStatement/UseSimpleUsingStatementCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseSimpleUsingStatement/UseSimpleUsingStatementCodeFixProvider.cs @@ -72,7 +72,7 @@ private static SyntaxNode RewriteBlock( if (originalBlockStatements.Count == currentBlockStatements.Count) { - var statementToUpdateIndex = originalBlockStatements.IndexOf(s => topmostUsingStatements.Contains(s)); + var statementToUpdateIndex = IndexOf(originalBlockStatements, s => topmostUsingStatements.Contains(s)); var statementToUpdate = currentBlockStatements[statementToUpdateIndex]; if (statementToUpdate is UsingStatementSyntax usingStatement && @@ -87,6 +87,17 @@ private static SyntaxNode RewriteBlock( return currentBlockLike; } + public static int IndexOf(IReadOnlyList list, Func predicate) + { + for (var i = 0; i < list.Count; i++) + { + if (predicate(list[i])) + return i; + } + + return -1; + } + private static SyntaxNode WithStatements( SyntaxNode currentBlockLike, UsingStatementSyntax usingStatement, diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs index 549ca2efceb6d..48ba16001283e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs @@ -103,28 +103,6 @@ public static bool TryRemoveFirst(this IList list, Func(this ImmutableArray list, Func predicate) - { - for (var i = 0; i < list.Length; i++) - { - if (predicate(list[i])) - return i; - } - - return -1; - } - - public static int IndexOf(this IReadOnlyList list, Func predicate) - { - for (var i = 0; i < list.Count; i++) - { - if (predicate(list[i])) - return i; - } - - return -1; - } - public static int IndexOf(this IList list, Func predicate) { for (var i = 0; i < list.Count; i++) @@ -136,28 +114,6 @@ public static int IndexOf(this IList list, Func predicate) return -1; } - public static int IndexOf(this ImmutableArray list, Func predicate, TArg arg) - { - for (var i = 0; i < list.Length; i++) - { - if (predicate(list[i], arg)) - return i; - } - - return -1; - } - - public static int IndexOf(this IReadOnlyList list, Func predicate, TArg arg) - { - for (var i = 0; i < list.Count; i++) - { - if (predicate(list[i], arg)) - return i; - } - - return -1; - } - public static int IndexOf(this IList list, Func predicate, TArg arg) { for (var i = 0; i < list.Count; i++) From ee2d1857b02d8839405d8f4fdeae83f6213ca9d0 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Thu, 14 Nov 2024 11:52:15 -0800 Subject: [PATCH 358/508] review feedback --- .../Protocol/LspServices/RequestTelemetryScope.cs | 5 ++++- .../Protocol/RoslynRequestExecutionQueue.cs | 12 ++---------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/LanguageServer/Protocol/LspServices/RequestTelemetryScope.cs b/src/LanguageServer/Protocol/LspServices/RequestTelemetryScope.cs index 66d1071293ea7..2408cd4025890 100644 --- a/src/LanguageServer/Protocol/LspServices/RequestTelemetryScope.cs +++ b/src/LanguageServer/Protocol/LspServices/RequestTelemetryScope.cs @@ -52,7 +52,10 @@ private static void ReportNonFatalError(Exception exception) { if (exception is StreamJsonRpc.LocalRpcException localRpcException && localRpcException.ErrorCode == LspErrorCodes.ContentModified) { - // Content modified exceptions are expected and should not be reported as NFWs. + // We throw content modified exceptions when asked to resolve code lens / inlay hints associated with a solution version we no longer have. + // This generally happens when the project changes underneath us. The client is eventually told to refresh, + // but they can send us resolve requests for prior versions before they see the refresh. + // There is no need to report these exceptions as NFW since they are expected to occur in normal workflows. return; } diff --git a/src/LanguageServer/Protocol/RoslynRequestExecutionQueue.cs b/src/LanguageServer/Protocol/RoslynRequestExecutionQueue.cs index c0e28138c3197..e8ff58d15579e 100644 --- a/src/LanguageServer/Protocol/RoslynRequestExecutionQueue.cs +++ b/src/LanguageServer/Protocol/RoslynRequestExecutionQueue.cs @@ -27,21 +27,13 @@ public RoslynRequestExecutionQueue(AbstractLanguageServer langua public override async Task WrapStartRequestTaskAsync(Task requestTask, bool rethrowExceptions) { - if (rethrowExceptions) + try { await requestTask.ConfigureAwait(false); } - else + catch (Exception) when (!rethrowExceptions) { // The caller has asked us to not rethrow, so swallow the exception. - try - { - await requestTask.ConfigureAwait(false); - } - catch (Exception) - { - // Swallow the exception so it does not bubble up into the queue. - } } } From ea7c07a125bae4a696434ab9e76d917ab5293c50 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Thu, 14 Nov 2024 11:57:50 -0800 Subject: [PATCH 359/508] adjust comment --- src/LanguageServer/Protocol/RoslynRequestExecutionQueue.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/LanguageServer/Protocol/RoslynRequestExecutionQueue.cs b/src/LanguageServer/Protocol/RoslynRequestExecutionQueue.cs index e8ff58d15579e..96d786c34f98c 100644 --- a/src/LanguageServer/Protocol/RoslynRequestExecutionQueue.cs +++ b/src/LanguageServer/Protocol/RoslynRequestExecutionQueue.cs @@ -33,7 +33,8 @@ public override async Task WrapStartRequestTaskAsync(Task requestTask, bool reth } catch (Exception) when (!rethrowExceptions) { - // The caller has asked us to not rethrow, so swallow the exception. + // The caller has asked us to not rethrow, so swallow the exception to avoid bringing down the queue. + // The queue item task itself already handles reporting the exception (if any). } } From 24e78cf119948ed9382f30221c6a1fdd439069ba Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Thu, 14 Nov 2024 12:19:33 -0800 Subject: [PATCH 360/508] Use `IsManagedType` for determining which state machine hoisted locals to clear (#75841) --- .../MethodToStateMachineRewriter.cs | 23 +- .../Emit/CodeGen/CodeGenAsyncIteratorTests.cs | 326 ++++++++++++++++ .../Test/Emit/CodeGen/CodeGenAsyncTests.cs | 216 +++++++++- .../Test/Emit/CodeGen/CodeGenIterators.cs | 369 ++++++++++++++++++ ....CodeAnalysis.CSharp.Emit.UnitTests.csproj | 1 + .../EditAndContinueStateMachineTests.cs | 101 +++-- 6 files changed, 964 insertions(+), 72 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs index c0cd01051e1e0..1fe789a5a9a69 100644 --- a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs @@ -422,31 +422,16 @@ internal static bool TryUnwrapBoundStateMachineScope(ref BoundStatement statemen return false; } - private void AddVariableCleanup(ArrayBuilder cleanup, FieldSymbol field) - { - if (MightContainReferences(field.Type)) - { - cleanup.Add(F.AssignmentExpression(F.Field(F.This(), field), F.NullOrDefault(field.Type))); - } - } - /// - /// Might the given type be, or contain, managed references? This is used to determine which - /// fields allocated to temporaries should be cleared when the underlying variable goes out of scope, so + /// Clear fields allocated to temporaries when the underlying variable goes out of scope, so /// that they do not cause unnecessary object retention. /// - private bool MightContainReferences(TypeSymbol type) + private void AddVariableCleanup(ArrayBuilder cleanup, FieldSymbol field) { - if (type.IsReferenceType || type.TypeKind == TypeKind.TypeParameter) return true; // type parameter or reference type - if (type.TypeKind != TypeKind.Struct) return false; // enums, etc - if (type.SpecialType == SpecialType.System_TypedReference) return true; - if (type.SpecialType.CanOptimizeBehavior()) return false; // int, etc - if (!type.IsFromCompilation(this.CompilationState.ModuleBuilderOpt.Compilation)) return true; // perhaps from ref assembly - foreach (var f in _emptyStructTypeCache.GetStructInstanceFields(type)) + if (field.Type.IsManagedTypeNoUseSiteDiagnostics) { - if (MightContainReferences(f.Type)) return true; + cleanup.Add(F.AssignmentExpression(F.Field(F.This(), field), F.NullOrDefault(field.Type))); } - return false; } private StateMachineFieldSymbol GetOrAllocateReusableHoistedField(TypeSymbol type, out bool reused, LocalSymbol local = null) diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs index 52613797b0a27..890e20d8e09c2 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs @@ -9417,5 +9417,331 @@ public static async IAsyncEnumerable> M(Task t, T t1, T t2) where T : un expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "42False43" : null, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_IntLocal() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); +await foreach (int value in values) { } +System.Console.Write(((int)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); + +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce() + { + int values2 = 42; + await System.Threading.Tasks.Task.CompletedTask; + yield return values2; + } +} +"""; + CompileAndVerify(src, expectedOutput: ExpectedOutput("42"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_StringLocal() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); +await foreach (int value in values) { } +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); + +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce() + { + string values2 = "value "; + await System.Threading.Tasks.Task.CompletedTask; + yield return 1; + System.Console.Write(values2); + } +} +"""; + // Note: hoisted top-level local does not get cleared when exiting normally + CompileAndVerify(src, expectedOutput: ExpectedOutput("value value"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedStringLocal() + { + string src = """ +using System.Reflection; + +var tcs = new System.Threading.Tasks.TaskCompletionSource(); +var values = C.Produce(true, tcs.Task); +var enumerator = values.GetAsyncEnumerator(); +assert(await enumerator.MoveNextAsync()); +assert(enumerator.Current == 1); +assert(await enumerator.MoveNextAsync()); +assert(enumerator.Current == 2); +_ = enumerator.MoveNextAsync(); + +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +void assert(bool b) +{ + if (!b) throw new System.Exception(); +} + +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce(bool b, System.Threading.Tasks.Task task) + { + while (b) + { + string values2 = "value "; + yield return 1; + System.Console.Write(values2); + b = false; + } + yield return 2; + await task; + yield return 3; + } +} +"""; + // Note: hoisted nested local gets cleared when exiting nested scope normally + CompileAndVerify(src, expectedOutput: ExpectedOutput("value True"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedLocalWithStructFromAnotherCompilation() + { + var libSrc = """ +public struct S +{ + public int field; + public override string ToString() => field.ToString(); +} +"""; + var libComp = CreateCompilation(libSrc, targetFramework: TargetFramework.Net80); + string src = """ +using System.Reflection; + +var tcs = new System.Threading.Tasks.TaskCompletionSource(); +var values = C.Produce(true, tcs.Task); +var enumerator = values.GetAsyncEnumerator(); +assert(await enumerator.MoveNextAsync()); +assert(enumerator.Current == 1); +assert(await enumerator.MoveNextAsync()); +assert(enumerator.Current == 2); +_ = enumerator.MoveNextAsync(); + +System.Console.Write(((S)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); + +void assert(bool b) +{ + if (!b) throw new System.Exception(); +} + +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce(bool b, System.Threading.Tasks.Task task) + { + while (b) + { + S values2 = new S { field = 42 }; + yield return 1; + System.Console.Write(values2); + b = false; + } + yield return 2; + await task; + yield return 3; + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: ExpectedOutput("4242"), references: [libComp.EmitToImageReference()], + verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + + verifier.VerifyIL("C.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", """ +{ + // Code size 437 (0x1b5) + .maxstack 3 + .locals init (int V_0, + S V_1, + System.Runtime.CompilerServices.TaskAwaiter V_2, + C.d__0 V_3, + System.Exception V_4) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.d__0.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: ldc.i4.s -6 + IL_000a: sub + IL_000b: switch ( + IL_0144, + IL_00bd, + IL_0072, + IL_002c, + IL_002c, + IL_002c, + IL_010e) + IL_002c: ldarg.0 + IL_002d: ldfld "bool C.d__0.<>w__disposeMode" + IL_0032: brfalse.s IL_0039 + IL_0034: leave IL_0181 + IL_0039: ldarg.0 + IL_003a: ldc.i4.m1 + IL_003b: dup + IL_003c: stloc.0 + IL_003d: stfld "int C.d__0.<>1__state" + IL_0042: br.s IL_009f + IL_0044: ldarg.0 + IL_0045: ldloca.s V_1 + IL_0047: initobj "S" + IL_004d: ldloca.s V_1 + IL_004f: ldc.i4.s 42 + IL_0051: stfld "int S.field" + IL_0056: ldloc.1 + IL_0057: stfld "S C.d__0.5__2" + IL_005c: ldarg.0 + IL_005d: ldc.i4.1 + IL_005e: stfld "int C.d__0.<>2__current" + IL_0063: ldarg.0 + IL_0064: ldc.i4.s -4 + IL_0066: dup + IL_0067: stloc.0 + IL_0068: stfld "int C.d__0.<>1__state" + IL_006d: leave IL_01a8 + IL_0072: ldarg.0 + IL_0073: ldc.i4.m1 + IL_0074: dup + IL_0075: stloc.0 + IL_0076: stfld "int C.d__0.<>1__state" + IL_007b: ldarg.0 + IL_007c: ldfld "bool C.d__0.<>w__disposeMode" + IL_0081: brfalse.s IL_0088 + IL_0083: leave IL_0181 + IL_0088: ldarg.0 + IL_0089: ldfld "S C.d__0.5__2" + IL_008e: box "S" + IL_0093: call "void System.Console.Write(object)" + IL_0098: ldarg.0 + IL_0099: ldc.i4.0 + IL_009a: stfld "bool C.d__0.b" + IL_009f: ldarg.0 + IL_00a0: ldfld "bool C.d__0.b" + IL_00a5: brtrue.s IL_0044 + IL_00a7: ldarg.0 + IL_00a8: ldc.i4.2 + IL_00a9: stfld "int C.d__0.<>2__current" + IL_00ae: ldarg.0 + IL_00af: ldc.i4.s -5 + IL_00b1: dup + IL_00b2: stloc.0 + IL_00b3: stfld "int C.d__0.<>1__state" + IL_00b8: leave IL_01a8 + IL_00bd: ldarg.0 + IL_00be: ldc.i4.m1 + IL_00bf: dup + IL_00c0: stloc.0 + IL_00c1: stfld "int C.d__0.<>1__state" + IL_00c6: ldarg.0 + IL_00c7: ldfld "bool C.d__0.<>w__disposeMode" + IL_00cc: brfalse.s IL_00d3 + IL_00ce: leave IL_0181 + IL_00d3: ldarg.0 + IL_00d4: ldfld "System.Threading.Tasks.Task C.d__0.task" + IL_00d9: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_00de: stloc.2 + IL_00df: ldloca.s V_2 + IL_00e1: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_00e6: brtrue.s IL_012a + IL_00e8: ldarg.0 + IL_00e9: ldc.i4.0 + IL_00ea: dup + IL_00eb: stloc.0 + IL_00ec: stfld "int C.d__0.<>1__state" + IL_00f1: ldarg.0 + IL_00f2: ldloc.2 + IL_00f3: stfld "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_00f8: ldarg.0 + IL_00f9: stloc.3 + IL_00fa: ldarg.0 + IL_00fb: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__0.<>t__builder" + IL_0100: ldloca.s V_2 + IL_0102: ldloca.s V_3 + IL_0104: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__0)" + IL_0109: leave IL_01b4 + IL_010e: ldarg.0 + IL_010f: ldfld "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_0114: stloc.2 + IL_0115: ldarg.0 + IL_0116: ldflda "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_011b: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_0121: ldarg.0 + IL_0122: ldc.i4.m1 + IL_0123: dup + IL_0124: stloc.0 + IL_0125: stfld "int C.d__0.<>1__state" + IL_012a: ldloca.s V_2 + IL_012c: call "void System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_0131: ldarg.0 + IL_0132: ldc.i4.3 + IL_0133: stfld "int C.d__0.<>2__current" + IL_0138: ldarg.0 + IL_0139: ldc.i4.s -6 + IL_013b: dup + IL_013c: stloc.0 + IL_013d: stfld "int C.d__0.<>1__state" + IL_0142: leave.s IL_01a8 + IL_0144: ldarg.0 + IL_0145: ldc.i4.m1 + IL_0146: dup + IL_0147: stloc.0 + IL_0148: stfld "int C.d__0.<>1__state" + IL_014d: ldarg.0 + IL_014e: ldfld "bool C.d__0.<>w__disposeMode" + IL_0153: pop + IL_0154: leave.s IL_0181 + } + catch System.Exception + { + IL_0156: stloc.s V_4 + IL_0158: ldarg.0 + IL_0159: ldc.i4.s -2 + IL_015b: stfld "int C.d__0.<>1__state" + IL_0160: ldarg.0 + IL_0161: ldc.i4.0 + IL_0162: stfld "int C.d__0.<>2__current" + IL_0167: ldarg.0 + IL_0168: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__0.<>t__builder" + IL_016d: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_0172: ldarg.0 + IL_0173: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__0.<>v__promiseOfValueOrEnd" + IL_0178: ldloc.s V_4 + IL_017a: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetException(System.Exception)" + IL_017f: leave.s IL_01b4 + } + IL_0181: ldarg.0 + IL_0182: ldc.i4.s -2 + IL_0184: stfld "int C.d__0.<>1__state" + IL_0189: ldarg.0 + IL_018a: ldc.i4.0 + IL_018b: stfld "int C.d__0.<>2__current" + IL_0190: ldarg.0 + IL_0191: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__0.<>t__builder" + IL_0196: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_019b: ldarg.0 + IL_019c: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__0.<>v__promiseOfValueOrEnd" + IL_01a1: ldc.i4.0 + IL_01a2: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_01a7: ret + IL_01a8: ldarg.0 + IL_01a9: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__0.<>v__promiseOfValueOrEnd" + IL_01ae: ldc.i4.1 + IL_01af: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_01b4: ret +} +"""); + } } } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs index 5906b205ab039..89482c2907c11 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; @@ -19,6 +18,11 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { public class CodeGenAsyncTests : EmitMetadataTestBase { + internal static string ExpectedOutput(string output) + { + return ExecutionConditionUtil.IsMonoOrCoreClr ? output : null; + } + private static CSharpCompilation CreateCompilation(string source, IEnumerable references = null, CSharpCompilationOptions options = null) { options = options ?? TestOptions.ReleaseExe; @@ -6053,5 +6057,215 @@ public async Task M(object o) var comp = CSharpTestBase.CreateCompilation(source); comp.VerifyEmitDiagnostics(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedStringLocal() + { + string src = """ +using System.Reflection; + +var tcs = new System.Threading.Tasks.TaskCompletionSource(); +var task = C.ProduceAsync(true, tcs.Task); + +var callback = (System.Delegate)task.GetType().GetField("m_action", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(task); +object stateMachineBox = callback.Target; +object stateMachine = stateMachineBox.GetType().GetField("StateMachine", BindingFlags.Public | BindingFlags.Instance).GetValue(stateMachineBox); + +System.Console.Write((string)stateMachine.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(stateMachine) is null); + +class C +{ + public static async System.Threading.Tasks.Task ProduceAsync(bool b, System.Threading.Tasks.Task task) + { + while (b) + { + string values2 = "value "; + await System.Threading.Tasks.Task.CompletedTask; + System.Console.Write(values2); + b = false; + } + await task; // block execution here to check what's in the field for "values2" + return 42; + } +} +"""; + // Note: nested hoisted local gets cleared when exiting nested scope normally + CompileAndVerify(src, expectedOutput: ExpectedOutput("value True"), targetFramework: TargetFramework.Net90, verify: Verification.Skipped).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedLocalWithStructFromAnotherCompilation() + { + var libSrc = """ +public struct S +{ + public int field; + public override string ToString() => field.ToString(); +} +"""; + var libComp = CreateCompilation(libSrc, targetFramework: TargetFramework.Net90); + string src = """ +using System.Reflection; + +var tcs = new System.Threading.Tasks.TaskCompletionSource(); +var task = C.ProduceAsync(true, tcs.Task); + +var callback = (System.Delegate)task.GetType().GetField("m_action", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(task); +object stateMachineBox = callback.Target; +object stateMachine = stateMachineBox.GetType().GetField("StateMachine", BindingFlags.Public | BindingFlags.Instance).GetValue(stateMachineBox); + +System.Console.Write((S)stateMachine.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(stateMachine)); + +class C +{ + public static async System.Threading.Tasks.Task ProduceAsync(bool b, System.Threading.Tasks.Task task) + { + while (b) + { + S values2 = new S { field = 42 }; + await System.Threading.Tasks.Task.CompletedTask; + System.Console.Write(values2); + b = false; + } + await task; // block execution here to check what's in the field for "values2" + return 10; + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: ExpectedOutput("4242"), references: [libComp.EmitToImageReference()], + targetFramework: TargetFramework.Net90, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("C.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", """ +{ + // Code size 309 (0x135) + .maxstack 3 + .locals init (int V_0, + int V_1, + S V_2, + System.Runtime.CompilerServices.TaskAwaiter V_3, + System.Exception V_4) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.d__0.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0065 + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: beq IL_00df + IL_0011: br IL_009f + IL_0016: ldarg.0 + IL_0017: ldloca.s V_2 + IL_0019: initobj "S" + IL_001f: ldloca.s V_2 + IL_0021: ldc.i4.s 42 + IL_0023: stfld "int S.field" + IL_0028: ldloc.2 + IL_0029: stfld "S C.d__0.5__2" + IL_002e: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.CompletedTask.get" + IL_0033: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_0038: stloc.3 + IL_0039: ldloca.s V_3 + IL_003b: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_0040: brtrue.s IL_0081 + IL_0042: ldarg.0 + IL_0043: ldc.i4.0 + IL_0044: dup + IL_0045: stloc.0 + IL_0046: stfld "int C.d__0.<>1__state" + IL_004b: ldarg.0 + IL_004c: ldloc.3 + IL_004d: stfld "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_0052: ldarg.0 + IL_0053: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder" + IL_0058: ldloca.s V_3 + IL_005a: ldarg.0 + IL_005b: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__0)" + IL_0060: leave IL_0134 + IL_0065: ldarg.0 + IL_0066: ldfld "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_006b: stloc.3 + IL_006c: ldarg.0 + IL_006d: ldflda "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_0072: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_0078: ldarg.0 + IL_0079: ldc.i4.m1 + IL_007a: dup + IL_007b: stloc.0 + IL_007c: stfld "int C.d__0.<>1__state" + IL_0081: ldloca.s V_3 + IL_0083: call "void System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_0088: ldarg.0 + IL_0089: ldfld "S C.d__0.5__2" + IL_008e: box "S" + IL_0093: call "void System.Console.Write(object)" + IL_0098: ldarg.0 + IL_0099: ldc.i4.0 + IL_009a: stfld "bool C.d__0.b" + IL_009f: ldarg.0 + IL_00a0: ldfld "bool C.d__0.b" + IL_00a5: brtrue IL_0016 + IL_00aa: ldarg.0 + IL_00ab: ldfld "System.Threading.Tasks.Task C.d__0.task" + IL_00b0: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_00b5: stloc.3 + IL_00b6: ldloca.s V_3 + IL_00b8: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_00bd: brtrue.s IL_00fb + IL_00bf: ldarg.0 + IL_00c0: ldc.i4.1 + IL_00c1: dup + IL_00c2: stloc.0 + IL_00c3: stfld "int C.d__0.<>1__state" + IL_00c8: ldarg.0 + IL_00c9: ldloc.3 + IL_00ca: stfld "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_00cf: ldarg.0 + IL_00d0: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder" + IL_00d5: ldloca.s V_3 + IL_00d7: ldarg.0 + IL_00d8: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__0)" + IL_00dd: leave.s IL_0134 + IL_00df: ldarg.0 + IL_00e0: ldfld "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_00e5: stloc.3 + IL_00e6: ldarg.0 + IL_00e7: ldflda "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_00ec: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_00f2: ldarg.0 + IL_00f3: ldc.i4.m1 + IL_00f4: dup + IL_00f5: stloc.0 + IL_00f6: stfld "int C.d__0.<>1__state" + IL_00fb: ldloca.s V_3 + IL_00fd: call "void System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_0102: ldc.i4.s 10 + IL_0104: stloc.1 + IL_0105: leave.s IL_0120 + } + catch System.Exception + { + IL_0107: stloc.s V_4 + IL_0109: ldarg.0 + IL_010a: ldc.i4.s -2 + IL_010c: stfld "int C.d__0.<>1__state" + IL_0111: ldarg.0 + IL_0112: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder" + IL_0117: ldloc.s V_4 + IL_0119: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_011e: leave.s IL_0134 + } + IL_0120: ldarg.0 + IL_0121: ldc.i4.s -2 + IL_0123: stfld "int C.d__0.<>1__state" + IL_0128: ldarg.0 + IL_0129: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder" + IL_012e: ldloc.1 + IL_012f: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult(int)" + IL_0134: ret +} +"""); + } } } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenIterators.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenIterators.cs index 3f29bbdb28868..f4ad723ea8864 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenIterators.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenIterators.cs @@ -3001,5 +3001,374 @@ .locals init (int V_0, IL_0044: ret }"); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_IntLocal() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); +foreach (int value in values) { } +System.Console.Write(((int)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); + +class C +{ + public static System.Collections.Generic.IEnumerable Produce() + { + int values2 = 42; + yield return values2; + System.Console.Write($"{values2} "); + } +} +"""; + CompileAndVerify(src, expectedOutput: "42 42").VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_StringLocal() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); +foreach (int value in values) { } +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); + +class C +{ + public static System.Collections.Generic.IEnumerable Produce() + { + string values2 = "ran"; + yield return 42; + System.Console.Write($"{values2} "); + } +} +"""; + // Note: top-level hoisted local does not get cleared when exiting normally + CompileAndVerify(src, expectedOutput: "ran ran").VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_IntArrayLocalAndForeachLocals() + { + string source = """ +using System.Linq; +using System.Reflection; + +var values = C.Produce(); +foreach (int value in values) { } +System.Console.Write(((int[])values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)).Length); + +class C +{ + public static System.Collections.Generic.IEnumerable Produce() + { + int[] values2 = Enumerable.Range(0, 100).ToArray(); + yield return 42; + System.Console.Write($"{values2.Length} "); + } +} +"""; + // Note: top-level hoisted local does not get cleared when exiting normally + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "100 100").VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedStringLocal() + { + var src = """ +using System.Reflection; + +var enumerable = C.M(true); +var enumerator = enumerable.GetEnumerator(); +try +{ + assert(enumerator.MoveNext()); + System.Console.Write($"{enumerator.Current} "); + assert(!enumerator.MoveNext()); + System.Console.Write(((string)enumerator.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator)) is null); +} +finally +{ + enumerator.Dispose(); +} + +void assert(bool b) +{ + if (!b) throw new System.Exception(); +} + +public class C +{ + public static System.Collections.Generic.IEnumerable M(bool b) + { + while (b) + { + string s = "value "; + yield return 42; + b = false; + System.Console.Write(s); + } + } +} +"""; + // Note: nested hoisted local gets cleared when exiting normally + CompileAndVerify(src, expectedOutput: "42 value True").VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedUnmanagedTypeParameterLocal() + { + var src = """ +using System.Reflection; + +var enumerable = C.M(true, 42); +var enumerator = enumerable.GetEnumerator(); +try +{ + assert(enumerator.MoveNext()); + System.Console.Write($"{enumerator.Current} "); + assert(!enumerator.MoveNext()); + System.Console.Write(" "); + System.Console.Write(((int)enumerator.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator))); +} +finally +{ + enumerator.Dispose(); +} + +void assert(bool b) +{ + if (!b) throw new System.Exception(); +} + +public class C +{ + public static System.Collections.Generic.IEnumerable M(bool b, T t) where T : unmanaged + { + while (b) + { + T local = t; + yield return 10; + b = false; + System.Console.Write(local); + } + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "10 42 42").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.Collections.IEnumerator.MoveNext()", """ +{ + // Code size 94 (0x5e) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.d__0.<>1__state" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0010 + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: beq.s IL_0036 + IL_000e: ldc.i4.0 + IL_000f: ret + IL_0010: ldarg.0 + IL_0011: ldc.i4.m1 + IL_0012: stfld "int C.d__0.<>1__state" + IL_0017: br.s IL_0054 + IL_0019: ldarg.0 + IL_001a: ldarg.0 + IL_001b: ldfld "T C.d__0.t" + IL_0020: stfld "T C.d__0.5__2" + IL_0025: ldarg.0 + IL_0026: ldc.i4.s 10 + IL_0028: stfld "int C.d__0.<>2__current" + IL_002d: ldarg.0 + IL_002e: ldc.i4.1 + IL_002f: stfld "int C.d__0.<>1__state" + IL_0034: ldc.i4.1 + IL_0035: ret + IL_0036: ldarg.0 + IL_0037: ldc.i4.m1 + IL_0038: stfld "int C.d__0.<>1__state" + IL_003d: ldarg.0 + IL_003e: ldc.i4.0 + IL_003f: stfld "bool C.d__0.b" + IL_0044: ldarg.0 + IL_0045: ldfld "T C.d__0.5__2" + IL_004a: box "T" + IL_004f: call "void System.Console.Write(object)" + IL_0054: ldarg.0 + IL_0055: ldfld "bool C.d__0.b" + IL_005a: brtrue.s IL_0019 + IL_005c: ldc.i4.0 + IL_005d: ret +} +"""); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 1 (0x1) + .maxstack 0 + IL_0000: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedLocalWithStructFromAnotherCompilation() + { + var libSrc = """ +public struct S +{ + public int field; + public override string ToString() => field.ToString(); +} +"""; + var libComp = CreateCompilation(libSrc); + var src = """ +using System.Reflection; + +var enumerable = C.M(true, new S { field = 42 }); +var enumerator = enumerable.GetEnumerator(); +try +{ + assert(enumerator.MoveNext()); + System.Console.Write($"{enumerator.Current} "); + assert(!enumerator.MoveNext()); + System.Console.Write(" "); + System.Console.Write(((S)enumerator.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator))); +} +finally +{ + enumerator.Dispose(); +} + +void assert(bool b) +{ + if (!b) throw new System.Exception(); +} + +public class C +{ + public static System.Collections.Generic.IEnumerable M(bool b, S s) + { + while (b) + { + S local = s; + yield return 10; + b = false; + System.Console.Write(local.ToString()); + } + } +} +"""; + + var verifier = CompileAndVerify(src, expectedOutput: "10 42 42", references: [libComp.EmitToImageReference()]).VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.Collections.IEnumerator.MoveNext()", """ +{ + // Code size 100 (0x64) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.d__0.<>1__state" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0010 + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: beq.s IL_0036 + IL_000e: ldc.i4.0 + IL_000f: ret + IL_0010: ldarg.0 + IL_0011: ldc.i4.m1 + IL_0012: stfld "int C.d__0.<>1__state" + IL_0017: br.s IL_005a + IL_0019: ldarg.0 + IL_001a: ldarg.0 + IL_001b: ldfld "S C.d__0.s" + IL_0020: stfld "S C.d__0.5__2" + IL_0025: ldarg.0 + IL_0026: ldc.i4.s 10 + IL_0028: stfld "int C.d__0.<>2__current" + IL_002d: ldarg.0 + IL_002e: ldc.i4.1 + IL_002f: stfld "int C.d__0.<>1__state" + IL_0034: ldc.i4.1 + IL_0035: ret + IL_0036: ldarg.0 + IL_0037: ldc.i4.m1 + IL_0038: stfld "int C.d__0.<>1__state" + IL_003d: ldarg.0 + IL_003e: ldc.i4.0 + IL_003f: stfld "bool C.d__0.b" + IL_0044: ldarg.0 + IL_0045: ldflda "S C.d__0.5__2" + IL_004a: constrained. "S" + IL_0050: callvirt "string object.ToString()" + IL_0055: call "void System.Console.Write(string)" + IL_005a: ldarg.0 + IL_005b: ldfld "bool C.d__0.b" + IL_0060: brtrue.s IL_0019 + IL_0062: ldc.i4.0 + IL_0063: ret +} +"""); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 1 (0x1) + .maxstack 0 + IL_0000: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedUnmanagedWithGenericsLocal() + { + var src = """ +using System.Reflection; + +var enumerable = C.M(true, 42); +var enumerator = enumerable.GetEnumerator(); +try +{ + assert(enumerator.MoveNext()); + System.Console.Write($"{enumerator.Current} "); + assert(!enumerator.MoveNext()); + System.Console.Write(((S)enumerator.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator)).field); +} +finally +{ + enumerator.Dispose(); +} + +void assert(bool b) +{ + if (!b) throw new System.Exception(); +} + +public struct S where T : unmanaged // UnmanagedWithGenerics +{ + public T field; +} + +public class C +{ + public static System.Collections.Generic.IEnumerable M(bool b, T t) where T : unmanaged + { + while (b) + { + S s = new S { field = t }; + yield return 42; + b = false; + System.Console.Write(s.field); + } + } +} +"""; + CompileAndVerify(src, expectedOutput: "42 4242").VerifyDiagnostics(); + } } } diff --git a/src/Compilers/CSharp/Test/Emit/Microsoft.CodeAnalysis.CSharp.Emit.UnitTests.csproj b/src/Compilers/CSharp/Test/Emit/Microsoft.CodeAnalysis.CSharp.Emit.UnitTests.csproj index 3276fae69d6c0..a79819fbe934a 100644 --- a/src/Compilers/CSharp/Test/Emit/Microsoft.CodeAnalysis.CSharp.Emit.UnitTests.csproj +++ b/src/Compilers/CSharp/Test/Emit/Microsoft.CodeAnalysis.CSharp.Emit.UnitTests.csproj @@ -27,6 +27,7 @@ + diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs index be32d75c9af80..f814e6204c9b2 100644 --- a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs @@ -5493,7 +5493,7 @@ static IEnumerable F() diff1.VerifyIL("C.d__0.System.Collections.IEnumerator.MoveNext", @" { - // Code size 526 (0x20e) + // Code size 514 (0x202) .maxstack 2 .locals init (bool V_0, int V_1, @@ -5514,7 +5514,7 @@ .locals init (bool V_0, IL_0022: br IL_0165 IL_0027: ldc.i4.0 IL_0028: stloc.0 - IL_0029: leave IL_020c + IL_0029: leave IL_0200 IL_002e: ldarg.0 IL_002f: ldc.i4.m1 IL_0030: stfld ""int C.d__0.<>1__state"" @@ -5557,7 +5557,7 @@ .locals init (bool V_0, IL_00a6: ldarg.0 IL_00a7: ldc.i4.s -8 IL_00a9: stfld ""int C.d__0.<>1__state"" - IL_00ae: br IL_01a6 + IL_00ae: br IL_019a IL_00b3: ldarg.0 IL_00b4: ldarg.0 IL_00b5: ldfld ""System.Collections.Generic.IEnumerator> C.d__0.<>s__6"" @@ -5608,7 +5608,7 @@ .locals init (bool V_0, IL_013c: stfld ""int C.d__0.<>1__state"" IL_0141: ldc.i4.1 IL_0142: stloc.0 - IL_0143: leave IL_020c + IL_0143: leave IL_0200 IL_0148: ldarg.0 IL_0149: ldc.i4.s -10 IL_014b: stfld ""int C.d__0.<>1__state"" @@ -5620,7 +5620,7 @@ .locals init (bool V_0, IL_0159: stfld ""int C.d__0.<>1__state"" IL_015e: ldc.i4.1 IL_015f: stloc.0 - IL_0160: leave IL_020c + IL_0160: leave IL_0200 IL_0165: ldarg.0 IL_0166: ldc.i4.s -10 IL_0168: stfld ""int C.d__0.<>1__state"" @@ -5642,57 +5642,54 @@ .locals init (bool V_0, IL_0194: ldnull IL_0195: stfld ""System.Collections.Generic.IEnumerator> C.d__0.<>s__8"" IL_019a: ldarg.0 - IL_019b: ldflda ""System.ValueTuple C.d__0.5__7"" - IL_01a0: initobj ""System.ValueTuple"" - IL_01a6: ldarg.0 - IL_01a7: ldfld ""System.Collections.Generic.IEnumerator> C.d__0.<>s__6"" - IL_01ac: callvirt ""bool System.Collections.IEnumerator.MoveNext()"" - IL_01b1: brtrue IL_00b3 - IL_01b6: ldarg.0 - IL_01b7: call ""void C.d__0.<>m__Finally6()"" - IL_01bc: nop - IL_01bd: ldarg.0 - IL_01be: ldnull - IL_01bf: stfld ""System.Collections.Generic.IEnumerator> C.d__0.<>s__6"" - IL_01c4: ldarg.0 - IL_01c5: call ""void C.d__0.<>m__Finally5()"" - IL_01ca: nop - IL_01cb: ldarg.0 - IL_01cc: call ""void C.d__0.<>m__Finally4()"" - IL_01d1: nop - IL_01d2: ldarg.0 - IL_01d3: ldnull - IL_01d4: stfld ""System.IDisposable C.d__0.5__4"" - IL_01d9: ldarg.0 - IL_01da: ldnull - IL_01db: stfld ""System.IDisposable C.d__0.5__5"" - IL_01e0: ldarg.0 - IL_01e1: call ""void C.d__0.<>m__Finally3()"" - IL_01e6: nop - IL_01e7: ldarg.0 - IL_01e8: ldnull - IL_01e9: stfld ""System.IDisposable C.d__0.<>s__3"" - IL_01ee: ldc.i4.0 - IL_01ef: stloc.0 - IL_01f0: br.s IL_01f2 - IL_01f2: ldarg.0 - IL_01f3: call ""void C.d__0.<>m__Finally2()"" - IL_01f8: nop - IL_01f9: br.s IL_01fb - IL_01fb: ldarg.0 - IL_01fc: call ""void C.d__0.<>m__Finally1()"" - IL_0201: nop - IL_0202: leave.s IL_020c + IL_019b: ldfld ""System.Collections.Generic.IEnumerator> C.d__0.<>s__6"" + IL_01a0: callvirt ""bool System.Collections.IEnumerator.MoveNext()"" + IL_01a5: brtrue IL_00b3 + IL_01aa: ldarg.0 + IL_01ab: call ""void C.d__0.<>m__Finally6()"" + IL_01b0: nop + IL_01b1: ldarg.0 + IL_01b2: ldnull + IL_01b3: stfld ""System.Collections.Generic.IEnumerator> C.d__0.<>s__6"" + IL_01b8: ldarg.0 + IL_01b9: call ""void C.d__0.<>m__Finally5()"" + IL_01be: nop + IL_01bf: ldarg.0 + IL_01c0: call ""void C.d__0.<>m__Finally4()"" + IL_01c5: nop + IL_01c6: ldarg.0 + IL_01c7: ldnull + IL_01c8: stfld ""System.IDisposable C.d__0.5__4"" + IL_01cd: ldarg.0 + IL_01ce: ldnull + IL_01cf: stfld ""System.IDisposable C.d__0.5__5"" + IL_01d4: ldarg.0 + IL_01d5: call ""void C.d__0.<>m__Finally3()"" + IL_01da: nop + IL_01db: ldarg.0 + IL_01dc: ldnull + IL_01dd: stfld ""System.IDisposable C.d__0.<>s__3"" + IL_01e2: ldc.i4.0 + IL_01e3: stloc.0 + IL_01e4: br.s IL_01e6 + IL_01e6: ldarg.0 + IL_01e7: call ""void C.d__0.<>m__Finally2()"" + IL_01ec: nop + IL_01ed: br.s IL_01ef + IL_01ef: ldarg.0 + IL_01f0: call ""void C.d__0.<>m__Finally1()"" + IL_01f5: nop + IL_01f6: leave.s IL_0200 } fault { - IL_0204: ldarg.0 - IL_0205: call ""void C.d__0.Dispose()"" - IL_020a: nop - IL_020b: endfinally + IL_01f8: ldarg.0 + IL_01f9: call ""void C.d__0.Dispose()"" + IL_01fe: nop + IL_01ff: endfinally } - IL_020c: ldloc.0 - IL_020d: ret + IL_0200: ldloc.0 + IL_0201: ret }"); } From 96fa04a72ac4c116b83b779fedb59e5b0d27b508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Thu, 14 Nov 2024 13:25:49 -0800 Subject: [PATCH 361/508] Include projects to restart/rebuild in ManagedHotReloadUpdates (#75918) --- .../EditAndContinue/EditAndContinueLanguageService.cs | 9 ++++++++- .../Debugger/GlassTestsHotReloadService.cs | 2 +- .../EditAndContinue/EditAndContinueWorkspaceTestBase.cs | 8 ++++---- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs index d0375b06d13c8..c4d3dd98990c0 100644 --- a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs +++ b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs @@ -400,6 +400,13 @@ public async ValueTask GetUpdatesAsync(ImmutableArray GetProjectPaths(ImmutableArray ids) + => ids.SelectAsArray(static (id, solution) => solution.GetRequiredProject(id).FilePath!, solution); } } diff --git a/src/EditorFeatures/ExternalAccess/Debugger/GlassTestsHotReloadService.cs b/src/EditorFeatures/ExternalAccess/Debugger/GlassTestsHotReloadService.cs index 41c74790a3687..8c21d74f0f2f6 100644 --- a/src/EditorFeatures/ExternalAccess/Debugger/GlassTestsHotReloadService.cs +++ b/src/EditorFeatures/ExternalAccess/Debugger/GlassTestsHotReloadService.cs @@ -85,7 +85,7 @@ public void EndDebuggingSession() public async ValueTask GetUpdatesAsync(Solution solution, CancellationToken cancellationToken) { var results = (await _encService.EmitSolutionUpdateAsync(GetSessionId(), solution, runningProjects: [], s_noActiveStatementSpanProvider, cancellationToken).ConfigureAwait(false)).Dehydrate(); - return new ManagedHotReloadUpdates(results.ModuleUpdates.Updates.FromContract(), results.GetAllDiagnostics().FromContract()); + return new ManagedHotReloadUpdates(results.ModuleUpdates.Updates.FromContract(), results.GetAllDiagnostics().FromContract(), [], []); } } } diff --git a/src/Features/TestUtilities/EditAndContinue/EditAndContinueWorkspaceTestBase.cs b/src/Features/TestUtilities/EditAndContinue/EditAndContinueWorkspaceTestBase.cs index fc40635903865..eb6e9ac492dc2 100644 --- a/src/Features/TestUtilities/EditAndContinue/EditAndContinueWorkspaceTestBase.cs +++ b/src/Features/TestUtilities/EditAndContinue/EditAndContinueWorkspaceTestBase.cs @@ -54,13 +54,13 @@ public abstract class EditAndContinueWorkspaceTestBase : TestBase, IDisposable /// /// Streams that are verified to be disposed at the end of the debug session (by default). /// - public List<(Guid mvid, Stream stream)> DisposalVerifiedStreams = []; + private ImmutableList _disposalVerifiedStreams = []; public override void Dispose() { base.Dispose(); - foreach (var (_, stream) in DisposalVerifiedStreams) + foreach (var stream in _disposalVerifiedStreams) { Assert.False(stream.CanRead); } @@ -314,7 +314,7 @@ internal Guid EmitLibrary(Compilation compilation, DebugInformationFormat pdbFor OpenAssemblyStreamImpl = () => { var stream = new MemoryStream(); - DisposalVerifiedStreams.Add((moduleId, stream)); + ImmutableInterlocked.Update(ref _disposalVerifiedStreams, s => s.Add(stream)); peImage.WriteToStream(stream); stream.Position = 0; return stream; @@ -322,7 +322,7 @@ internal Guid EmitLibrary(Compilation compilation, DebugInformationFormat pdbFor OpenPdbStreamImpl = () => { var stream = new MemoryStream(); - DisposalVerifiedStreams.Add((moduleId, stream)); + ImmutableInterlocked.Update(ref _disposalVerifiedStreams, s => s.Add(stream)); pdbImage.WriteToStream(stream); stream.Position = 0; return stream; From 6acf726dbbdba68e61e877ca054e7be8b08e45d1 Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Thu, 14 Nov 2024 13:37:45 -0800 Subject: [PATCH 362/508] Reduce formatting allocation in CodeAnalysis process in scrolling speedometer (#75912) TokenPairWithOperations[] accounts for 2.4% of total allocations in the test. Locally reproducing this demonstrated that there is usually a small number of tokens that are added, but occasionally a much larger token count is added. Doing a single shot allocation in the case where we are significantly increasing the SegmentedList size is more efficient than growing organically by adding single items. --- .../Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs index 83886f8fdf3b6..15700c2a11709 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs @@ -223,6 +223,11 @@ private void AddTokenOperations( using (Logger.LogBlock(FunctionId.Formatting_CollectTokenOperation, cancellationToken)) { + // Grow the SegmentedList in a single allocation if the resultant list is going to be + // significantly larger than it's existing Capacity. + if (tokenStream.TokenCount > 2 * list.Capacity) + list.EnsureCapacity(tokenStream.TokenCount); + foreach (var (index, currentToken, nextToken) in tokenStream.TokenIterator) { cancellationToken.ThrowIfCancellationRequested(); From da8e09e6fd555eb215d20c6beb7d9a9f58e1023f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 13:41:16 -0800 Subject: [PATCH 363/508] Update src/Compilers/CSharp/Portable/Parser/LanguageParser.cs Co-authored-by: Julien Couvreur --- src/Compilers/CSharp/Portable/Parser/LanguageParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index f3345fab50efb..8ed96d2f9c71a 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -6052,7 +6052,7 @@ private ScanTypeFlags ScanPossibleTypeArgumentList( return result; } - // Allow for any chain of errant commas in teh generic name. like `Dictionary<,int>` or + // Allow for any chain of errant commas in the generic name. like `Dictionary<,int>` or // `Dictionary` We still want to think of these as generics, just with missing type-arguments, vs // some invalid tree-expression that we would otherwise form. if (this.CurrentToken.Kind == SyntaxKind.CommaToken) From 31474976a0f2c6f6b8b5d26476b52936be7671b4 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 13:45:22 -0800 Subject: [PATCH 364/508] ADd tests --- .../Test/Semantic/Semantics/NameOfTests.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index 9b657176b542d..529e7fdadf3a2 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -2382,6 +2382,30 @@ public void OpenTypeInNameof_CSharp13() Diagnostic(ErrorCode.ERR_FeatureInPreview, "List<>").WithArguments("Unbound generic types in nameof operator").WithLocation(4, 16)); } + [Fact] + public void OpenTypeInNameof_Next() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List<>); + Console.WriteLine(v); + """, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_Preview() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List<>); + Console.WriteLine(v); + """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(); + } + [Fact] public void OpenTypeInNameof_CSharp13_Nested1() { From 1df770bb23869ad9dc00d5ede15ca4bf63fe8c6a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 13:47:01 -0800 Subject: [PATCH 365/508] Remove unnecessary options --- .../Test/Semantic/Semantics/NameOfTests.cs | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index 529e7fdadf3a2..2604a25dfcb77 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -2368,7 +2368,7 @@ class Attr : System.Attribute { public Attr(string s) {} }"; } [Fact] - public void OpenTypeInNameof_CSharp13() + public void OpenTypeInNameof_Preview() { CreateCompilation(""" using System; @@ -2376,14 +2376,11 @@ public void OpenTypeInNameof_CSharp13() var v = nameof(List<>); Console.WriteLine(v); - """, parseOptions: TestOptions.Regular13).VerifyDiagnostics( - // (4,16): error CS8652: The feature 'Unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // var v = nameof(List<>); - Diagnostic(ErrorCode.ERR_FeatureInPreview, "List<>").WithArguments("Unbound generic types in nameof operator").WithLocation(4, 16)); + """).VerifyDiagnostics(); } [Fact] - public void OpenTypeInNameof_Next() + public void OpenTypeInNameof_CSharp13() { CreateCompilation(""" using System; @@ -2391,11 +2388,14 @@ public void OpenTypeInNameof_Next() var v = nameof(List<>); Console.WriteLine(v); - """, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(); + """, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (4,16): error CS8652: The feature 'Unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var v = nameof(List<>); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "List<>").WithArguments("Unbound generic types in nameof operator").WithLocation(4, 16)); } [Fact] - public void OpenTypeInNameof_Preview() + public void OpenTypeInNameof_Next() { CreateCompilation(""" using System; @@ -2403,7 +2403,7 @@ public void OpenTypeInNameof_Preview() var v = nameof(List<>); Console.WriteLine(v); - """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(); + """, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(); } [Fact] @@ -2467,7 +2467,7 @@ public void OpenTypeInNameof_BaseCase() var v = nameof(List<>); Console.WriteLine(v); - """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(), + """).VerifyDiagnostics(), expectedOutput: "List"); } @@ -2482,7 +2482,7 @@ public void OpenTypeInNameof_Nested1() Console.WriteLine(v); class A { public class B; } - """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(), + """).VerifyDiagnostics(), expectedOutput: "B"); } @@ -2497,7 +2497,7 @@ public void OpenTypeInNameof_Nested2() Console.WriteLine(v); class A { public class B; } - """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(), + """).VerifyDiagnostics(), expectedOutput: "B"); } @@ -2512,7 +2512,7 @@ public void OpenTypeInNameof_Nested3() Console.WriteLine(v); class A { public class B; } - """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(), + """).VerifyDiagnostics(), expectedOutput: "B"); } @@ -2526,7 +2526,7 @@ public void OpenTypeInNameof_MultipleTypeArguments() var v = nameof(Dictionary<,>); Console.WriteLine(v); - """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(), + """).VerifyDiagnostics(), expectedOutput: "Dictionary"); } @@ -2539,7 +2539,7 @@ public void OpenTypeInNameof_IncorrectTypeArgumentCount1() var v = nameof(Dictionary<>); Console.WriteLine(v); - """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + """).VerifyDiagnostics( // (2,1): hidden CS8019: Unnecessary using directive. // using System.Collections.Generic; Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using System.Collections.Generic;").WithLocation(2, 1), @@ -2557,7 +2557,7 @@ public void OpenTypeInNameof_IncorrectTypeArgumentCount2() var v = nameof(List<,>); Console.WriteLine(v); - """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + """).VerifyDiagnostics( // (2,1): hidden CS8019: Unnecessary using directive. // using System.Collections.Generic; Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using System.Collections.Generic;").WithLocation(2, 1), @@ -2575,7 +2575,7 @@ public void OpenTypeInNameof_NoNestedOpenTypes() var v = nameof(List>); Console.WriteLine(v); - """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + """).VerifyDiagnostics( // (4,21): error CS9270: Nested unbound generic type not allowed in 'nameof' operator // var v = nameof(List>); Diagnostic(ErrorCode.ERR_NestedUnboundTypeNotAllowedInNameofExpression, "List<>").WithLocation(4, 21)); @@ -2590,7 +2590,7 @@ public void OpenTypeInNameof_NoPartialOpenTypes_1() var v = nameof(Dictionary<,int>); Console.WriteLine(v); - """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + """).VerifyDiagnostics( // (4,27): error CS1031: Type expected // var v = nameof(Dictionary<,int>); Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(4, 27)); @@ -2605,7 +2605,7 @@ public void OpenTypeInNameof_NoPartialOpenTypes_2() var v = nameof(Dictionary); Console.WriteLine(v); - """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + """).VerifyDiagnostics( // (4,31): error CS1031: Type expected // var v = nameof(Dictionary); Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(4, 31)); @@ -2621,7 +2621,7 @@ public void OpenTypeInNameof_MemberAccessThatDoesNotUseTypeArgument() var v = nameof(List<>.Count); Console.WriteLine(v); - """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(), + """).VerifyDiagnostics(), expectedOutput: "Count"); } @@ -2639,7 +2639,7 @@ interface IGoo { T Count { get; } } - """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(), + """).VerifyDiagnostics(), expectedOutput: "Count"); } @@ -2657,7 +2657,7 @@ interface IGoo { T Count { get; } } - """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(), + """).VerifyDiagnostics(), expectedOutput: "ToString"); } @@ -2675,7 +2675,7 @@ interface IGoo where T : IComparable { T X { get; } } - """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(), + """).VerifyDiagnostics(), expectedOutput: "CompareTo"); } @@ -2693,7 +2693,7 @@ interface IGoo where T : U where U : IComparable { T X { get; } } - """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(), + """).VerifyDiagnostics(), expectedOutput: "CompareTo"); } @@ -2716,7 +2716,7 @@ interface IGoo where T : Base { T X { get; } } - """, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(), + """).VerifyDiagnostics(), expectedOutput: "Z"); } } From 5a695dc52f19c91450921a32b923422f9ea48b12 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 13:55:52 -0800 Subject: [PATCH 366/508] Add tests --- .../Parsing/TypeArgumentListParsingTests.cs | 878 +++++++++++++++++- 1 file changed, 874 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/TypeArgumentListParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/TypeArgumentListParsingTests.cs index 9a47468d3af2b..4a14eda2eeba3 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/TypeArgumentListParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/TypeArgumentListParsingTests.cs @@ -2,19 +2,17 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.CSharp.UnitTests { - public class TypeArgumentListParsingTests : ParsingTests + public sealed class TypeArgumentListParsingTests : ParsingTests { public TypeArgumentListParsingTests(ITestOutputHelper output) : base(output) { } - protected override SyntaxTree ParseTree(string text, CSharpParseOptions options) + protected override SyntaxTree ParseTree(string text, CSharpParseOptions? options) { return SyntaxFactory.ParseSyntaxTree(text, options: options); } @@ -2826,5 +2824,877 @@ void M() } EOF(); } + + [Fact] + public void TestGenericWithExtraCommasAndMissingTypes1() + { + UsingTree(""" + class C + { + void M() + { + var added = Goo.Instance; + } + } + """, + // (5,32): error CS1031: Type expected + // var added = Goo.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 32), + // (5,33): error CS1031: Type expected + // var added = Goo.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(5, 33)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "added"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Goo"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Instance"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void TestGenericWithExtraCommasAndMissingTypes2() + { + UsingTree(""" + class C + { + void M() + { + var added = Goo.Instance; + } + } + """, + // (5,28): error CS1031: Type expected + // var added = Goo.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 28), + // (5,29): error CS1031: Type expected + // var added = Goo.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(5, 29)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "added"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Goo"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Id"); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Instance"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void TestGenericWithExtraCommasAndMissingTypes3() + { + UsingTree(""" + class C + { + void M() + { + var added = Goo<,Id,>.Instance; + } + } + """, + // (5,25): error CS1031: Type expected + // var added = Goo<,Id,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 25), + // (5,29): error CS1031: Type expected + // var added = Goo<,Id,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(5, 29)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "added"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Goo"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Id"); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Instance"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void TestGenericWithExtraCommasAndMissingTypes4() + { + UsingTree(""" + class C + { + void M() + { + var added = Goo<,,Id>.Instance; + } + } + """, + // (5,25): error CS1031: Type expected + // var added = Goo<,,Id>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 25), + // (5,26): error CS1031: Type expected + // var added = Goo<,,Id>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 26)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "added"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Goo"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Id"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Instance"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void TestGenericWithExtraCommasAndMissingTypes5() + { + UsingTree(""" + class C + { + void M() + { + var added = Goo.Instance; + } + } + """, + // (5,30): error CS1031: Type expected + // var added = Goo.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 30), + // (5,31): error CS1031: Type expected + // var added = Goo.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(5, 31)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "added"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Goo"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Id"); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Instance"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void TestGenericWithExtraCommasAndMissingTypes6() + { + UsingTree(""" + class C + { + void M() + { + var added = Goo<(int i, int j),,>.Instance; + } + } + """, + // (5,40): error CS1031: Type expected + // var added = Goo<(int i, int j),,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 40), + // (5,41): error CS1031: Type expected + // var added = Goo<(int i, int j),,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(5, 41)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "added"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Goo"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "i"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "j"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Instance"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void TestGenericWithExtraCommasAndMissingTypes7() + { + UsingTree(""" + class C + { + void M() + { + var added = Goo,,>.Instance; + } + } + """, + // (5,32): error CS1031: Type expected + // var added = Goo,,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 32), + // (5,33): error CS1031: Type expected + // var added = Goo,,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(5, 33)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "added"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Goo"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "K"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Instance"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void TestGenericWithExtraCommasAndMissingTypes8() + { + UsingTree(""" + class C + { + void M() + { + var added = Goo,,>.Instance; + } + } + """, + // (5,31): error CS1031: Type expected + // var added = Goo,,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 31), + // (5,32): error CS1031: Type expected + // var added = Goo,,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(5, 32), + // (5,34): error CS1031: Type expected + // var added = Goo,,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 34), + // (5,35): error CS1031: Type expected + // var added = Goo,,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(5, 35)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "added"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Goo"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "K"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Instance"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } } } From 4fcadd9681ec2c8ea9e1faac0f395b6bcb41b415 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 13:59:09 -0800 Subject: [PATCH 367/508] Simplify tests --- .../Test/Semantic/Semantics/NameOfTests.cs | 192 ++++++++---------- 1 file changed, 85 insertions(+), 107 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index 2604a25dfcb77..c93d5d859ebb5 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -2460,74 +2460,64 @@ class A { public class B; } [Fact] public void OpenTypeInNameof_BaseCase() { - CompileAndVerify( - CreateCompilation(""" - using System; - using System.Collections.Generic; + CompileAndVerify(""" + using System; + using System.Collections.Generic; - var v = nameof(List<>); - Console.WriteLine(v); - """).VerifyDiagnostics(), - expectedOutput: "List"); + var v = nameof(List<>); + Console.WriteLine(v); + """, expectedOutput: "List").VerifyDiagnostics(); } [Fact] public void OpenTypeInNameof_Nested1() { - CompileAndVerify( - CreateCompilation(""" - using System; + CompileAndVerify(""" + using System; - var v = nameof(A<>.B); - Console.WriteLine(v); + var v = nameof(A<>.B); + Console.WriteLine(v); - class A { public class B; } - """).VerifyDiagnostics(), - expectedOutput: "B"); + class A { public class B; } + """, expectedOutput: "B").VerifyDiagnostics(); } [Fact] public void OpenTypeInNameof_Nested2() { - CompileAndVerify( - CreateCompilation(""" - using System; + CompileAndVerify(""" + using System; - var v = nameof(A.B<>); - Console.WriteLine(v); + var v = nameof(A.B<>); + Console.WriteLine(v); - class A { public class B; } - """).VerifyDiagnostics(), - expectedOutput: "B"); + class A { public class B; } + """, expectedOutput: "B").VerifyDiagnostics(); } [Fact] public void OpenTypeInNameof_Nested3() { - CompileAndVerify( - CreateCompilation(""" - using System; + CompileAndVerify(""" + using System; - var v = nameof(A<>.B<>); - Console.WriteLine(v); + var v = nameof(A<>.B<>); + Console.WriteLine(v); - class A { public class B; } - """).VerifyDiagnostics(), - expectedOutput: "B"); + class A { public class B; } + """, expectedOutput: "B").VerifyDiagnostics(); } [Fact] public void OpenTypeInNameof_MultipleTypeArguments() { - CompileAndVerify( - CreateCompilation(""" - using System; - using System.Collections.Generic; + CompileAndVerify(""" + using System; + using System.Collections.Generic; - var v = nameof(Dictionary<,>); - Console.WriteLine(v); - """).VerifyDiagnostics(), - expectedOutput: "Dictionary"); + var v = nameof(Dictionary<,>); + Console.WriteLine(v); + """, expectedOutput: "Dictionary").VerifyDiagnostics(); } [Fact] @@ -2614,110 +2604,98 @@ public void OpenTypeInNameof_NoPartialOpenTypes_2() [Fact] public void OpenTypeInNameof_MemberAccessThatDoesNotUseTypeArgument() { - CompileAndVerify( - CreateCompilation(""" - using System; - using System.Collections.Generic; + CompileAndVerify(""" + using System; + using System.Collections.Generic; - var v = nameof(List<>.Count); - Console.WriteLine(v); - """).VerifyDiagnostics(), - expectedOutput: "Count"); + var v = nameof(List<>.Count); + Console.WriteLine(v); + """, expectedOutput: "Count").VerifyDiagnostics(); } [Fact] public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument() { - CompileAndVerify( - CreateCompilation(""" - using System; + CompileAndVerify(""" + using System; - var v = nameof(IGoo<>.Count); - Console.WriteLine(v); + var v = nameof(IGoo<>.Count); + Console.WriteLine(v); - interface IGoo - { - T Count { get; } - } - """).VerifyDiagnostics(), - expectedOutput: "Count"); + interface IGoo + { + T Count { get; } + } + """, expectedOutput: "Count").VerifyDiagnostics(); } [Fact] public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument_ReferenceObjectMember() { - CompileAndVerify( - CreateCompilation(""" - using System; + CompileAndVerify(""" + using System; - var v = nameof(IGoo<>.Count.ToString); - Console.WriteLine(v); + var v = nameof(IGoo<>.Count.ToString); + Console.WriteLine(v); - interface IGoo - { - T Count { get; } - } - """).VerifyDiagnostics(), - expectedOutput: "ToString"); + interface IGoo + { + T Count { get; } + } + """, expectedOutput: "ToString").VerifyDiagnostics(); } [Fact] public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument_ReferenceConstraintMember_Interface() { - CompileAndVerify( - CreateCompilation(""" - using System; + CompileAndVerify(""" + using System; - var v = nameof(IGoo<>.X.CompareTo); - Console.WriteLine(v); + var v = nameof(IGoo<>.X.CompareTo); + Console.WriteLine(v); - interface IGoo where T : IComparable - { - T X { get; } - } - """).VerifyDiagnostics(), - expectedOutput: "CompareTo"); + interface IGoo where T : IComparable + { + T X { get; } + } + """, expectedOutput: "CompareTo").VerifyDiagnostics(); } [Fact] public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument_ReferenceConstraintMember_ThroughTypeParameter() { - CompileAndVerify( - CreateCompilation(""" - using System; + CompileAndVerify(""" + using System; - var v = nameof(IGoo<,>.X.CompareTo); - Console.WriteLine(v); + var v = nameof(IGoo<,>.X.CompareTo); + Console.WriteLine(v); - interface IGoo where T : U where U : IComparable - { - T X { get; } - } - """).VerifyDiagnostics(), - expectedOutput: "CompareTo"); + interface IGoo where T : U where U : IComparable + { + T X { get; } + } + """, expectedOutput: "CompareTo").VerifyDiagnostics(); } [Fact] public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument_ReferenceConstraintMember_Class() { - CompileAndVerify( - CreateCompilation(""" - using System; + CompileAndVerify(""" + using System; - var v = nameof(IGoo<>.X.Z); - Console.WriteLine(v); + var v = nameof(IGoo<>.X.Z); + Console.WriteLine(v); - class Base - { - public int Z { get; } - } + class Base + { + public int Z { get; } + } - interface IGoo where T : Base - { - T X { get; } - } - """).VerifyDiagnostics(), - expectedOutput: "Z"); + interface IGoo where T : Base + { + T X { get; } + } + """, expectedOutput: "Z").VerifyDiagnostics(); } } } From 7aff3440ea5f49733bf65b6e4b2b439ec7f51f89 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 14:12:15 -0800 Subject: [PATCH 368/508] Share open type binding --- .../Portable/Binder/Binder_Invocation.cs | 27 ----- .../CSharp/Portable/Binder/Binder_Symbols.cs | 2 +- .../CSharp/Portable/Binder/NameofBinder.cs | 12 ++- .../CSharp/Portable/Binder/OpenTypeVisitor.cs | 99 +++++++++++++++++++ .../CSharp/Portable/Binder/TypeofBinder.cs | 94 +----------------- .../CSharp/Portable/CSharpResources.resx | 3 - .../CSharp/Portable/Errors/ErrorCode.cs | 2 - .../CSharp/Portable/Errors/ErrorFacts.cs | 1 - .../Portable/xlf/CSharpResources.cs.xlf | 5 - .../Portable/xlf/CSharpResources.de.xlf | 5 - .../Portable/xlf/CSharpResources.es.xlf | 5 - .../Portable/xlf/CSharpResources.fr.xlf | 5 - .../Portable/xlf/CSharpResources.it.xlf | 5 - .../Portable/xlf/CSharpResources.ja.xlf | 5 - .../Portable/xlf/CSharpResources.ko.xlf | 5 - .../Portable/xlf/CSharpResources.pl.xlf | 5 - .../Portable/xlf/CSharpResources.pt-BR.xlf | 5 - .../Portable/xlf/CSharpResources.ru.xlf | 5 - .../Portable/xlf/CSharpResources.tr.xlf | 5 - .../Portable/xlf/CSharpResources.zh-Hans.xlf | 5 - .../Portable/xlf/CSharpResources.zh-Hant.xlf | 5 - .../Test/Semantic/Semantics/NameOfTests.cs | 4 +- 22 files changed, 114 insertions(+), 195 deletions(-) create mode 100644 src/Compilers/CSharp/Portable/Binder/OpenTypeVisitor.cs diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index cea3ebdc28da1..b1dfcab8fc30d 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -2222,8 +2222,6 @@ private BoundExpression BindNameofOperatorInternal(InvocationExpressionSyntax no CheckFeatureAvailability(node, MessageID.IDS_FeatureNameof, diagnostics); var argument = node.ArgumentList.Arguments[0].Expression; - validateOpenGenericNames(argument, topLevel: true); - var boundArgument = BindExpression(argument, diagnostics); bool syntaxIsOk = CheckSyntaxForNameofArgument(argument, out string name, boundArgument.HasAnyErrors ? BindingDiagnosticBag.Discarded : diagnostics); @@ -2248,31 +2246,6 @@ private BoundExpression BindNameofOperatorInternal(InvocationExpressionSyntax no boundArgument = BindToNaturalType(boundArgument, diagnostics, reportNoTargetType: false); return new BoundNameOfOperator(node, boundArgument, ConstantValue.Create(name), Compilation.GetSpecialType(SpecialType.System_String)); - - void validateOpenGenericNames(SyntaxNode current, bool topLevel) - { - if (current is TypeArgumentListSyntax typeArgumentList) - { - if (!topLevel) - { - foreach (var typeArgument in typeArgumentList.Arguments) - { - if (typeArgument.Kind() == SyntaxKind.OmittedTypeArgument) - diagnostics.Add(ErrorCode.ERR_NestedUnboundTypeNotAllowedInNameofExpression, typeArgumentList.Parent); - } - } - - // Once we hit a type argument list, we're no longer at the top level, and we want to report errors - // for any omitted type arguments we see at a deeper level. - topLevel = false; - } - - foreach (var child in current.ChildNodesAndTokens()) - { - if (child.IsNode) - validateOpenGenericNames(child.AsNode(), topLevel); - } - } } private void EnsureNameofExpressionSymbols(BoundMethodGroup methodGroup, BindingDiagnosticBag diagnostics) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index aa1593be79fe3..7aebd527acc06 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -1212,7 +1212,7 @@ private TypeWithAnnotations BindGenericSimpleNamespaceOrTypeOrAliasSymbol( if (isUnboundTypeExpr) { - if (!IsInsideNameof && !IsUnboundTypeAllowed(node)) + if (!IsUnboundTypeAllowed(node)) { // If we already have an error type then skip reporting that the unbound type is illegal. if (!unconstructedType.IsErrorType()) diff --git a/src/Compilers/CSharp/Portable/Binder/NameofBinder.cs b/src/Compilers/CSharp/Portable/Binder/NameofBinder.cs index 668cad30a97f3..acf4ba3dfcf59 100644 --- a/src/Compilers/CSharp/Portable/Binder/NameofBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/NameofBinder.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Generic; using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp @@ -19,19 +21,25 @@ namespace Microsoft.CodeAnalysis.CSharp ///
internal sealed class NameofBinder : Binder { - private readonly SyntaxNode _nameofArgument; + private readonly ExpressionSyntax _nameofArgument; private readonly WithTypeParametersBinder? _withTypeParametersBinder; private readonly Binder? _withParametersBinder; private ThreeState _lazyIsNameofOperator; - internal NameofBinder(SyntaxNode nameofArgument, Binder next, WithTypeParametersBinder? withTypeParametersBinder, Binder? withParametersBinder) + private readonly Dictionary? _allowedMap; + + internal NameofBinder(ExpressionSyntax nameofArgument, Binder next, WithTypeParametersBinder? withTypeParametersBinder, Binder? withParametersBinder) : base(next) { _nameofArgument = nameofArgument; _withTypeParametersBinder = withTypeParametersBinder; _withParametersBinder = withParametersBinder; + OpenTypeVisitor.Visit(nameofArgument, out _allowedMap); } + protected override bool IsUnboundTypeAllowed(GenericNameSyntax syntax) + => _allowedMap != null && _allowedMap.TryGetValue(syntax, out bool allowed) && allowed; + private bool IsNameofOperator { get diff --git a/src/Compilers/CSharp/Portable/Binder/OpenTypeVisitor.cs b/src/Compilers/CSharp/Portable/Binder/OpenTypeVisitor.cs new file mode 100644 index 0000000000000..1b1b9c1326877 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Binder/OpenTypeVisitor.cs @@ -0,0 +1,99 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Microsoft.CodeAnalysis.CSharp; + +internal partial class Binder +{ + /// + /// This visitor walks over a type expression looking for open types. + /// + /// Open types are allowed if an only if: + /// + /// There is no constructed generic type elsewhere in the visited syntax; and + /// The open type is not used as a type argument or array/pointer/nullable element type. + /// + /// + /// Open types can be used both in typeof(...) and nameof(...) expressions. + /// + protected sealed class OpenTypeVisitor : CSharpSyntaxVisitor + { + private Dictionary? _allowedMap; + private bool _seenConstructed; + + /// The argument to typeof. + /// + /// Keys are GenericNameSyntax nodes representing unbound generic types. + /// Values are false if the node should result in an error and true otherwise. + /// + public static void Visit(ExpressionSyntax typeSyntax, out Dictionary? allowedMap) + { + OpenTypeVisitor visitor = new OpenTypeVisitor(); + visitor.Visit(typeSyntax); + allowedMap = visitor._allowedMap; + } + + public override void VisitGenericName(GenericNameSyntax node) + { + SeparatedSyntaxList typeArguments = node.TypeArgumentList.Arguments; + if (node.IsUnboundGenericName) + { + _allowedMap ??= []; + _allowedMap[node] = !_seenConstructed; + } + else + { + _seenConstructed = true; + foreach (TypeSyntax arg in typeArguments) + { + Visit(arg); + } + } + } + + public override void VisitQualifiedName(QualifiedNameSyntax node) + { + bool seenConstructedBeforeRight = _seenConstructed; + + // Visit Right first because it's smaller (to make backtracking cheaper). + Visit(node.Right); + + bool seenConstructedBeforeLeft = _seenConstructed; + + Visit(node.Left); + + // If the first time we saw a constructed type was in Left, then we need to re-visit Right + if (!seenConstructedBeforeRight && !seenConstructedBeforeLeft && _seenConstructed) + { + Visit(node.Right); + } + } + + public override void VisitAliasQualifiedName(AliasQualifiedNameSyntax node) + { + Visit(node.Name); + } + + public override void VisitArrayType(ArrayTypeSyntax node) + { + _seenConstructed = true; + Visit(node.ElementType); + } + + public override void VisitPointerType(PointerTypeSyntax node) + { + _seenConstructed = true; + Visit(node.ElementType); + } + + public override void VisitNullableType(NullableTypeSyntax node) + { + _seenConstructed = true; + Visit(node.ElementType); + } + } +} diff --git a/src/Compilers/CSharp/Portable/Binder/TypeofBinder.cs b/src/Compilers/CSharp/Portable/Binder/TypeofBinder.cs index 279fbd58e052c..6ba2056754f2b 100644 --- a/src/Compilers/CSharp/Portable/Binder/TypeofBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/TypeofBinder.cs @@ -20,7 +20,7 @@ namespace Microsoft.CodeAnalysis.CSharp /// should be considered open so that the flag can be set /// appropriately in BoundTypeOfOperator. ///
- internal sealed class TypeofBinder : Binder + internal sealed partial class TypeofBinder : Binder { private readonly Dictionary _allowedMap; @@ -32,96 +32,6 @@ internal TypeofBinder(ExpressionSyntax typeExpression, Binder next) } protected override bool IsUnboundTypeAllowed(GenericNameSyntax syntax) - { - bool allowed; - return _allowedMap != null && _allowedMap.TryGetValue(syntax, out allowed) && allowed; - } - - /// - /// This visitor walks over a type expression looking for open types. - /// Open types are allowed if an only if: - /// 1) There is no constructed generic type elsewhere in the visited syntax; and - /// 2) The open type is not used as a type argument or array/pointer/nullable - /// element type. - /// - private class OpenTypeVisitor : CSharpSyntaxVisitor - { - private Dictionary _allowedMap; - private bool _seenConstructed; - - /// The argument to typeof. - /// - /// Keys are GenericNameSyntax nodes representing unbound generic types. - /// Values are false if the node should result in an error and true otherwise. - /// - public static void Visit(ExpressionSyntax typeSyntax, out Dictionary allowedMap) - { - OpenTypeVisitor visitor = new OpenTypeVisitor(); - visitor.Visit(typeSyntax); - allowedMap = visitor._allowedMap; - } - - public override void VisitGenericName(GenericNameSyntax node) - { - SeparatedSyntaxList typeArguments = node.TypeArgumentList.Arguments; - if (node.IsUnboundGenericName) - { - if (_allowedMap == null) - { - _allowedMap = new Dictionary(); - } - _allowedMap[node] = !_seenConstructed; - } - else - { - _seenConstructed = true; - foreach (TypeSyntax arg in typeArguments) - { - Visit(arg); - } - } - } - - public override void VisitQualifiedName(QualifiedNameSyntax node) - { - bool seenConstructedBeforeRight = _seenConstructed; - - // Visit Right first because it's smaller (to make backtracking cheaper). - Visit(node.Right); - - bool seenConstructedBeforeLeft = _seenConstructed; - - Visit(node.Left); - - // If the first time we saw a constructed type was in Left, then we need to re-visit Right - if (!seenConstructedBeforeRight && !seenConstructedBeforeLeft && _seenConstructed) - { - Visit(node.Right); - } - } - - public override void VisitAliasQualifiedName(AliasQualifiedNameSyntax node) - { - Visit(node.Name); - } - - public override void VisitArrayType(ArrayTypeSyntax node) - { - _seenConstructed = true; - Visit(node.ElementType); - } - - public override void VisitPointerType(PointerTypeSyntax node) - { - _seenConstructed = true; - Visit(node.ElementType); - } - - public override void VisitNullableType(NullableTypeSyntax node) - { - _seenConstructed = true; - Visit(node.ElementType); - } - } + => _allowedMap != null && _allowedMap.TryGetValue(syntax, out bool allowed) && allowed; } } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 5c89169ebc8d0..3b72670421515 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -8008,9 +8008,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Non-nullable property must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. - - Nested unbound generic type not allowed in 'nameof' operator - first-class Span types diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 7918c9339f489..90e45ee89de30 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2345,8 +2345,6 @@ internal enum ErrorCode ERR_CannotApplyOverloadResolutionPriorityToMember = 9262, ERR_PartialPropertyDuplicateInitializer = 9263, - ERR_NestedUnboundTypeNotAllowedInNameofExpression = 9270, - WRN_UninitializedNonNullableBackingField = 9264, WRN_UnassignedInternalRefField = 9265, WRN_AccessorDoesNotUseBackingField = 9266, diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index 609393fa529ac..b3c03386122fa 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -2467,7 +2467,6 @@ or ErrorCode.WRN_UninitializedNonNullableBackingField or ErrorCode.WRN_UnassignedInternalRefField or ErrorCode.WRN_AccessorDoesNotUseBackingField or ErrorCode.ERR_IteratorRefLikeElementType - or ErrorCode.ERR_NestedUnboundTypeNotAllowedInNameofExpression => false, }; #pragma warning restore CS8524 // The switch expression does not handle some values of its input type (it is not exhaustive) involving an unnamed enum value. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 90f4f4128269f..d57d2056d6ac4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -1457,11 +1457,6 @@ Přístup k vloženému poli nemůže mít specifikátor pojmenovaného argumentu. - - Nested unbound generic type not allowed in 'nameof' operator - Nested unbound generic type not allowed in 'nameof' operator - - The 'new()' constraint cannot be used with the 'unmanaged' constraint Omezení new() nejde používat s omezením unmanaged. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index de221dea80883..d1c2050f539cc 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -1457,11 +1457,6 @@ Ein Inlinearrayzugriff verfügt möglicherweise nicht über einen benannten Argumentspezifizierer. - - Nested unbound generic type not allowed in 'nameof' operator - Nested unbound generic type not allowed in 'nameof' operator - - The 'new()' constraint cannot be used with the 'unmanaged' constraint Die new()-Einschränkung kann nicht mit der unmanaged-Einschränkung verwendet werden. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index b3b218dad5b8d..18af306ceff6a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -1457,11 +1457,6 @@ Un acceso de matriz insertado no puede tener un especificador de argumento con nombre - - Nested unbound generic type not allowed in 'nameof' operator - Nested unbound generic type not allowed in 'nameof' operator - - The 'new()' constraint cannot be used with the 'unmanaged' constraint La restricción "new()" no se puede utilizar con la restricción "unmanaged" diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index cf9930538a830..20801aee27d5d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -1457,11 +1457,6 @@ Un accès au tableau en ligne peut ne pas avoir de spécificateur d'argument nommé - - Nested unbound generic type not allowed in 'nameof' operator - Nested unbound generic type not allowed in 'nameof' operator - - The 'new()' constraint cannot be used with the 'unmanaged' constraint La contrainte 'new()' ne peut pas être utilisée avec la contrainte 'unmanaged' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index a8ab3ed4f6fdf..4ffc922323fff 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -1457,11 +1457,6 @@ Un accesso a matrice inline non può includere un identificatore di argomento denominato - - Nested unbound generic type not allowed in 'nameof' operator - Nested unbound generic type not allowed in 'nameof' operator - - The 'new()' constraint cannot be used with the 'unmanaged' constraint Non è possibile usare il vincolo 'new()' con il vincolo 'unmanaged' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 383f0a71d9d03..ce7577b0eff94 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -1457,11 +1457,6 @@ インライン配列のアクセスには名前付き引数の指定子を指定できません - - Nested unbound generic type not allowed in 'nameof' operator - Nested unbound generic type not allowed in 'nameof' operator - - The 'new()' constraint cannot be used with the 'unmanaged' constraint new()' 制約は 'unmanaged' 制約と一緒には使用できません diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index e8efe2aab4a81..2b18ff361afec 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -1457,11 +1457,6 @@ 인라인 배열 액세스에는 명명된 인수 지정자가 없을 수 있습니다. - - Nested unbound generic type not allowed in 'nameof' operator - Nested unbound generic type not allowed in 'nameof' operator - - The 'new()' constraint cannot be used with the 'unmanaged' constraint new()' 제약 조건은 'unmanaged' 제약 조건과 함께 사용할 수 없습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index f6d441038d1ee..2db0850452569 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -1457,11 +1457,6 @@ Dostęp do tablicy śródwierszowej nie może mieć specyfikatora argumentu nazwanego - - Nested unbound generic type not allowed in 'nameof' operator - Nested unbound generic type not allowed in 'nameof' operator - - The 'new()' constraint cannot be used with the 'unmanaged' constraint Ograniczenie „new()” nie może być używane z ograniczeniem „unmanaged” diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 9aa9dbaf780a2..eb945e5c7dece 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -1457,11 +1457,6 @@ Um acesso à matriz não pode ter um especificador de argumento nomeado - - Nested unbound generic type not allowed in 'nameof' operator - Nested unbound generic type not allowed in 'nameof' operator - - The 'new()' constraint cannot be used with the 'unmanaged' constraint A restrição 'new()' não pode ser usada com a restrição 'unmanaged' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index abcb4ac722ddc..7575c0f6172ac 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -1457,11 +1457,6 @@ Доступ к встроенному массиву может не иметь спецификатора именованного аргумента. - - Nested unbound generic type not allowed in 'nameof' operator - Nested unbound generic type not allowed in 'nameof' operator - - The 'new()' constraint cannot be used with the 'unmanaged' constraint Ограничение "new()" невозможно использовать вместе с ограничением "unmanaged" diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 108d9cc367851..91fded227f750 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -1457,11 +1457,6 @@ Satır içi dizi erişiminin adlandırılmış bağımsız değişken belirticisi olamaz - - Nested unbound generic type not allowed in 'nameof' operator - Nested unbound generic type not allowed in 'nameof' operator - - The 'new()' constraint cannot be used with the 'unmanaged' constraint 'new()' kısıtlaması, 'unmanaged' kısıtlamasıyla kullanılamaz diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index f69f68c1732a7..8612f36308be7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -1457,11 +1457,6 @@ 内联数组访问可能没有命名参数说明符 - - Nested unbound generic type not allowed in 'nameof' operator - Nested unbound generic type not allowed in 'nameof' operator - - The 'new()' constraint cannot be used with the 'unmanaged' constraint "new()" 约束不能与 "unmanaged" 约束一起使用 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 42d250ed421dd..2f63ff0cdce65 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -1457,11 +1457,6 @@ 內嵌陣列存取不能有具名引數指定名稱 - - Nested unbound generic type not allowed in 'nameof' operator - Nested unbound generic type not allowed in 'nameof' operator - - The 'new()' constraint cannot be used with the 'unmanaged' constraint new()' 條件約束不能和 'unmanaged' 條件約束一起使用 diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index c93d5d859ebb5..2c77708276b69 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -2566,9 +2566,9 @@ public void OpenTypeInNameof_NoNestedOpenTypes() var v = nameof(List>); Console.WriteLine(v); """).VerifyDiagnostics( - // (4,21): error CS9270: Nested unbound generic type not allowed in 'nameof' operator + // (4,21): error CS7003: Unexpected use of an unbound generic name // var v = nameof(List>); - Diagnostic(ErrorCode.ERR_NestedUnboundTypeNotAllowedInNameofExpression, "List<>").WithLocation(4, 21)); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(4, 21)); } [Fact] From f006bbb3f3daea0c7e44aec4aaa9ae4e9ac0a1d0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 14:15:01 -0800 Subject: [PATCH 369/508] Update src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs --- src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index b1dfcab8fc30d..fcf522526898d 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -2221,7 +2221,6 @@ private BoundExpression BindNameofOperatorInternal(InvocationExpressionSyntax no { CheckFeatureAvailability(node, MessageID.IDS_FeatureNameof, diagnostics); var argument = node.ArgumentList.Arguments[0].Expression; - var boundArgument = BindExpression(argument, diagnostics); bool syntaxIsOk = CheckSyntaxForNameofArgument(argument, out string name, boundArgument.HasAnyErrors ? BindingDiagnosticBag.Discarded : diagnostics); From 74a3fe0959c169c24fca2615b8c56fef03d10151 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 14:16:40 -0800 Subject: [PATCH 370/508] Update src/Compilers/CSharp/Portable/Binder/TypeofBinder.cs --- src/Compilers/CSharp/Portable/Binder/TypeofBinder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Binder/TypeofBinder.cs b/src/Compilers/CSharp/Portable/Binder/TypeofBinder.cs index 6ba2056754f2b..66e1192f235fe 100644 --- a/src/Compilers/CSharp/Portable/Binder/TypeofBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/TypeofBinder.cs @@ -20,7 +20,7 @@ namespace Microsoft.CodeAnalysis.CSharp /// should be considered open so that the flag can be set /// appropriately in BoundTypeOfOperator. ///
- internal sealed partial class TypeofBinder : Binder + internal sealed class TypeofBinder : Binder { private readonly Dictionary _allowedMap; From a671848d47923b78edeab0387a1a2cae25596e52 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 14:21:15 -0800 Subject: [PATCH 371/508] Add tests --- .../Test/Semantic/Semantics/NameOfTests.cs | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index 2c77708276b69..34307cd7a70fa 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -2557,7 +2557,7 @@ public void OpenTypeInNameof_IncorrectTypeArgumentCount2() } [Fact] - public void OpenTypeInNameof_NoNestedOpenTypes() + public void OpenTypeInNameof_NoNestedOpenTypes1() { CreateCompilation(""" using System; @@ -2571,6 +2571,45 @@ public void OpenTypeInNameof_NoNestedOpenTypes() Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(4, 21)); } + [Fact] + public void OpenTypeInNameof_NoNestedOpenTypes2() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List[]>); + Console.WriteLine(v); + """).VerifyDiagnostics( + // (4,21): error CS7003: Unexpected use of an unbound generic name + // var v = nameof(List>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(4, 21)); + } + + [Fact] + public void Nameof_NestedClosedType1() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List>); + Console.WriteLine(v); + """).VerifyDiagnostics(); + } + + [Fact] + public void Nameof_NestedClosedType2() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List>); + Console.WriteLine(v); + """).VerifyDiagnostics(); + } + [Fact] public void OpenTypeInNameof_NoPartialOpenTypes_1() { From ae6727f85d2b66bbf6a84303870d76c60d171e5f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 14:24:06 -0800 Subject: [PATCH 372/508] Add tests --- .../Test/Semantic/Semantics/NameOfTests.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index 34307cd7a70fa..a7695da9cd697 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -2586,6 +2586,40 @@ public void OpenTypeInNameof_NoNestedOpenTypes2() Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(4, 21)); } + [Fact] + public void OpenTypeInNameof_NoNestedOpenTypes3() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List.Inner>); + Console.WriteLine(v); + + public class Outer { public class Inner { } } + """).VerifyDiagnostics( + // (4,21): error CS7003: Unexpected use of an unbound generic name + // var v = nameof(List.Inner>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "Outer<>").WithLocation(4, 21)); + } + + [Fact] + public void OpenTypeInNameof_NoNestedOpenTypes4() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List>); + Console.WriteLine(v); + + public class Outer { public class Inner { } } + """).VerifyDiagnostics( + // (4,27): error CS7003: Unexpected use of an unbound generic name + // var v = nameof(List>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "Inner<>").WithLocation(4, 27)); + } + [Fact] public void Nameof_NestedClosedType1() { From 7a6328a59652cf0ed6aee2ee9586560ce1d145a5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 14:38:43 -0800 Subject: [PATCH 373/508] Simplify --- src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index 7aebd527acc06..576c0012a969a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -1377,23 +1377,20 @@ private NamedTypeSymbol ConstructNamedTypeUnlessTypeArgumentOmitted(SyntaxNode t { // Inside a nameof an open-generic type is acceptable. Fall through and bind the remainder accordingly. CheckFeatureAvailability(typeSyntax, MessageID.IDS_FeatureUnboundGenericTypesInNameof, diagnostics); - typeArguments = UnboundArgumentErrorTypeSymbol.CreateTypeArguments( - type.TypeParameters, - type.TypeParameters.Length, - errorInfo: null); } else { // Note: lookup won't have reported this, since the arity was correct. // CONSIDER: the text of this error message makes sense, but we might want to add a separate code. - Error(diagnostics, ErrorCode.ERR_BadArity, typeSyntax, type, MessageID.IDS_SK_TYPE.Localize(), typeArgumentsSyntax.Count); // If the syntax looks like an unbound generic type, then they probably wanted the definition. // Give an error indicating that the syntax is incorrect and then use the definition. // CONSIDER: we could construct an unbound generic type symbol, but that would probably be confusing // outside a typeof. - return type; + Error(diagnostics, ErrorCode.ERR_BadArity, typeSyntax, type, MessageID.IDS_SK_TYPE.Localize(), typeArgumentsSyntax.Count); } + + return type; } // we pass an empty basesBeingResolved here because this invocation is not on any possible path of From cf0df29f0dc4f819cb857f0dfae0360af844a47d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 14:43:08 -0800 Subject: [PATCH 374/508] Add semantic model test --- .../Test/Semantic/Semantics/NameOfTests.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index a7695da9cd697..2c8f404a0b525 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -2770,5 +2770,37 @@ interface IGoo where T : Base } """, expectedOutput: "Z").VerifyDiagnostics(); } + + [Fact] + public void OpenTypeInNameof_SemanticModelTests() + { + var compilation = CreateCompilation(""" + using System; + + var v1 = nameof(IGoo<>); + var v2 = typeof(IGoo<>); + Console.WriteLine(v1 + v2); + + interface IGoo { } + """).VerifyDiagnostics(); + var tree = compilation.SyntaxTrees.Single(); + var semanticModel = compilation.GetSemanticModel(tree); + + var root = tree.GetRoot(); + + var firstGeneric = root.DescendantNodes().OfType().First(); + var lastGeneric = root.DescendantNodes().OfType().Last(); + + Assert.NotSame(firstGeneric, lastGeneric); + + // Ensure the type inside the nameof is the same as the type inside the typeof. + var type1 = semanticModel.GetTypeInfo(firstGeneric).Type; + var type2 = semanticModel.GetTypeInfo(firstGeneric).Type; + + Assert.NotNull(type1); + Assert.NotNull(type2); + + Assert.Equal(type1, type2); + } } } From 5af3f7ad20a3ec2de62d72f5b08753d8c8f0ddbe Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 14:45:22 -0800 Subject: [PATCH 375/508] Update test --- .../CSharp/Test/Semantic/Semantics/NameOfTests.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index 2c8f404a0b525..6dbcfe735f6bb 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -2771,17 +2771,19 @@ interface IGoo where T : Base """, expectedOutput: "Z").VerifyDiagnostics(); } - [Fact] - public void OpenTypeInNameof_SemanticModelTests() + [Theory] + [InlineData("IGoo<>")] + [InlineData("IGoo<>.Count")] + public void OpenTypeInNameof_SemanticModelTest1(string nameofType) { - var compilation = CreateCompilation(""" + var compilation = CreateCompilation($$""" using System; - var v1 = nameof(IGoo<>); + var v1 = nameof({{nameofType}}); var v2 = typeof(IGoo<>); Console.WriteLine(v1 + v2); - interface IGoo { } + interface IGoo { public T Count { get; } } """).VerifyDiagnostics(); var tree = compilation.SyntaxTrees.Single(); var semanticModel = compilation.GetSemanticModel(tree); From 48688a53b72e5a7e4cc800f771d71f989368f0c1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 14:58:05 -0800 Subject: [PATCH 376/508] Add testS --- .../Test/Semantic/Semantics/NameOfTests.cs | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index 6dbcfe735f6bb..b9f520ed140d0 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -2771,6 +2771,60 @@ interface IGoo where T : Base """, expectedOutput: "Z").VerifyDiagnostics(); } + [Fact] + public void OpenTypeInNameof_GenericMethod1() + { + CompileAndVerify(""" + using System; + + var v = nameof(IGoo.M); + Console.WriteLine(v); + + interface IGoo + { + void M(); + } + """, expectedOutput: "M").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_GenericMethod2() + { + CreateCompilation(""" + using System; + + var v = nameof(IGoo.M<>); + Console.WriteLine(v); + + interface IGoo + { + void M(); + } + """).VerifyDiagnostics( + // (3,16): error CS0305: Using the generic method group 'M' requires 1 type arguments + // var v = nameof(IGoo.M<>); + Diagnostic(ErrorCode.ERR_BadArity, "IGoo.M<>").WithArguments("M", "method group", "1").WithLocation(3, 16)); + } + + [Fact] + public void OpenTypeInNameof_GenericMethod3() + { + CreateCompilation(""" + using System; + + var v = nameof(IGoo.M); + Console.WriteLine(v); + + interface IGoo + { + void M(); + } + """).VerifyDiagnostics( + // (3,16): error CS8084: Type parameters are not allowed on a method group as an argument to 'nameof'. + // var v = nameof(IGoo.M); + Diagnostic(ErrorCode.ERR_NameofMethodGroupWithTypeParameters, "IGoo.M").WithLocation(3, 16)); + } + [Theory] [InlineData("IGoo<>")] [InlineData("IGoo<>.Count")] From 979f225909b396143784e31f00e9078c0b2db8c0 Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Thu, 14 Nov 2024 15:21:23 -0800 Subject: [PATCH 377/508] Add MetadataAsSource test for GoToTypeDefinitionHandler --- .../Definitions/GoToTypeDefinitionTests.cs | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/LanguageServer/ProtocolUnitTests/Definitions/GoToTypeDefinitionTests.cs b/src/LanguageServer/ProtocolUnitTests/Definitions/GoToTypeDefinitionTests.cs index f595711b57f6c..a18b449d3cd47 100644 --- a/src/LanguageServer/ProtocolUnitTests/Definitions/GoToTypeDefinitionTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Definitions/GoToTypeDefinitionTests.cs @@ -4,7 +4,7 @@ #nullable disable -using System.Collections.Immutable; +using System; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -219,6 +219,33 @@ class B Assert.Equal(SourceGeneratedDocumentUri.Scheme, result.Uri.Scheme); } + [Theory, CombinatorialData] + public async Task TestGotoTypeDefinitionAsync_MetadataAsSource(bool mutatingLspWorkspace) + { + var source = + """ + using System; + class A + { + void Rethrow(NotImplementedException exception) + { + throw {|caret:exception|}; + } + } + """; + + // Create a server with LSP misc file workspace and metadata service. + await using var testLspServer = await CreateTestLspServerAsync(source, mutatingLspWorkspace, new InitializationOptions { ServerKind = WellKnownLspServerKinds.CSharpVisualBasicLspServer }); + + // Get the metadata definition. + var results = await RunGotoTypeDefinitionAsync(testLspServer, testLspServer.GetLocations("caret").Single()); + + // Open the metadata file and verify it gets added to the metadata workspace. + await testLspServer.OpenDocumentAsync(results.Single().Uri, text: string.Empty).ConfigureAwait(false); + + Assert.Equal(WorkspaceKind.MetadataAsSource, (await GetWorkspaceForDocument(testLspServer, results.Single().Uri)).Kind); + } + [Theory, CombinatorialData] public async Task TestGotoTypeDefinitionAsync_CrossLanguage(bool mutatingLspWorkspace) { @@ -251,5 +278,11 @@ End Class return await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentTypeDefinitionName, CreateTextDocumentPositionParams(caret), CancellationToken.None); } + + private static async Task GetWorkspaceForDocument(TestLspServer testLspServer, Uri fileUri) + { + var (lspWorkspace, _, _) = await testLspServer.GetManager().GetLspDocumentInfoAsync(new LSP.TextDocumentIdentifier { Uri = fileUri }, CancellationToken.None); + return lspWorkspace!; + } } } From d0b7fa551d08e8b0beed4fd40bd4a6d9bb7a550d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 16:08:43 -0800 Subject: [PATCH 378/508] Update test --- src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index b9f520ed140d0..13ffdbd2e5a6a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -2851,12 +2851,12 @@ interface IGoo { public T Count { get; } } // Ensure the type inside the nameof is the same as the type inside the typeof. var type1 = semanticModel.GetTypeInfo(firstGeneric).Type; - var type2 = semanticModel.GetTypeInfo(firstGeneric).Type; + var type2 = semanticModel.GetTypeInfo(lastGeneric).Type; Assert.NotNull(type1); Assert.NotNull(type2); - Assert.Equal(type1, type2); + Assert.NotEqual(type1, type2); } } } From 13e7eba07d3643a15bcbbc74a347bcd570beb233 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 16:10:07 -0800 Subject: [PATCH 379/508] Update test --- src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index 13ffdbd2e5a6a..86120193c10e2 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -2639,7 +2639,7 @@ public void Nameof_NestedClosedType2() using System; using System.Collections.Generic; - var v = nameof(List>); + var v = nameof(List[]>); Console.WriteLine(v); """).VerifyDiagnostics(); } From 1e6366f565c60b6cf6b87254c8a9d67a0a4a5ebf Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 17:19:44 -0800 Subject: [PATCH 380/508] Add docs and tests --- .../CSharp/Portable/Binder/Binder_Symbols.cs | 9 +++++++++ .../Test/Semantic/Semantics/NameOfTests.cs | 19 ++++++++++++------- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index 576c0012a969a..5b1564b9e09d7 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -1377,6 +1377,15 @@ private NamedTypeSymbol ConstructNamedTypeUnlessTypeArgumentOmitted(SyntaxNode t { // Inside a nameof an open-generic type is acceptable. Fall through and bind the remainder accordingly. CheckFeatureAvailability(typeSyntax, MessageID.IDS_FeatureUnboundGenericTypesInNameof, diagnostics); + + // From the spec: + // + // Member lookup on an unbound type expression will be performed the same way as for a `this` + // expression within that type declaration. + // + // So we want to just return the originating type symbol as is (e.g. List in nameof(List<>)). + // This is distinctly different than how typeof(List<>) works, where it returns an unbound generic + // type. } else { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index 86120193c10e2..7349c25dec38d 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -2828,12 +2828,12 @@ interface IGoo [Theory] [InlineData("IGoo<>")] [InlineData("IGoo<>.Count")] - public void OpenTypeInNameof_SemanticModelTest1(string nameofType) + public void OpenTypeInNameof_SemanticModelTest1(string nameofTypeString) { var compilation = CreateCompilation($$""" using System; - var v1 = nameof({{nameofType}}); + var v1 = nameof({{nameofTypeString}}); var v2 = typeof(IGoo<>); Console.WriteLine(v1 + v2); @@ -2850,13 +2850,18 @@ interface IGoo { public T Count { get; } } Assert.NotSame(firstGeneric, lastGeneric); // Ensure the type inside the nameof is the same as the type inside the typeof. - var type1 = semanticModel.GetTypeInfo(firstGeneric).Type; - var type2 = semanticModel.GetTypeInfo(lastGeneric).Type; + var nameofType = semanticModel.GetTypeInfo(firstGeneric).Type; + var typeofType = semanticModel.GetTypeInfo(lastGeneric).Type; - Assert.NotNull(type1); - Assert.NotNull(type2); + Assert.NotNull(nameofType); + Assert.NotNull(typeofType); - Assert.NotEqual(type1, type2); + Assert.NotEqual(nameofType, typeofType); + + var igooType = compilation.GetTypeByMetadataName("IGoo`1").GetPublicSymbol(); + Assert.NotNull(igooType); + + Assert.Equal(igooType, nameofType); } } } From 739a4ef95a761cd4cf9db96ae505746b449e9ed7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 14 Nov 2024 17:21:06 -0800 Subject: [PATCH 381/508] Update test --- src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index 7349c25dec38d..1024d22990f6c 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -2856,8 +2856,13 @@ interface IGoo { public T Count { get; } } Assert.NotNull(nameofType); Assert.NotNull(typeofType); + // typeof will produce IGoo<>, while nameof will produce IGoo. These are distinctly different types (the + // latter has members for example). Assert.NotEqual(nameofType, typeofType); + Assert.Empty(typeofType.GetMembers("Count")); + Assert.Single(nameofType.GetMembers("Count")); + var igooType = compilation.GetTypeByMetadataName("IGoo`1").GetPublicSymbol(); Assert.NotNull(igooType); From ab8015d21fee599b91216a9a70bd4235befe2d24 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Thu, 14 Nov 2024 17:54:10 -0800 Subject: [PATCH 382/508] Simplify shape of APIs used for Overload Resolution Priority feature (#75891) --- .../SynthesizedStateMachineProperty.cs | 2 +- .../SynthesizedSymbols/AnonymousType.PropertySymbol.cs | 2 +- src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs | 4 ++-- .../CSharp/Portable/Symbols/ErrorPropertySymbol.cs | 2 +- .../FunctionPointers/FunctionPointerMethodSymbol.cs | 4 ++-- .../CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs | 7 ++++--- .../Portable/Symbols/Metadata/PE/PEPropertySymbol.cs | 7 ++++--- src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs | 4 ++-- src/Compilers/CSharp/Portable/Symbols/PropertySymbol.cs | 4 ++-- .../Portable/Symbols/ReducedExtensionMethodSymbol.cs | 2 +- .../CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs | 2 +- .../CSharp/Portable/Symbols/SignatureOnlyPropertySymbol.cs | 2 +- .../Portable/Symbols/Source/SourceDelegateMethodSymbol.cs | 4 ++-- .../Portable/Symbols/Source/SourceDestructorSymbol.cs | 4 ++-- .../Portable/Symbols/Source/SourceEventAccessorSymbol.cs | 4 ++-- .../Symbols/Source/SourceMethodSymbolWithAttributes.cs | 4 ++-- .../Portable/Symbols/Source/SourcePropertySymbolBase.cs | 4 ++-- .../ReadOnlyListType/SynthesizedReadOnlyListProperty.cs | 2 +- .../Synthesized/Records/SynthesizedRecordObjectMethod.cs | 4 ++-- .../Symbols/Synthesized/SynthesizedEntryPointSymbol.cs | 4 ++-- .../Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs | 4 ++-- .../Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs | 4 ++-- .../Synthesized/SynthesizedIntrinsicOperatorSymbol.cs | 4 ++-- .../SynthesizedSimpleProgramEntryPointSymbol.cs | 4 ++-- .../Symbols/Synthesized/SynthesizedStaticConstructor.cs | 4 ++-- .../CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs | 2 +- .../Portable/Symbols/Wrapped/WrappedPropertySymbol.cs | 2 +- .../Attributes/CommonMethodEarlyWellKnownAttributeData.cs | 5 ++--- .../CommonPropertyEarlyWellKnownAttributeData.cs | 5 ++--- .../Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs | 2 +- .../ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs | 2 +- 31 files changed, 55 insertions(+), 55 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/SynthesizedStateMachineProperty.cs b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/SynthesizedStateMachineProperty.cs index cae0ccd0b2648..8d02f62939390 100644 --- a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/SynthesizedStateMachineProperty.cs +++ b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/SynthesizedStateMachineProperty.cs @@ -160,7 +160,7 @@ internal override ObsoleteAttributeData ObsoleteAttributeData get { return null; } } - internal override int? TryGetOverloadResolutionPriority() => null; + internal override int TryGetOverloadResolutionPriority() => 0; bool ISynthesizedMethodBodyImplementationSymbol.HasMethodBodyDependency { diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.PropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.PropertySymbol.cs index 14b838693648f..0caa26f8424dc 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.PropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.PropertySymbol.cs @@ -201,7 +201,7 @@ public FieldSymbol BackingField get { return _backingField; } } - internal override int? TryGetOverloadResolutionPriority() => null; + internal override int TryGetOverloadResolutionPriority() => 0; public override bool Equals(Symbol obj, TypeCompareKind compareKind) { diff --git a/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs index a18307dfcc0cc..ac17af6693ab3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs @@ -295,9 +295,9 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol buil return false; } - internal sealed override int? TryGetOverloadResolutionPriority() + internal sealed override int TryGetOverloadResolutionPriority() { - return null; + return 0; } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/ErrorPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ErrorPropertySymbol.cs index 136f99adf5013..3662cefe70bbe 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ErrorPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ErrorPropertySymbol.cs @@ -93,6 +93,6 @@ public ErrorPropertySymbol(Symbol containingSymbol, TypeSymbol type, string name public override ImmutableArray RefCustomModifiers { get { return ImmutableArray.Empty; } } - internal override int? TryGetOverloadResolutionPriority() => null; + internal override int TryGetOverloadResolutionPriority() => 0; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs index 760b4fe8674e9..f0ed88d0c3f4c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs @@ -866,9 +866,9 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol? bui return false; } - internal sealed override int? TryGetOverloadResolutionPriority() + internal sealed override int TryGetOverloadResolutionPriority() { - return null; + return 0; } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs index c530ef5ba6398..9629eda149760 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs @@ -1702,11 +1702,12 @@ internal override bool HasAsyncMethodBuilderAttribute(out TypeSymbol builderArgu return builderArgument is not null; } - internal override int? TryGetOverloadResolutionPriority() + internal override int TryGetOverloadResolutionPriority() { if (!_packedFlags.IsOverloadResolutionPriorityPopulated) { - if (_containingType.ContainingPEModule.Module.TryGetOverloadResolutionPriorityValue(_handle, out int priority)) + if (_containingType.ContainingPEModule.Module.TryGetOverloadResolutionPriorityValue(_handle, out int priority) && + priority != 0) { Interlocked.CompareExchange(ref AccessUncommonFields()._lazyOverloadResolutionPriority, priority, 0); } @@ -1721,7 +1722,7 @@ internal override bool HasAsyncMethodBuilderAttribute(out TypeSymbol builderArgu _packedFlags.SetIsOverloadResolutionPriorityPopulated(); } - return _uncommonFields?._lazyOverloadResolutionPriority; + return _uncommonFields?._lazyOverloadResolutionPriority ?? 0; } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEPropertySymbol.cs index a5f273713f236..0956462733b10 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEPropertySymbol.cs @@ -1043,12 +1043,13 @@ internal override bool HasRuntimeSpecialName get { return null; } } - internal override int? TryGetOverloadResolutionPriority() + internal override int TryGetOverloadResolutionPriority() { Debug.Assert(IsIndexer || IsIndexedProperty); if (!_flags.IsOverloadResolutionPriorityPopulated) { - if (_containingType.ContainingPEModule.Module.TryGetOverloadResolutionPriorityValue(_handle, out int priority)) + if (_containingType.ContainingPEModule.Module.TryGetOverloadResolutionPriorityValue(_handle, out int priority) && + priority != 0) { Interlocked.CompareExchange(ref AccessUncommonFields()._lazyOverloadResolutionPriority, priority, 0); } @@ -1063,7 +1064,7 @@ internal override bool HasRuntimeSpecialName _flags.SetOverloadResolutionPriorityPopulated(); } - return _uncommonFields?._lazyOverloadResolutionPriority; + return _uncommonFields?._lazyOverloadResolutionPriority ?? 0; } private sealed class PEPropertySymbolWithCustomModifiers : PEPropertySymbol diff --git a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs index e1fcf1786b6cc..ac078acaa6101 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs @@ -1192,9 +1192,9 @@ internal virtual void AddSynthesizedReturnTypeAttributes(PEModuleBuilder moduleB /// /// Do not call this method from early attribute binding, cycles will occur. /// - internal int OverloadResolutionPriority => CanHaveOverloadResolutionPriority ? (TryGetOverloadResolutionPriority() ?? 0) : 0; + internal int OverloadResolutionPriority => CanHaveOverloadResolutionPriority ? TryGetOverloadResolutionPriority() : 0; - internal abstract int? TryGetOverloadResolutionPriority(); + internal abstract int TryGetOverloadResolutionPriority(); internal bool CanHaveOverloadResolutionPriority => MethodKind is MethodKind.Ordinary diff --git a/src/Compilers/CSharp/Portable/Symbols/PropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PropertySymbol.cs index 329beb975e807..40946df3c5950 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PropertySymbol.cs @@ -350,11 +350,11 @@ internal int OverloadResolutionPriority return 0; } - return TryGetOverloadResolutionPriority() ?? 0; + return TryGetOverloadResolutionPriority(); } } - internal abstract int? TryGetOverloadResolutionPriority(); + internal abstract int TryGetOverloadResolutionPriority(); internal bool CanHaveOverloadResolutionPriority => !IsOverride && !IsExplicitInterfaceImplementation && (IsIndexer || IsIndexedProperty); diff --git a/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs index 55f514fae9519..5bdf91a8c8508 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs @@ -608,7 +608,7 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol buil #nullable enable - internal override int? TryGetOverloadResolutionPriority() + internal override int TryGetOverloadResolutionPriority() { return _reducedFrom.TryGetOverloadResolutionPriority(); } diff --git a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs index c71b5cbcb5bc8..7afa024b16661 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs @@ -184,7 +184,7 @@ internal override bool IsMetadataFinal internal sealed override bool UseUpdatedEscapeRules => true; - internal sealed override int? TryGetOverloadResolutionPriority() => throw ExceptionUtilities.Unreachable(); + internal sealed override int TryGetOverloadResolutionPriority() => throw ExceptionUtilities.Unreachable(); #endregion } diff --git a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyPropertySymbol.cs index d2e426f22923f..1ec96ab09bb70 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyPropertySymbol.cs @@ -103,7 +103,7 @@ public SignatureOnlyPropertySymbol( public override bool IsIndexer { get { throw ExceptionUtilities.Unreachable(); } } - internal override int? TryGetOverloadResolutionPriority() => throw ExceptionUtilities.Unreachable(); + internal override int TryGetOverloadResolutionPriority() => throw ExceptionUtilities.Unreachable(); #endregion Not used by PropertySignatureComparer } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs index 90af0833df5fe..4bdec7c9aec71 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs @@ -201,9 +201,9 @@ internal sealed override System.AttributeTargets GetAttributeTarget() return System.AttributeTargets.Delegate; } - internal sealed override int? TryGetOverloadResolutionPriority() + internal sealed override int TryGetOverloadResolutionPriority() { - return null; + return 0; } private sealed class Constructor : SourceDelegateMethodSymbol diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceDestructorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceDestructorSymbol.cs index bebafb570db77..5e6846f3883b4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceDestructorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceDestructorSymbol.cs @@ -177,9 +177,9 @@ internal override bool GenerateDebugInfo get { return true; } } - internal sealed override int? TryGetOverloadResolutionPriority() + internal sealed override int TryGetOverloadResolutionPriority() { - return null; + return 0; } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs index bde10d904f2eb..7a3aa3b89b479 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs @@ -229,9 +229,9 @@ protected string GetOverriddenAccessorName(SourceEventSymbol @event, bool isAdde return null; } - internal sealed override int? TryGetOverloadResolutionPriority() + internal sealed override int TryGetOverloadResolutionPriority() { - return null; + return 0; } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs index bb5183e3e9d75..691df206c1abb 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs @@ -1744,7 +1744,7 @@ internal override System.Reflection.MethodImplAttributes ImplementationAttribute } } - internal override int? TryGetOverloadResolutionPriority() - => GetEarlyDecodedWellKnownAttributeData()?.OverloadResolutionPriority; + internal override int TryGetOverloadResolutionPriority() + => GetEarlyDecodedWellKnownAttributeData()?.OverloadResolutionPriority ?? 0; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs index 2b611cea14e60..197f65d0fcc57 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs @@ -1701,10 +1701,10 @@ private void ValidateIndexerNameAttribute(CSharpAttributeData attribute, Attribu } } - internal sealed override int? TryGetOverloadResolutionPriority() + internal sealed override int TryGetOverloadResolutionPriority() { Debug.Assert(this.IsIndexer); - return GetEarlyDecodedWellKnownAttributeData()?.OverloadResolutionPriority; + return GetEarlyDecodedWellKnownAttributeData()?.OverloadResolutionPriority ?? 0; } #endregion diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListProperty.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListProperty.cs index afbe2a2c285c5..406c84d246d21 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListProperty.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListProperty.cs @@ -78,6 +78,6 @@ internal SynthesizedReadOnlyListProperty( internal override ObsoleteAttributeData? ObsoleteAttributeData => null; - internal override int? TryGetOverloadResolutionPriority() => null; + internal override int TryGetOverloadResolutionPriority() => 0; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordObjectMethod.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordObjectMethod.cs index b3bb0c75a8269..f5872cbed3094 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordObjectMethod.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordObjectMethod.cs @@ -57,9 +57,9 @@ internal static bool VerifyOverridesMethodFromObject(MethodSymbol overriding, Sp return reportAnError; } - internal sealed override int? TryGetOverloadResolutionPriority() + internal sealed override int TryGetOverloadResolutionPriority() { - return null; + return 0; } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs index dc3baa3e834d8..62f5a6a4969a3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs @@ -320,9 +320,9 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol buil return false; } - internal override int? TryGetOverloadResolutionPriority() + internal override int TryGetOverloadResolutionPriority() { - return null; + return 0; } /// A synthesized entrypoint that forwards all calls to an async Main Method diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs index 79de6ffeedf69..811658bfbe4f5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs @@ -362,9 +362,9 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol buil return false; } - internal sealed override int? TryGetOverloadResolutionPriority() + internal sealed override int TryGetOverloadResolutionPriority() { - return null; + return 0; } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs index f8587cdaa0746..a3f190bfafa9d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs @@ -89,9 +89,9 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol buil return false; } - internal sealed override int? TryGetOverloadResolutionPriority() + internal sealed override int TryGetOverloadResolutionPriority() { - return null; + return 0; } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs index a596adb68da1f..5ab93f4506691 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs @@ -514,9 +514,9 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol buil return false; } - internal override int? TryGetOverloadResolutionPriority() + internal override int TryGetOverloadResolutionPriority() { - return null; + return 0; } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs index bcd7cbafad6e0..971dea49f7b2d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs @@ -292,9 +292,9 @@ private static bool IsNullableAnalysisEnabled(CSharpCompilation compilation, Com return false; } - internal sealed override int? TryGetOverloadResolutionPriority() + internal sealed override int TryGetOverloadResolutionPriority() { - return null; + return 0; } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs index e7e49a20ee75c..df7e6416e5419 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs @@ -441,9 +441,9 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol? bui return false; } - internal sealed override int? TryGetOverloadResolutionPriority() + internal sealed override int TryGetOverloadResolutionPriority() { - return null; + return 0; } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs index 0dcfcb6163033..2087167575af1 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs @@ -365,7 +365,7 @@ internal override bool GenerateDebugInfo internal sealed override bool UseUpdatedEscapeRules => UnderlyingMethod.UseUpdatedEscapeRules; - internal sealed override int? TryGetOverloadResolutionPriority() + internal sealed override int TryGetOverloadResolutionPriority() { return UnderlyingMethod.TryGetOverloadResolutionPriority(); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedPropertySymbol.cs index 6f8c67a69891f..9cbab5ca03d21 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedPropertySymbol.cs @@ -189,6 +189,6 @@ internal override bool HasRuntimeSpecialName } } - internal override int? TryGetOverloadResolutionPriority() => _underlyingProperty.OverloadResolutionPriority; + internal override int TryGetOverloadResolutionPriority() => _underlyingProperty.OverloadResolutionPriority; } } diff --git a/src/Compilers/Core/Portable/Symbols/Attributes/CommonMethodEarlyWellKnownAttributeData.cs b/src/Compilers/Core/Portable/Symbols/Attributes/CommonMethodEarlyWellKnownAttributeData.cs index eaf500643a99b..2cf7763f5dccd 100644 --- a/src/Compilers/Core/Portable/Symbols/Attributes/CommonMethodEarlyWellKnownAttributeData.cs +++ b/src/Compilers/Core/Portable/Symbols/Attributes/CommonMethodEarlyWellKnownAttributeData.cs @@ -76,9 +76,9 @@ public bool HasSetsRequiredMembersAttribute #endregion #region OverloadResolutionPriorityAttribute - private int? _overloadResolutionPriority = null; + private int _overloadResolutionPriority = 0; [DisallowNull] - public int? OverloadResolutionPriority + public int OverloadResolutionPriority { get { @@ -88,7 +88,6 @@ public int? OverloadResolutionPriority set { VerifySealed(expected: false); - Debug.Assert(value != null); _overloadResolutionPriority = value; SetDataStored(); } diff --git a/src/Compilers/Core/Portable/Symbols/Attributes/CommonPropertyEarlyWellKnownAttributeData.cs b/src/Compilers/Core/Portable/Symbols/Attributes/CommonPropertyEarlyWellKnownAttributeData.cs index 5582c14667ef9..f4f2036cf4a75 100644 --- a/src/Compilers/Core/Portable/Symbols/Attributes/CommonPropertyEarlyWellKnownAttributeData.cs +++ b/src/Compilers/Core/Portable/Symbols/Attributes/CommonPropertyEarlyWellKnownAttributeData.cs @@ -38,9 +38,9 @@ public ObsoleteAttributeData? ObsoleteAttributeData #endregion #region OverloadResolutionPriorityAttribute - private int? _overloadResolutionPriority = null; + private int _overloadResolutionPriority = 0; [DisallowNull] - public int? OverloadResolutionPriority + public int OverloadResolutionPriority { get { @@ -50,7 +50,6 @@ public int? OverloadResolutionPriority set { VerifySealed(expected: false); - Debug.Assert(value != null); _overloadResolutionPriority = value; SetDataStored(); } diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs index e126c2e2e5096..5b50a64384805 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs @@ -765,6 +765,6 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol buil return false; } - internal override int? TryGetOverloadResolutionPriority() => null; + internal override int TryGetOverloadResolutionPriority() => 0; } } diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs index d8a443dfb53be..96aec08e8669b 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs @@ -285,7 +285,7 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol buil return false; } - internal override int? TryGetOverloadResolutionPriority() => null; + internal override int TryGetOverloadResolutionPriority() => 0; #if DEBUG protected override MethodSymbolAdapter CreateCciAdapter() From ad68d4b7f7d68109527a908027866d47456288e1 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Thu, 14 Nov 2024 18:00:16 -0800 Subject: [PATCH 383/508] EE: Support compact name in `IDkmLanguageInstructionDecoder.GetMethodName()` implementation (#75764) --- eng/Directory.Packages.props | 4 +- .../CSharpInstructionDecoder.cs | 13 ++ .../InstructionDecoderTests.cs | 162 ++++++++++++++++- .../ExpressionCompiler/InstructionDecoder.cs | 8 + .../LanguageInstructionDecoder.cs | 16 +- .../VisualBasicInstructionDecoder.vb | 6 +- .../InstructionDecoderTests.vb | 163 ++++++++++++++++-- 7 files changed, 346 insertions(+), 26 deletions(-) diff --git a/eng/Directory.Packages.props b/eng/Directory.Packages.props index 962f5a6caca28..1466d9260850b 100644 --- a/eng/Directory.Packages.props +++ b/eng/Directory.Packages.props @@ -122,8 +122,8 @@ VS Debugger --> - - + + - + https://github.com/dotnet/arcade - 3c393bbd85ae16ddddba20d0b75035b0c6f1a52d + 1c7e09a8d9c9c9b15ba574cd6a496553505559de @@ -156,9 +156,9 @@ https://github.com/dotnet/roslyn 5d10d428050c0d6afef30a072c4ae68776621877 - + https://github.com/dotnet/arcade - 3c393bbd85ae16ddddba20d0b75035b0c6f1a52d + 1c7e09a8d9c9c9b15ba574cd6a496553505559de https://github.com/dotnet/roslyn-analyzers diff --git a/eng/common/templates-official/steps/get-delegation-sas.yml b/eng/common/templates-official/steps/get-delegation-sas.yml index 6e368af1ce310..c5a9c1f8275c5 100644 --- a/eng/common/templates-official/steps/get-delegation-sas.yml +++ b/eng/common/templates-official/steps/get-delegation-sas.yml @@ -1,52 +1,7 @@ -parameters: -- name: federatedServiceConnection - type: string -- name: outputVariableName - type: string -- name: expiryInHours - type: number - default: 1 -- name: base64Encode - type: boolean - default: false -- name: storageAccount - type: string -- name: container - type: string -- name: permissions - type: string - default: 'rl' - steps: -- task: AzureCLI@2 - displayName: 'Generate delegation SAS Token for ${{ parameters.storageAccount }}/${{ parameters.container }}' - inputs: - azureSubscription: ${{ parameters.federatedServiceConnection }} - scriptType: 'pscore' - scriptLocation: 'inlineScript' - inlineScript: | - # Calculate the expiration of the SAS token and convert to UTC - $expiry = (Get-Date).AddHours(${{ parameters.expiryInHours }}).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ") - - # Temporarily work around a helix issue where SAS tokens with / in them will cause incorrect downloads - # of correlation payloads. https://github.com/dotnet/dnceng/issues/3484 - $sas = "" - do { - $sas = az storage container generate-sas --account-name ${{ parameters.storageAccount }} --name ${{ parameters.container }} --permissions ${{ parameters.permissions }} --expiry $expiry --auth-mode login --as-user -o tsv - if ($LASTEXITCODE -ne 0) { - Write-Error "Failed to generate SAS token." - exit 1 - } - } while($sas.IndexOf('/') -ne -1) - - if ($LASTEXITCODE -ne 0) { - Write-Error "Failed to generate SAS token." - exit 1 - } - - if ('${{ parameters.base64Encode }}' -eq 'true') { - $sas = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($sas)) - } +- template: /eng/common/core-templates/steps/get-delegation-sas.yml + parameters: + is1ESPipeline: true - Write-Host "Setting '${{ parameters.outputVariableName }}' with the access token value" - Write-Host "##vso[task.setvariable variable=${{ parameters.outputVariableName }};issecret=true]$sas" \ No newline at end of file + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/steps/get-delegation-sas.yml b/eng/common/templates/steps/get-delegation-sas.yml index 6e368af1ce310..83760c9798e36 100644 --- a/eng/common/templates/steps/get-delegation-sas.yml +++ b/eng/common/templates/steps/get-delegation-sas.yml @@ -1,52 +1,7 @@ -parameters: -- name: federatedServiceConnection - type: string -- name: outputVariableName - type: string -- name: expiryInHours - type: number - default: 1 -- name: base64Encode - type: boolean - default: false -- name: storageAccount - type: string -- name: container - type: string -- name: permissions - type: string - default: 'rl' - steps: -- task: AzureCLI@2 - displayName: 'Generate delegation SAS Token for ${{ parameters.storageAccount }}/${{ parameters.container }}' - inputs: - azureSubscription: ${{ parameters.federatedServiceConnection }} - scriptType: 'pscore' - scriptLocation: 'inlineScript' - inlineScript: | - # Calculate the expiration of the SAS token and convert to UTC - $expiry = (Get-Date).AddHours(${{ parameters.expiryInHours }}).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ") - - # Temporarily work around a helix issue where SAS tokens with / in them will cause incorrect downloads - # of correlation payloads. https://github.com/dotnet/dnceng/issues/3484 - $sas = "" - do { - $sas = az storage container generate-sas --account-name ${{ parameters.storageAccount }} --name ${{ parameters.container }} --permissions ${{ parameters.permissions }} --expiry $expiry --auth-mode login --as-user -o tsv - if ($LASTEXITCODE -ne 0) { - Write-Error "Failed to generate SAS token." - exit 1 - } - } while($sas.IndexOf('/') -ne -1) - - if ($LASTEXITCODE -ne 0) { - Write-Error "Failed to generate SAS token." - exit 1 - } - - if ('${{ parameters.base64Encode }}' -eq 'true') { - $sas = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($sas)) - } +- template: /eng/common/core-templates/steps/get-delegation-sas.yml + parameters: + is1ESPipeline: false - Write-Host "Setting '${{ parameters.outputVariableName }}' with the access token value" - Write-Host "##vso[task.setvariable variable=${{ parameters.outputVariableName }};issecret=true]$sas" \ No newline at end of file + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/global.json b/global.json index a7c4604b7c964..d0270ea4b9171 100644 --- a/global.json +++ b/global.json @@ -11,8 +11,8 @@ } }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24516.2", - "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.24516.2", + "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24562.13", + "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.24562.13", "Microsoft.Build.Traversal": "3.4.0" } } From bf1ec778ddaddc6d8db409ba9ba87c9f534e7056 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Fri, 15 Nov 2024 14:17:54 +0100 Subject: [PATCH 385/508] Update .NET 9 runtime for single machine CI job (#75889) * Update .NET 9 runtime for single machine CI job * Use regular pool --- azure-pipelines.yml | 13 ++++++++++++- eng/pipelines/test-windows-job-single-machine.yml | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 39bf2f2c610a1..0f26c4fff3483 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -53,6 +53,12 @@ variables: ${{ else }}: value: windows.vs2022preview.amd64 + - name: Vs2022RegularQueueName + ${{ if eq(variables['System.TeamProject'], 'public') }}: + value: windows.vs2022.amd64.open + ${{ else }}: + value: windows.vs2022.amd64 + - name: UbuntuQueueName ${{ if eq(variables['System.TeamProject'], 'public') }}: value: Build.Ubuntu.2004.Amd64.Open @@ -107,6 +113,11 @@ parameters: default: name: $(PoolName) demands: ImageOverride -equals $(Vs2022PreviewQueueName) + - name: vs2022RegularPool + type: object + default: + name: $(PoolName) + demands: ImageOverride -equals $(Vs2022RegularQueueName) stages: - stage: Windows_Debug_Build @@ -271,7 +282,7 @@ stages: jobName: Test_Windows_CoreClr_Debug_Single_Machine testArtifactName: Transport_Artifacts_Windows_Debug configuration: Debug - poolParameters: ${{ parameters.vs2022PreviewPool }} + poolParameters: ${{ parameters.vs2022RegularPool }} testArguments: -testCoreClr - template: eng/pipelines/test-windows-job.yml diff --git a/eng/pipelines/test-windows-job-single-machine.yml b/eng/pipelines/test-windows-job-single-machine.yml index 75acc4af38da4..975bdb744f2f0 100644 --- a/eng/pipelines/test-windows-job-single-machine.yml +++ b/eng/pipelines/test-windows-job-single-machine.yml @@ -33,7 +33,7 @@ jobs: displayName: 'Install .NET 9 Runtime' inputs: packageType: runtime - version: 9.0.0-preview.3.24172.9 + version: 9.0.0-rc.2.24473.5 includePreviewVersions: true installationPath: '$(Build.SourcesDirectory)/.dotnet' From c267dc9840aba8c551de23d1fdb444eb7a9a5b3b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 15 Nov 2024 13:18:50 -0500 Subject: [PATCH 386/508] Update Language Feature Status.md (#75935) --- docs/Language Feature Status.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Language Feature Status.md b/docs/Language Feature Status.md index 0ebb6d44a313e..c23f966750f1a 100644 --- a/docs/Language Feature Status.md +++ b/docs/Language Feature Status.md @@ -15,6 +15,7 @@ efforts behind them. | [Null-conditional assignment](https://github.com/dotnet/csharplang/issues/6045) | [null-conditional-assignment](https://github.com/dotnet/roslyn/tree/features/null-conditional-assignment) | [In Progress](https://github.com/dotnet/roslyn/issues/75554) | [RikkiGibson](https://github.com/RikkiGibson) | [cston](https://github.com/cston), [jjonescz](https://github.com/jjonescz) | TBD | [RikkiGibson](https://github.com/RikkiGibson) | | [`field` keyword in properties](https://github.com/dotnet/csharplang/issues/140) | [field-keyword](https://github.com/dotnet/roslyn/tree/features/field-keyword) | [Merged into 17.12p3](https://github.com/dotnet/roslyn/issues/57012) | [Youssef1313](https://github.com/Youssef1313), [cston](https://github.com/cston) | [333fred](https://github.com/333fred), [RikkiGibson](https://github.com/RikkiGibson) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | | [First-class Span Types](https://github.com/dotnet/csharplang/issues/7905) | [FirstClassSpan](https://github.com/dotnet/roslyn/tree/features/FirstClassSpan) | [Merged into 17.13p1](https://github.com/dotnet/roslyn/issues/73445) | [jjonescz](https://github.com/jjonescz) | [cston](https://github.com/cston), [333fred](https://github.com/333fred) | | [333fred](https://github.com/333fred), [stephentoub](https://github.com/stephentoub) | +| [Unbound generic types in `nameof`](https://github.com/dotnet/csharplang/issues/8480) | [PR](https://github.com/dotnet/roslyn/pull/75368) | [In Progress](https://github.com/dotnet/roslyn/pull/75368) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | [jcouv](https://github.com/jcouv) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | # Working Set VB From b0aac2dce92ecf6c296405df2d379ae373916bf2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 15 Nov 2024 11:53:02 -0800 Subject: [PATCH 387/508] Clean up --- .../RawStringLiteralCommandHandler_Return.cs | 266 +++++++++--------- 1 file changed, 134 insertions(+), 132 deletions(-) diff --git a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs index a0379988ae245..f7092f52beaaf 100644 --- a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs +++ b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs @@ -51,113 +51,166 @@ public bool ExecuteCommand(ReturnKeyCommandArgs args, CommandExecutionContext co if (position >= currentSnapshot.Length) return false; - if (currentSnapshot[position] == '"') - { - return HandleCaretOnQuote(textView, subjectBuffer, span, position, context.OperationContext.UserCancellationToken); - } - - return HandleCaretNotOnQuote(textView, subjectBuffer, position, context.OperationContext.UserCancellationToken); - } - - private bool HandleCaretNotOnQuote(ITextView textView, ITextBuffer subjectBuffer, int position, CancellationToken cancellationToken) - { - // If the caret is not on a quote, we need to find whether we are within the contents of a single-line raw string literal - // but not inside an interpolation - // If we are inside a raw string literal and the caret is not on top of a quote, it is part of the literal's text - // Here we try to ensure that the literal's closing quotes are properly placed in their own line - // We could reach this point after pressing enter within a single-line raw string + var cancellationToken = context.OperationContext.UserCancellationToken; - var currentSnapshot = subjectBuffer.CurrentSnapshot; var document = currentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); if (document == null) return false; - var parsedDocument = ParsedDocument.CreateSynchronously(document, cancellationToken); + return currentSnapshot[position] == '"' + ? ExecuteReturnCommandBeforeQuoteCharacter() + : ExecuteReturnCommandNotBeforeQuoteCharacter(); - var token = parsedDocument.Root.FindToken(position); - var preferredIndentationToken = token; - switch (token.Kind()) + bool ExecuteReturnCommandBeforeQuoteCharacter() { - case SyntaxKind.SingleLineRawStringLiteralToken: - break; + var quotesBefore = 0; + var quotesAfter = 0; - case SyntaxKind.InterpolatedStringTextToken: - case SyntaxKind.OpenBraceToken: - if (token.Parent?.Parent is InterpolatedStringExpressionSyntax interpolated && - interpolated.StringStartToken.Kind() is SyntaxKind.InterpolatedSingleLineRawStringStartToken) - { - if (token.Kind() is SyntaxKind.OpenBraceToken) - { - // If we are not at the start of the interpolation braces, we do not intend to handle converting the raw string - // into a new one - if (position != token.SpanStart) - { - return false; - } + // Ensure we're in between a balanced set of quotes, with at least 3 quotes on each side. - // We prefer the indentation options of the string start delimiter because the indentation of the interpolation - // is empty and thus we cannot properly indent the lines that we insert - preferredIndentationToken = interpolated.StringStartToken; - } + var currentSnapshot = subjectBuffer.CurrentSnapshot; + for (int i = position, n = currentSnapshot.Length; i < n; i++) + { + if (currentSnapshot[i] != '"') + break; + quotesAfter++; + } + + for (var i = position - 1; i >= 0; i--) + { + if (currentSnapshot[i] != '"') break; - } + quotesBefore++; + } + + var isEmpty = quotesBefore > 0; + if (isEmpty && quotesAfter != quotesBefore) return false; - default: + if (quotesAfter < 3) return false; - } - return MakeEdit(textView, subjectBuffer, position, document.Project, parsedDocument, token, preferredIndentationToken, isEmpty: false, cancellationToken); - } + var parsedDocument = ParsedDocument.CreateSynchronously(document, cancellationToken); - private bool MakeEdit( - ITextView textView, ITextBuffer subjectBuffer, int position, Project project, - ParsedDocument parsedDocument, SyntaxToken token, SyntaxToken preferredIndentationToken, - bool isEmpty, CancellationToken cancellationToken) - { - var indentationOptions = subjectBuffer.GetIndentationOptions(_editorOptionsService, project.GetFallbackAnalyzerOptions(), project.Services, explicitFormat: false); - var indentation = preferredIndentationToken.GetPreferredIndentation(parsedDocument, indentationOptions, cancellationToken); + var token = parsedDocument.Root.FindToken(position); + if (token.Kind() is not (SyntaxKind.SingleLineRawStringLiteralToken or + SyntaxKind.MultiLineRawStringLiteralToken or + SyntaxKind.InterpolatedSingleLineRawStringStartToken or + SyntaxKind.InterpolatedMultiLineRawStringStartToken)) + { + return false; + } - var newLine = indentationOptions.FormattingOptions.NewLine; - using var transaction = CaretPreservingEditTransaction.TryCreate( - CSharpEditorResources.Split_raw_string, textView, _undoHistoryRegistry, _editorOperationsFactoryService); - var edit = subjectBuffer.CreateEdit(); + return MakeEdit(parsedDocument, token, preferredIndentationToken: token, isEmpty); + } - var openingEnd = GetEndPositionOfOpeningDelimiter(token, isEmpty); - var insertedLines = 1; - if (isEmpty) + bool ExecuteReturnCommandNotBeforeQuoteCharacter() { - // If the literal is empty, we just want to help the user transform it into a multiline raw string literal - // with the extra empty newline between the delimiters to place the caret at - edit.Insert(position, newLine + newLine + indentation); + // If the caret is not on a quote, we need to find whether we are within the contents of a single-line raw + // string literal but not inside an interpolation. If we are inside a raw string literal and the caret is not on + // top of a quote, it is part of the literal's text. Here we try to ensure that the literal's closing quotes are + // properly placed in their own line We could reach this point after pressing enter within a single-line raw + // string + + var parsedDocument = ParsedDocument.CreateSynchronously(document, cancellationToken); + + var token = parsedDocument.Root.FindToken(position); + var preferredIndentationToken = token; + switch (token.Kind()) + { + case SyntaxKind.SingleLineRawStringLiteralToken: + break; + + case SyntaxKind.InterpolatedStringTextToken: + case SyntaxKind.OpenBraceToken: + if (token.Parent?.Parent is InterpolatedStringExpressionSyntax interpolated && + interpolated.StringStartToken.Kind() is SyntaxKind.InterpolatedSingleLineRawStringStartToken) + { + if (token.Kind() is SyntaxKind.OpenBraceToken) + { + // If we are not at the start of the interpolation braces, we do not intend to handle converting the raw string + // into a new one + if (position != token.SpanStart) + return false; + + // We prefer the indentation options of the string start delimiter because the indentation of the interpolation + // is empty and thus we cannot properly indent the lines that we insert + preferredIndentationToken = interpolated.StringStartToken; + } + + break; + } + + return false; + + default: + return false; + } + + return MakeEdit(parsedDocument, token, preferredIndentationToken, isEmpty: false); } - else + + bool MakeEdit( + ParsedDocument parsedDocument, + SyntaxToken token, + SyntaxToken preferredIndentationToken, + bool isEmpty) { - var newLineAndIndentation = newLine + indentation; - // Add a newline at the position of the end literal - var closingStart = GetStartPositionOfClosingDelimiter(token); - edit.Insert(closingStart, newLineAndIndentation); - // Add a newline at the caret's position, to insert the newline that the user requested - edit.Insert(position, newLineAndIndentation); - // Also add a newline at the start of the text, only if there is text before the caret's position - if (openingEnd != position) + var project = document.Project; + var indentationOptions = subjectBuffer.GetIndentationOptions(_editorOptionsService, project.GetFallbackAnalyzerOptions(), project.Services, explicitFormat: false); + var indentation = preferredIndentationToken.GetPreferredIndentation(parsedDocument, indentationOptions, cancellationToken); + + var newLine = indentationOptions.FormattingOptions.NewLine; + + using var transaction = CaretPreservingEditTransaction.TryCreate( + CSharpEditorResources.Split_raw_string, textView, _undoHistoryRegistry, _editorOperationsFactoryService); + var edit = subjectBuffer.CreateEdit(); + + var openingEnd = GetEndPositionOfOpeningDelimiter(token, isEmpty); + + // We're always at least inserting one new line on enter. + var insertedLinesBeforeCaret = 1; + if (isEmpty) { - insertedLines++; - edit.Insert(openingEnd, newLineAndIndentation); + // If the literal is empty, we just want to help the user transform it into a multiline raw string literal + // with the extra empty newline between the delimiters to place the caret at + edit.Insert(position, newLine + newLine + indentation); + } + else + { + // Otherwise, we're starting with a raw string that has content in it. That's something like: + // """GooBar""". If we hit enter at the `G` we only want to insert a single new line before the caret. + // However, if we were to hit enter anywhere after that, we want two new lines inserted. One after the + // `"""` and one at the caret itself. + var newLineAndIndentation = newLine + indentation; + + // Add a newline at the position of the end literal + var closingStart = GetStartPositionOfClosingDelimiter(token); + edit.Insert(closingStart, newLineAndIndentation); + + // Add a newline at the caret's position, to insert the newline that the user requested + edit.Insert(position, newLineAndIndentation); + + // Also add a newline at the start of the text, only if there is text before the caret's position + if (openingEnd != position) + { + insertedLinesBeforeCaret++; + edit.Insert(openingEnd, newLineAndIndentation); + } } - } - var snapshot = edit.Apply(); + var snapshot = edit.Apply(); - // move caret: - var lineInNewSnapshot = snapshot.GetLineFromPosition(openingEnd); - var nextLine = snapshot.GetLineFromLineNumber(lineInNewSnapshot.LineNumber + insertedLines); - textView.Caret.MoveTo(new VirtualSnapshotPoint(nextLine, indentation.Length)); + // move caret: + var lineInNewSnapshot = snapshot.GetLineFromPosition(openingEnd); + var nextLine = snapshot.GetLineFromLineNumber(lineInNewSnapshot.LineNumber + insertedLinesBeforeCaret); + textView.Caret.MoveTo(new VirtualSnapshotPoint(nextLine, indentation.Length)); - transaction?.Complete(); - return true; + transaction?.Complete(); + return true; + } } private static int GetEndPositionOfOpeningDelimiter(SyntaxToken currentStringLiteralToken, bool isEmpty) @@ -185,6 +238,7 @@ private static int GetEndPositionOfOpeningDelimiter(SyntaxToken currentStringLit var quotes = isEmpty ? index / 2 : index; return tokenStart + quotes; } + index++; } @@ -280,56 +334,4 @@ private static int GetStartPositionOfClosingDelimiter(SyntaxToken currentStringL return -1; } } - - private bool HandleCaretOnQuote(ITextView textView, ITextBuffer subjectBuffer, SnapshotSpan span, int position, CancellationToken cancellationToken) - { - var quotesBefore = 0; - var quotesAfter = 0; - - var currentSnapshot = subjectBuffer.CurrentSnapshot; - for (int i = position, n = currentSnapshot.Length; i < n; i++) - { - if (currentSnapshot[i] != '"') - break; - - quotesAfter++; - } - - for (var i = position - 1; i >= 0; i--) - { - if (currentSnapshot[i] != '"') - break; - - quotesBefore++; - } - - var isEmpty = quotesBefore > 0; - if (isEmpty && quotesAfter != quotesBefore) - return false; - - if (quotesAfter < 3) - return false; - - return SplitRawString(textView, subjectBuffer, span.Start.Position, isEmpty, cancellationToken); - } - - private bool SplitRawString(ITextView textView, ITextBuffer subjectBuffer, int position, bool isEmpty, CancellationToken cancellationToken) - { - var document = subjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); - if (document == null) - return false; - - var parsedDocument = ParsedDocument.CreateSynchronously(document, cancellationToken); - - var token = parsedDocument.Root.FindToken(position); - if (token.Kind() is not (SyntaxKind.SingleLineRawStringLiteralToken or - SyntaxKind.MultiLineRawStringLiteralToken or - SyntaxKind.InterpolatedSingleLineRawStringStartToken or - SyntaxKind.InterpolatedMultiLineRawStringStartToken)) - { - return false; - } - - return MakeEdit(textView, subjectBuffer, position, document.Project, parsedDocument, token, preferredIndentationToken: token, isEmpty, cancellationToken); - } } From 2bd4adb804a3c47e060905652b1730f35a394e49 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 15 Nov 2024 11:54:49 -0800 Subject: [PATCH 388/508] Clean up --- .../RawStringLiteralCommandHandler_Return.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs index f7092f52beaaf..bc7d71210c537 100644 --- a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs +++ b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs @@ -125,25 +125,25 @@ bool ExecuteReturnCommandNotBeforeQuoteCharacter() case SyntaxKind.InterpolatedStringTextToken: case SyntaxKind.OpenBraceToken: - if (token.Parent?.Parent is InterpolatedStringExpressionSyntax interpolated && - interpolated.StringStartToken.Kind() is SyntaxKind.InterpolatedSingleLineRawStringStartToken) + if (token.Parent?.Parent is not InterpolatedStringExpressionSyntax interpolated || + interpolated.StringStartToken.Kind() is not SyntaxKind.InterpolatedSingleLineRawStringStartToken) { - if (token.Kind() is SyntaxKind.OpenBraceToken) - { - // If we are not at the start of the interpolation braces, we do not intend to handle converting the raw string - // into a new one - if (position != token.SpanStart) - return false; - - // We prefer the indentation options of the string start delimiter because the indentation of the interpolation - // is empty and thus we cannot properly indent the lines that we insert - preferredIndentationToken = interpolated.StringStartToken; - } + return false; + } - break; + if (token.Kind() is SyntaxKind.OpenBraceToken) + { + // If we are not at the start of the interpolation braces, we do not intend to handle converting the raw string + // into a new one + if (position != token.SpanStart) + return false; + + // We prefer the indentation options of the string start delimiter because the indentation of the interpolation + // is empty and thus we cannot properly indent the lines that we insert + preferredIndentationToken = interpolated.StringStartToken; } - return false; + break; default: return false; From 632f8c8d334e3f419e5a398668d12a7646dce2a8 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Fri, 15 Nov 2024 11:57:20 -0800 Subject: [PATCH 389/508] Optimize single spread of IEnumerable to array (#75847) --- .../LocalRewriter_CollectionExpression.cs | 103 +- .../Semantics/CollectionExpressionTests.cs | 1194 +++++++++++------ .../Core/Portable/WellKnownMember.cs | 1 + .../Core/Portable/WellKnownMembers.cs | 12 + 4 files changed, 898 insertions(+), 412 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs index 65c0eb04c461f..4f4d5c20dfdb1 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs @@ -175,7 +175,7 @@ private bool TryRewriteSingleElementSpreadToList(BoundCollectionExpression node, Debug.Assert(singleSpread.Expression.Type is not null); - if (!ShouldUseAddRangeOrToListMethod(singleSpread.Expression.Type, toListOfElementType.Parameters[0].Type, singleSpread.EnumeratorInfoOpt?.GetEnumeratorInfo.Method)) + if (!ShouldUseIEnumerableBulkAddMethod(singleSpread.Expression.Type, toListOfElementType.Parameters[0].Type, singleSpread.EnumeratorInfoOpt?.GetEnumeratorInfo.Method)) { return false; } @@ -185,7 +185,10 @@ private bool TryRewriteSingleElementSpreadToList(BoundCollectionExpression node, return true; } - private bool ShouldUseAddRangeOrToListMethod(TypeSymbol spreadType, TypeSymbol targetEnumerableType, MethodSymbol? getEnumeratorMethod) + /// + /// Decides if a bulk-add method such as AddRange, ToList, ToArray, etc. is suitable for copying a spread value with type 'spreadType' to the destination collection. + /// + private bool ShouldUseIEnumerableBulkAddMethod(TypeSymbol spreadType, TypeSymbol targetEnumerableType, MethodSymbol? getEnumeratorMethod) { Debug.Assert(targetEnumerableType.OriginalDefinition == (object)_compilation.GetSpecialType(SpecialType.System_Collections_Generic_IEnumerable_T)); @@ -296,7 +299,11 @@ private BoundExpression VisitArrayOrSpanCollectionExpression(BoundCollectionExpr } BoundExpression array; - if (ShouldUseKnownLength(node, out _)) + if (TryOptimizeSingleSpreadToArray(node, arrayType) is { } optimizedArray) + { + array = optimizedArray; + } + else if (ShouldUseKnownLength(node, out _)) { array = CreateAndPopulateArray(node, arrayType); } @@ -432,7 +439,7 @@ SpecialType.System_Collections_Generic_IReadOnlyCollection_T or // fieldValue = e1; SynthesizedReadOnlyListKind.SingleElement => this.VisitExpression((BoundExpression)elements.Single()), // fieldValue = new ElementType[] { e1, ..., eN }; - SynthesizedReadOnlyListKind.Array => CreateAndPopulateArray(node, ArrayTypeSymbol.CreateSZArray(_compilation.Assembly, elementType)), + SynthesizedReadOnlyListKind.Array => createArray(node, ArrayTypeSymbol.CreateSZArray(_compilation.Assembly, elementType)), // fieldValue = new List { e1, ..., eN }; SynthesizedReadOnlyListKind.List => CreateAndPopulateList(node, elementType, elements), var v => throw ExceptionUtilities.UnexpectedValue(v) @@ -448,6 +455,14 @@ SpecialType.System_Collections_Generic_IReadOnlyCollection_T or } return _factory.Convert(collectionType, arrayOrList); + + BoundExpression createArray(BoundCollectionExpression node, ArrayTypeSymbol arrayType) + { + if (TryOptimizeSingleSpreadToArray(node, arrayType) is { } optimizedArray) + return optimizedArray; + + return CreateAndPopulateArray(node, arrayType); + } } private BoundExpression VisitCollectionBuilderCollectionExpression(BoundCollectionExpression node) @@ -632,36 +647,45 @@ private static bool ShouldUseKnownLength(BoundCollectionExpression node, out int return false; } - /// - /// Create and populate an array from a collection expression where the - /// collection has a known length, although possibly including spreads. - /// - private BoundExpression CreateAndPopulateArray(BoundCollectionExpression node, ArrayTypeSymbol arrayType) + /// Attempt to optimize conversion of a single-spread collection expr to array, even if the spread length is not known. + /// + /// The following optimizations are tried, in order: + /// 1. 'List.ToArray' if the spread value is a list + /// 2. 'Enumerable.ToArray' if we can convert the spread value to IEnumerable and additional conditions are met + /// 3. 'Span/ReadOnlySpan.ToArray' if we can convert the spread value to Span or ReadOnlySpan + /// + private BoundExpression? TryOptimizeSingleSpreadToArray(BoundCollectionExpression node, ArrayTypeSymbol arrayType) { - var syntax = node.Syntax; - var elements = node.Elements; - - int numberIncludingLastSpread; - if (!ShouldUseKnownLength(node, out numberIncludingLastSpread)) - { - // Should have been handled by the caller. - throw ExceptionUtilities.UnexpectedValue(node); - } - - // Collection-expr is of the form `[..spreadExpression]`, where 'spreadExpression' has same element type as the target collection. + // Collection-expr is of the form `[..spreadExpression]`. // Optimize to `spreadExpression.ToArray()` if possible. if (node is { Elements: [BoundCollectionExpressionSpreadElement { Expression: { } spreadExpression } spreadElement] } - && spreadElement.IteratorBody is BoundExpressionStatement expressionStatement - && expressionStatement.Expression is not BoundConversion) + && spreadElement.IteratorBody is BoundExpressionStatement expressionStatement) { + var spreadElementHasIdentityConversion = expressionStatement.Expression is not BoundConversion; var spreadTypeOriginalDefinition = spreadExpression.Type!.OriginalDefinition; - if (tryGetToArrayMethod(spreadTypeOriginalDefinition, WellKnownType.System_Collections_Generic_List_T, WellKnownMember.System_Collections_Generic_List_T__ToArray, out MethodSymbol? listToArrayMethod)) + + if (spreadElementHasIdentityConversion + && tryGetToArrayMethod(spreadTypeOriginalDefinition, WellKnownType.System_Collections_Generic_List_T, WellKnownMember.System_Collections_Generic_List_T__ToArray, out MethodSymbol? listToArrayMethod)) { var rewrittenSpreadExpression = VisitExpression(spreadExpression); return _factory.Call(rewrittenSpreadExpression, listToArrayMethod.AsMember((NamedTypeSymbol)spreadExpression.Type!)); } - if (TryGetSpanConversion(spreadExpression.Type, writableOnly: false, out var asSpanMethod)) + // See if 'Enumerable.ToArray(IEnumerable)' will work, possibly due to a covariant conversion on the spread value. + if (_factory.WellKnownMethod(WellKnownMember.System_Linq_Enumerable__ToArray, isOptional: true) is { } linqToArrayMethodGeneric) + { + // Note that in general, we expect well-known collection types and methods to lack constraints on their type parameter(s). + // Because an array element type may not be a valid type argument for unconstrained type parameter, we still check constraints here regardless. + var linqToArrayMethod = linqToArrayMethodGeneric.Construct([arrayType.ElementTypeWithAnnotations]); + if (linqToArrayMethod.CheckConstraints(new ConstraintsHelper.CheckConstraintsArgs(_compilation, _compilation.Conversions, Location.None, BindingDiagnosticBag.Discarded)) + && ShouldUseIEnumerableBulkAddMethod(spreadExpression.Type!, linqToArrayMethod.Parameters[0].Type, spreadElement.EnumeratorInfoOpt?.GetEnumeratorInfo.Method)) + { + return _factory.Call(receiver: null, linqToArrayMethod, VisitExpression(spreadExpression)); + } + } + + if (spreadElementHasIdentityConversion + && TryGetSpanConversion(spreadExpression.Type, writableOnly: false, out var asSpanMethod)) { var spanType = CallAsSpanMethod(spreadExpression, asSpanMethod).Type!.OriginalDefinition; if (tryGetToArrayMethod(spanType, WellKnownType.System_ReadOnlySpan_T, WellKnownMember.System_ReadOnlySpan_T__ToArray, out var toArrayMethod) @@ -685,6 +709,28 @@ bool tryGetToArrayMethod(TypeSymbol spreadTypeOriginalDefinition, WellKnownType } } + return null; + } + + /// + /// Create and populate an array from a collection expression where the + /// collection has a known length, although possibly including spreads. + /// + private BoundExpression CreateAndPopulateArray(BoundCollectionExpression node, ArrayTypeSymbol arrayType) + { + var syntax = node.Syntax; + var elements = node.Elements; + + int numberIncludingLastSpread; + if (!ShouldUseKnownLength(node, out numberIncludingLastSpread)) + { + // Should have been handled by the caller. + throw ExceptionUtilities.UnexpectedValue(node); + } + + // Shouldn't call this method if the single spread optimization would work. + Debug.Assert(TryOptimizeSingleSpreadToArray(node, arrayType) is null); + if (numberIncludingLastSpread == 0) { int knownLength = elements.Length; @@ -812,11 +858,10 @@ private bool TryGetSpanConversion(TypeSymbol type, bool writableOnly, out Method if (type is ArrayTypeSymbol { IsSZArray: true } arrayType && _factory.WellKnownMethod(writableOnly ? WellKnownMember.System_Span_T__ctor_Array : WellKnownMember.System_ReadOnlySpan_T__ctor_Array, isOptional: true) is { } spanCtorArray) { - // conversion to 'object' will fail if, for example, 'arrayType.ElementType' is a pointer. - var useSiteInfo = CompoundUseSiteInfo.Discarded; - if (_compilation.Conversions.ClassifyConversionFromType(source: arrayType.ElementType, destination: _compilation.GetSpecialType(SpecialType.System_Object), isChecked: false, ref useSiteInfo).IsImplicit) + var spanOfElementType = spanCtorArray.ContainingType.Construct(arrayType.ElementType); + if (spanOfElementType.CheckConstraints(new ConstraintsHelper.CheckConstraintsArgs(_compilation, _compilation.Conversions, Location.None, BindingDiagnosticBag.Discarded))) { - asSpanMethod = spanCtorArray.AsMember(spanCtorArray.ContainingType.Construct(arrayType.ElementType)); + asSpanMethod = spanCtorArray.AsMember(spanOfElementType); return true; } } @@ -1135,7 +1180,7 @@ private BoundExpression CreateAndPopulateList(BoundCollectionExpression node, Ty if (addRangeMethod is null) return false; - if (!ShouldUseAddRangeOrToListMethod(rewrittenSpreadOperand.Type, addRangeMethod.Parameters[0].Type, spreadElement.EnumeratorInfoOpt?.GetEnumeratorInfo.Method)) + if (!ShouldUseIEnumerableBulkAddMethod(rewrittenSpreadOperand.Type, addRangeMethod.Parameters[0].Type, spreadElement.EnumeratorInfoOpt?.GetEnumeratorInfo.Method)) { return false; } diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs index c2350f829f9eb..eeb7002d0a44e 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs @@ -4639,50 +4639,11 @@ static void Main() expectedOutput: IncludeExpectedOutput("[a, b, c, d], ")); verifier.VerifyIL("Program.GetChars", """ { - // Code size 64 (0x40) - .maxstack 3 - .locals init (int V_0, - char[] V_1, - System.CharEnumerator V_2, - char V_3) + // Code size 11 (0xb) + .maxstack 1 IL_0000: ldstr "abcd" - IL_0005: ldc.i4.0 - IL_0006: stloc.0 - IL_0007: dup - IL_0008: callvirt "int string.Length.get" - IL_000d: newarr "char" - IL_0012: stloc.1 - IL_0013: callvirt "System.CharEnumerator string.GetEnumerator()" - IL_0018: stloc.2 - .try - { - IL_0019: br.s IL_002a - IL_001b: ldloc.2 - IL_001c: callvirt "char System.CharEnumerator.Current.get" - IL_0021: stloc.3 - IL_0022: ldloc.1 - IL_0023: ldloc.0 - IL_0024: ldloc.3 - IL_0025: stelem.i2 - IL_0026: ldloc.0 - IL_0027: ldc.i4.1 - IL_0028: add - IL_0029: stloc.0 - IL_002a: ldloc.2 - IL_002b: callvirt "bool System.CharEnumerator.MoveNext()" - IL_0030: brtrue.s IL_001b - IL_0032: leave.s IL_003e - } - finally - { - IL_0034: ldloc.2 - IL_0035: brfalse.s IL_003d - IL_0037: ldloc.2 - IL_0038: callvirt "void System.IDisposable.Dispose()" - IL_003d: endfinally - } - IL_003e: ldloc.1 - IL_003f: ret + IL_0005: call "char[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_000a: ret } """); } @@ -9454,37 +9415,47 @@ .maxstack 3 ("IEnumerable", "int[]") => """ { - // Code size 31 (0x1f) - .maxstack 3 + // Code size 20 (0x14) + .maxstack 1 .locals init (System.ReadOnlySpan V_0) - IL_0000: newobj "System.Collections.Generic.List..ctor()" - IL_0005: dup - IL_0006: ldarg.0 - IL_0007: callvirt "void System.Collections.Generic.List.AddRange(System.Collections.Generic.IEnumerable)" - IL_000c: callvirt "int[] System.Collections.Generic.List.ToArray()" - IL_0011: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(int[])" - IL_0016: stloc.0 - IL_0017: ldloca.s V_0 - IL_0019: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" - IL_001e: ret + IL_0000: ldarg.0 + IL_0001: call "int[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0006: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(int[])" + IL_000b: stloc.0 + IL_000c: ldloca.s V_0 + IL_000e: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_0013: ret + } + """, + ("IEnumerable", "Span") => + """ + { + // Code size 25 (0x19) + .maxstack 1 + .locals init (System.ReadOnlySpan V_0) + IL_0000: ldarg.0 + IL_0001: call "int[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0006: newobj "System.Span..ctor(int[])" + IL_000b: call "System.ReadOnlySpan System.Span.op_Implicit(System.Span)" + IL_0010: stloc.0 + IL_0011: ldloca.s V_0 + IL_0013: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_0018: ret } """, ("int[]", "int[]") => """ { - // Code size 28 (0x1c) + // Code size 20 (0x14) .maxstack 1 .locals init (System.ReadOnlySpan V_0) IL_0000: ldarg.0 - IL_0001: newobj "System.ReadOnlySpan..ctor(int[])" - IL_0006: stloc.0 - IL_0007: ldloca.s V_0 - IL_0009: call "int[] System.ReadOnlySpan.ToArray()" - IL_000e: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(int[])" - IL_0013: stloc.0 - IL_0014: ldloca.s V_0 - IL_0016: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" - IL_001b: ret + IL_0001: call "int[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0006: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(int[])" + IL_000b: stloc.0 + IL_000c: ldloca.s V_0 + IL_000e: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_0013: ret } """, ("ReadOnlySpan", "ReadOnlySpan") => @@ -11347,14 +11318,11 @@ static void Main() verifier.VerifyIL("Program.Main", """ { - // Code size 158 (0x9e) + // Code size 96 (0x60) .maxstack 3 .locals init (int V_0, System.Span V_1, - int V_2, - object[] V_3, - System.Collections.Generic.List.Enumerator V_4, - object V_5) + int V_2) IL_0000: ldc.i4.3 IL_0001: stloc.0 IL_0002: ldloc.0 @@ -11396,44 +11364,10 @@ .locals init (int V_0, IL_004d: dup IL_004e: ldc.i4.0 IL_004f: call "void CollectionExtensions.Report(object, bool)" - IL_0054: ldc.i4.0 - IL_0055: stloc.2 - IL_0056: dup - IL_0057: callvirt "int System.Collections.Generic.List.Count.get" - IL_005c: newarr "object" - IL_0061: stloc.3 - IL_0062: callvirt "System.Collections.Generic.List.Enumerator System.Collections.Generic.List.GetEnumerator()" - IL_0067: stloc.s V_4 - .try - { - IL_0069: br.s IL_007d - IL_006b: ldloca.s V_4 - IL_006d: call "dynamic System.Collections.Generic.List.Enumerator.Current.get" - IL_0072: stloc.s V_5 - IL_0074: ldloc.3 - IL_0075: ldloc.2 - IL_0076: ldloc.s V_5 - IL_0078: stelem.ref - IL_0079: ldloc.2 - IL_007a: ldc.i4.1 - IL_007b: add - IL_007c: stloc.2 - IL_007d: ldloca.s V_4 - IL_007f: call "bool System.Collections.Generic.List.Enumerator.MoveNext()" - IL_0084: brtrue.s IL_006b - IL_0086: leave.s IL_0096 - } - finally - { - IL_0088: ldloca.s V_4 - IL_008a: constrained. "System.Collections.Generic.List.Enumerator" - IL_0090: callvirt "void System.IDisposable.Dispose()" - IL_0095: endfinally - } - IL_0096: ldloc.3 - IL_0097: ldc.i4.0 - IL_0098: call "void CollectionExtensions.Report(object, bool)" - IL_009d: ret + IL_0054: call "object[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0059: ldc.i4.0 + IL_005a: call "void CollectionExtensions.Report(object, bool)" + IL_005f: ret } """); } @@ -23887,18 +23821,14 @@ .locals init (<>y__InlineArray3 V_0) verifier.VerifyIL("R..ctor(int, T[])", """ { - // Code size 26 (0x1a) + // Code size 18 (0x12) .maxstack 2 - .locals init (System.ReadOnlySpan V_0) IL_0000: ldarg.0 IL_0001: ldarg.2 - IL_0002: newobj "System.ReadOnlySpan..ctor(T[])" - IL_0007: stloc.0 - IL_0008: ldloca.s V_0 - IL_000a: call "T[] System.ReadOnlySpan.ToArray()" - IL_000f: newobj "System.Span..ctor(T[])" - IL_0014: call "R..ctor(scoped System.Span)" - IL_0019: ret + IL_0002: call "T[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0007: newobj "System.Span..ctor(T[])" + IL_000c: call "R..ctor(scoped System.Span)" + IL_0011: ret } """); } @@ -24677,27 +24607,24 @@ static void M(bool b, T[] a) expectedOutput: IncludeExpectedOutput("[1, null, 3], ")); verifier.VerifyIL("Program.M", """ { - // Code size 47 (0x2f) + // Code size 39 (0x27) .maxstack 2 .locals init (System.Span V_0, //s System.ReadOnlySpan V_1) IL_0000: ldloca.s V_0 IL_0002: initobj "System.Span" IL_0008: ldarg.0 - IL_0009: brfalse.s IL_0020 + IL_0009: brfalse.s IL_0018 IL_000b: ldloca.s V_0 IL_000d: ldarg.1 - IL_000e: newobj "System.ReadOnlySpan..ctor(T[])" - IL_0013: stloc.1 - IL_0014: ldloca.s V_1 - IL_0016: call "T[] System.ReadOnlySpan.ToArray()" - IL_001b: call "System.Span..ctor(T[])" - IL_0020: ldloc.0 - IL_0021: call "System.ReadOnlySpan System.Span.op_Implicit(System.Span)" - IL_0026: stloc.1 - IL_0027: ldloca.s V_1 - IL_0029: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" - IL_002e: ret + IL_000e: call "T[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0013: call "System.Span..ctor(T[])" + IL_0018: ldloc.0 + IL_0019: call "System.ReadOnlySpan System.Span.op_Implicit(System.Span)" + IL_001e: stloc.1 + IL_001f: ldloca.s V_1 + IL_0021: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_0026: ret } """); } @@ -30396,23 +30323,19 @@ static void Main() verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { - // Code size 47 (0x2f) + // Code size 39 (0x27) .maxstack 3 - .locals init (System.ReadOnlySpan V_0) IL_0000: ldc.i4.3 IL_0001: newarr "int" IL_0006: dup IL_0007: ldtoken ".__StaticArrayInitTypeSize=12 .4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" - IL_0011: newobj "System.ReadOnlySpan..ctor(int[])" - IL_0016: stloc.0 - IL_0017: ldloca.s V_0 - IL_0019: call "int[] System.ReadOnlySpan.ToArray()" - IL_001e: call "System.Collections.Immutable.ImmutableArray System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray(int[])" - IL_0023: box "System.Collections.Immutable.ImmutableArray" - IL_0028: ldc.i4.0 - IL_0029: call "void CollectionExtensions.Report(object, bool)" - IL_002e: ret + IL_0011: call "int[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0016: call "System.Collections.Immutable.ImmutableArray System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray(int[])" + IL_001b: box "System.Collections.Immutable.ImmutableArray" + IL_0020: ldc.i4.0 + IL_0021: call "void CollectionExtensions.Report(object, bool)" + IL_0026: ret } """); } @@ -30439,26 +30362,20 @@ static void Main() verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { - // Code size 57 (0x39) + // Code size 44 (0x2c) .maxstack 3 - .locals init (System.Collections.Generic.IEnumerable V_0) //arr IL_0000: ldc.i4.3 IL_0001: newarr "int" IL_0006: dup IL_0007: ldtoken ".__StaticArrayInitTypeSize=12 .4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" IL_0011: newobj "<>z__ReadOnlyArray..ctor(int[])" - IL_0016: stloc.0 - IL_0017: newobj "System.Collections.Generic.List..ctor()" - IL_001c: dup - IL_001d: ldloc.0 - IL_001e: callvirt "void System.Collections.Generic.List.AddRange(System.Collections.Generic.IEnumerable)" - IL_0023: callvirt "int[] System.Collections.Generic.List.ToArray()" - IL_0028: call "System.Collections.Immutable.ImmutableArray System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray(int[])" - IL_002d: box "System.Collections.Immutable.ImmutableArray" - IL_0032: ldc.i4.0 - IL_0033: call "void CollectionExtensions.Report(object, bool)" - IL_0038: ret + IL_0016: call "int[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_001b: call "System.Collections.Immutable.ImmutableArray System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray(int[])" + IL_0020: box "System.Collections.Immutable.ImmutableArray" + IL_0025: ldc.i4.0 + IL_0026: call "void CollectionExtensions.Report(object, bool)" + IL_002b: ret } """); } @@ -34205,62 +34122,9 @@ .locals init (int[] V_0, """); } - [Fact] - public void ArrayToArray_Covariant_SingleSpread() - { - var source = """ - class Base { } - class Derived : Base { } - - class C - { - static void Main() - { - Base[] array = new Derived[] { new Derived() }; - array.Report(); - - Base[] copy = [..array]; - copy.Report(); - } - } - """; - - var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[Derived], [Derived], "), targetFramework: TargetFramework.Net80); - verifier.VerifyDiagnostics(); - verifier.VerifyIL("C.Main", """ - { - // Code size 57 (0x39) - .maxstack 4 - .locals init (Base[] V_0, - System.ReadOnlySpan V_1) - IL_0000: ldc.i4.1 - IL_0001: newarr "Derived" - IL_0006: dup - IL_0007: ldc.i4.0 - IL_0008: newobj "Derived..ctor()" - IL_000d: stelem.ref - IL_000e: stloc.0 - IL_000f: ldloc.0 - IL_0010: dup - IL_0011: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(Base[])" - IL_0016: stloc.1 - IL_0017: ldloca.s V_1 - IL_0019: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" - IL_001e: newobj "System.ReadOnlySpan..ctor(Base[])" - IL_0023: stloc.1 - IL_0024: ldloca.s V_1 - IL_0026: call "Base[] System.ReadOnlySpan.ToArray()" - IL_002b: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(Base[])" - IL_0030: stloc.1 - IL_0031: ldloca.s V_1 - IL_0033: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" - IL_0038: ret - } - """); - } - - [Fact] - public void ArrayToArray_Covariant_SingleSpread_ReadOnlySpanCtorMissing() + [Theory] + [CombinatorialData] + public void ArrayToArray_Covariant_SingleSpread(bool missingReadOnlySpanCtor) { var source = """ class Base { } @@ -34279,23 +34143,19 @@ static void Main() } """; - // In the event that the ReadOnlySpan ctor is missing, we do not fall back to converting the array spread value to Span. - // Instead, we lower the spread without optimizing it. var comp = CreateCompilation(new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - comp.MakeMemberMissing(WellKnownMember.System_ReadOnlySpan_T__ctor_Array); + + if (missingReadOnlySpanCtor) + comp.MakeMemberMissing(WellKnownMember.System_ReadOnlySpan_T__ctor_Array); var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[Derived], [Derived], ")); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { - // Code size 90 (0x5a) + // Code size 49 (0x31) .maxstack 4 .locals init (Base[] V_0, - System.ReadOnlySpan V_1, - int V_2, - Base[] V_3, - int V_4, - Base V_5) + System.ReadOnlySpan V_1) IL_0000: ldc.i4.1 IL_0001: newarr "Derived" IL_0006: dup @@ -34309,44 +34169,12 @@ .locals init (Base[] V_0, IL_0016: stloc.1 IL_0017: ldloca.s V_1 IL_0019: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" - IL_001e: ldc.i4.0 - IL_001f: stloc.2 - IL_0020: dup - IL_0021: ldlen - IL_0022: conv.i4 - IL_0023: newarr "Base" - IL_0028: stloc.0 - IL_0029: stloc.3 - IL_002a: ldc.i4.0 - IL_002b: stloc.s V_4 - IL_002d: br.s IL_0044 - IL_002f: ldloc.3 - IL_0030: ldloc.s V_4 - IL_0032: ldelem.ref - IL_0033: stloc.s V_5 - IL_0035: ldloc.0 - IL_0036: ldloc.2 - IL_0037: ldloc.s V_5 - IL_0039: stelem.ref - IL_003a: ldloc.2 - IL_003b: ldc.i4.1 - IL_003c: add - IL_003d: stloc.2 - IL_003e: ldloc.s V_4 - IL_0040: ldc.i4.1 - IL_0041: add - IL_0042: stloc.s V_4 - IL_0044: ldloc.s V_4 - IL_0046: ldloc.3 - IL_0047: ldlen - IL_0048: conv.i4 - IL_0049: blt.s IL_002f - IL_004b: ldloc.0 - IL_004c: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(Base[])" - IL_0051: stloc.1 - IL_0052: ldloca.s V_1 - IL_0054: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" - IL_0059: ret + IL_001e: call "Base[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0023: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(Base[])" + IL_0028: stloc.1 + IL_0029: ldloca.s V_1 + IL_002b: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_0030: ret } """); } @@ -34857,8 +34685,54 @@ .locals init (int V_0, //i """); } + [Theory] + [CombinatorialData] + public void SingleSpread_ArrayToArray_WellKnownMemberMissing_01(bool readOnlySpanMembersMissing) + { + var source = """ + class C + { + static void Main() + { + int[] arr = [1, 2, 3]; + arr.Report(); + int[] arr1 = [..arr]; + arr1.Report(); + } + } + """; + + var comp = CreateCompilation([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + if (readOnlySpanMembersMissing) + { + comp.MakeMemberMissing(WellKnownMember.System_ReadOnlySpan_T__ctor_Array); + comp.MakeMemberMissing(WellKnownMember.System_ReadOnlySpan_T__ToArray); + } + + var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],")); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.Main", """ + { + // Code size 36 (0x24) + .maxstack 3 + IL_0000: ldc.i4.3 + IL_0001: newarr "int" + IL_0006: dup + IL_0007: ldtoken ".__StaticArrayInitTypeSize=12 .4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" + IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" + IL_0011: dup + IL_0012: ldc.i4.0 + IL_0013: call "void CollectionExtensions.Report(object, bool)" + IL_0018: call "int[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_001d: ldc.i4.0 + IL_001e: call "void CollectionExtensions.Report(object, bool)" + IL_0023: ret + } + """); + } + [Fact] - public void SingleSpread_WellKnownMemberMissing() + public void SingleSpread_ArrayToArray_WellKnownMemberMissing_02() { var source = """ class C @@ -34874,6 +34748,7 @@ static void Main() """; var comp = CreateCompilation([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + comp.MakeMemberMissing(WellKnownMember.System_Linq_Enumerable__ToArray); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],")); verifier.VerifyDiagnostics(); @@ -34899,84 +34774,40 @@ .locals init (System.ReadOnlySpan V_0) IL_002b: ret } """); + } + + [Fact] + public void SingleSpread_ArrayToArray_WellKnownMemberMissing_03() + { + var source = """ + class C + { + static void Main() + { + int[] arr = [1, 2, 3]; + arr.Report(); + int[] arr1 = [..arr]; + arr1.Report(); + } + } + """; - // No ReadOnlySpan(T[]) constructor. Spread optimizations can't be performed. - comp = CreateCompilation([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + var comp = CreateCompilation([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + comp.MakeMemberMissing(WellKnownMember.System_Linq_Enumerable__ToArray); comp.MakeMemberMissing(WellKnownMember.System_ReadOnlySpan_T__ctor_Array); + comp.MakeMemberMissing(WellKnownMember.System_ReadOnlySpan_T__ToArray); - verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],")); + var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],")); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { - // Code size 72 (0x48) - .maxstack 3 - .locals init (int V_0, + // Code size 72 (0x48) + .maxstack 3 + .locals init (int V_0, int[] V_1, int[] V_2, int V_3, int V_4) - IL_0000: ldc.i4.3 - IL_0001: newarr "int" - IL_0006: dup - IL_0007: ldtoken ".__StaticArrayInitTypeSize=12 .4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" - IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" - IL_0011: dup - IL_0012: ldc.i4.0 - IL_0013: call "void CollectionExtensions.Report(object, bool)" - IL_0018: ldc.i4.0 - IL_0019: stloc.0 - IL_001a: dup - IL_001b: ldlen - IL_001c: conv.i4 - IL_001d: newarr "int" - IL_0022: stloc.1 - IL_0023: stloc.2 - IL_0024: ldc.i4.0 - IL_0025: stloc.3 - IL_0026: br.s IL_003a - IL_0028: ldloc.2 - IL_0029: ldloc.3 - IL_002a: ldelem.i4 - IL_002b: stloc.s V_4 - IL_002d: ldloc.1 - IL_002e: ldloc.0 - IL_002f: ldloc.s V_4 - IL_0031: stelem.i4 - IL_0032: ldloc.0 - IL_0033: ldc.i4.1 - IL_0034: add - IL_0035: stloc.0 - IL_0036: ldloc.3 - IL_0037: ldc.i4.1 - IL_0038: add - IL_0039: stloc.3 - IL_003a: ldloc.3 - IL_003b: ldloc.2 - IL_003c: ldlen - IL_003d: conv.i4 - IL_003e: blt.s IL_0028 - IL_0040: ldloc.1 - IL_0041: ldc.i4.0 - IL_0042: call "void CollectionExtensions.Report(object, bool)" - IL_0047: ret - } - """); - - // No ReadOnlySpan.ToArray method. ToArray optimization for single spreads cannot be performed, but CopyTo optimization still can. - comp = CreateCompilation([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - comp.MakeMemberMissing(WellKnownMember.System_ReadOnlySpan_T__ToArray); - - verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],")); - verifier.VerifyDiagnostics(); - verifier.VerifyIL("C.Main", """ - { - // Code size 92 (0x5c) - .maxstack 4 - .locals init (int[] V_0, - int V_1, - int[] V_2, - System.ReadOnlySpan V_3, - System.Span V_4) IL_0000: ldc.i4.3 IL_0001: newarr "int" IL_0006: dup @@ -34985,53 +34816,436 @@ .locals init (int[] V_0, IL_0011: dup IL_0012: ldc.i4.0 IL_0013: call "void CollectionExtensions.Report(object, bool)" - IL_0018: stloc.0 - IL_0019: ldc.i4.0 - IL_001a: stloc.1 - IL_001b: ldloc.0 - IL_001c: ldlen - IL_001d: conv.i4 - IL_001e: newarr "int" + IL_0018: ldc.i4.0 + IL_0019: stloc.0 + IL_001a: dup + IL_001b: ldlen + IL_001c: conv.i4 + IL_001d: newarr "int" + IL_0022: stloc.1 IL_0023: stloc.2 - IL_0024: ldloca.s V_3 - IL_0026: ldloc.0 - IL_0027: call "System.ReadOnlySpan..ctor(int[])" - IL_002c: ldloca.s V_3 - IL_002e: ldloc.2 - IL_002f: newobj "System.Span..ctor(int[])" - IL_0034: stloc.s V_4 - IL_0036: ldloca.s V_4 - IL_0038: ldloc.1 - IL_0039: ldloca.s V_3 - IL_003b: call "int System.ReadOnlySpan.Length.get" - IL_0040: call "System.Span System.Span.Slice(int, int)" - IL_0045: call "void System.ReadOnlySpan.CopyTo(System.Span)" - IL_004a: ldloc.1 - IL_004b: ldloca.s V_3 - IL_004d: call "int System.ReadOnlySpan.Length.get" - IL_0052: add - IL_0053: stloc.1 - IL_0054: ldloc.2 - IL_0055: ldc.i4.0 - IL_0056: call "void CollectionExtensions.Report(object, bool)" - IL_005b: ret + IL_0024: ldc.i4.0 + IL_0025: stloc.3 + IL_0026: br.s IL_003a + IL_0028: ldloc.2 + IL_0029: ldloc.3 + IL_002a: ldelem.i4 + IL_002b: stloc.s V_4 + IL_002d: ldloc.1 + IL_002e: ldloc.0 + IL_002f: ldloc.s V_4 + IL_0031: stelem.i4 + IL_0032: ldloc.0 + IL_0033: ldc.i4.1 + IL_0034: add + IL_0035: stloc.0 + IL_0036: ldloc.3 + IL_0037: ldc.i4.1 + IL_0038: add + IL_0039: stloc.3 + IL_003a: ldloc.3 + IL_003b: ldloc.2 + IL_003c: ldlen + IL_003d: conv.i4 + IL_003e: blt.s IL_0028 + IL_0040: ldloc.1 + IL_0041: ldc.i4.0 + IL_0042: call "void CollectionExtensions.Report(object, bool)" + IL_0047: ret } """); } [Fact] - public void MultipleSpreads_WellKnownMemberMissing() + public void SingleSpread_ArrayToArray_Covariant() { var source = """ + using System; + + Console.Write(C.M(["a"])[0]); + class C { - static void Main() - { - int[] arr = [1, 2]; - arr.Report(); - int[] arr1 = [..arr, ..arr]; - arr1.Report(); - } + public static object[] M(string[] arr) => [..arr]; + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "a"); + verifier.VerifyIL("C.M", """ + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call "object[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0006: ret + } + """); + } + + [Fact] + public void SingleSpread_ArrayToSpan_CovariantVersusInvariant() + { + var source = """ + using System; + + string[] arr = ["a"]; + C.M1(arr); + C.M2(arr); + + class C + { + public static void M1(string[] arr) + { + Span span = [..arr]; + span.Report(); + } + public static void M2(object[] arr) + { + Span span = [..arr]; + span.Report(); + } + } + """; + + var verifier = CompileAndVerify([source, s_collectionExtensionsWithSpan], expectedOutput: IncludeExpectedOutput("[a], [a], "), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); + var expectedIL = """ + { + // Code size 25 (0x19) + .maxstack 1 + .locals init (System.ReadOnlySpan V_0) + IL_0000: ldarg.0 + IL_0001: call "object[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0006: newobj "System.Span..ctor(object[])" + IL_000b: call "System.ReadOnlySpan System.Span.op_Implicit(System.Span)" + IL_0010: stloc.0 + IL_0011: ldloca.s V_0 + IL_0013: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_0018: ret + } + """; + verifier.VerifyIL("C.M1", expectedIL); + verifier.VerifyIL("C.M2", expectedIL); + } + + [Fact] + public void SingleSpread_ArrayToReadOnlySpan_CovariantVersusInvariant() + { + var source = """ + using System; + + string[] arr = ["a"]; + C.M1(arr); + C.M2(arr); + + class C + { + public static void M1(string[] arr) + { + ReadOnlySpan span = [..arr]; + span.Report(); + } + public static void M2(object[] arr) + { + ReadOnlySpan span = [..arr]; + span.Report(); + } + } + """; + + var verifier = CompileAndVerify([source, s_collectionExtensionsWithSpan], expectedOutput: IncludeExpectedOutput("[a], [a], "), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); + var expectedIL = """ + { + // Code size 21 (0x15) + .maxstack 2 + .locals init (System.ReadOnlySpan V_0) //span + IL_0000: ldloca.s V_0 + IL_0002: ldarg.0 + IL_0003: call "object[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0008: call "System.ReadOnlySpan..ctor(object[])" + IL_000d: ldloca.s V_0 + IL_000f: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_0014: ret + } + """; + verifier.VerifyIL("C.M1", expectedIL); + verifier.VerifyIL("C.M2", expectedIL); + } + + [Fact] + public void SingleSpread_ListToArray_Covariant() + { + // Linq ToArray method is applicable, but we do not use it in this case, + // because the List has a struct enumerator and doesn't implement ICollection. + // If we could get a Span out of the List, then covariant-convert, we could use ReadOnlySpan.ToArray() here. + // https://github.com/dotnet/roslyn/issues/71106 + var source = """ + using System; + using System.Collections.Generic; + + Console.Write(C.M(["a"])[0]); + + class C + { + public static object[] M(List list) => [..list]; + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "a"); + verifier.VerifyIL("C.M", """ + { + // Code size 66 (0x42) + .maxstack 3 + .locals init (int V_0, + object[] V_1, + System.Collections.Generic.List.Enumerator V_2, + string V_3) + IL_0000: ldarg.0 + IL_0001: ldc.i4.0 + IL_0002: stloc.0 + IL_0003: dup + IL_0004: callvirt "int System.Collections.Generic.List.Count.get" + IL_0009: newarr "object" + IL_000e: stloc.1 + IL_000f: callvirt "System.Collections.Generic.List.Enumerator System.Collections.Generic.List.GetEnumerator()" + IL_0014: stloc.2 + .try + { + IL_0015: br.s IL_0027 + IL_0017: ldloca.s V_2 + IL_0019: call "string System.Collections.Generic.List.Enumerator.Current.get" + IL_001e: stloc.3 + IL_001f: ldloc.1 + IL_0020: ldloc.0 + IL_0021: ldloc.3 + IL_0022: stelem.ref + IL_0023: ldloc.0 + IL_0024: ldc.i4.1 + IL_0025: add + IL_0026: stloc.0 + IL_0027: ldloca.s V_2 + IL_0029: call "bool System.Collections.Generic.List.Enumerator.MoveNext()" + IL_002e: brtrue.s IL_0017 + IL_0030: leave.s IL_0040 + } + finally + { + IL_0032: ldloca.s V_2 + IL_0034: constrained. "System.Collections.Generic.List.Enumerator" + IL_003a: callvirt "void System.IDisposable.Dispose()" + IL_003f: endfinally + } + IL_0040: ldloc.1 + IL_0041: ret + } + """); + } + + [Fact] + public void SingleSpread_ListToIEnumerable_Covariant() + { + var source = """ + using System; + using System.Collections.Generic; + + foreach (var item in C.M(["a"])) + Console.Write(item); + + class C + { + public static IEnumerable M(List list) => [..list]; + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "a"); + verifier.VerifyIL("C.M", """ + { + // Code size 71 (0x47) + .maxstack 3 + .locals init (int V_0, + object[] V_1, + System.Collections.Generic.List.Enumerator V_2, + string V_3) + IL_0000: ldarg.0 + IL_0001: ldc.i4.0 + IL_0002: stloc.0 + IL_0003: dup + IL_0004: callvirt "int System.Collections.Generic.List.Count.get" + IL_0009: newarr "object" + IL_000e: stloc.1 + IL_000f: callvirt "System.Collections.Generic.List.Enumerator System.Collections.Generic.List.GetEnumerator()" + IL_0014: stloc.2 + .try + { + IL_0015: br.s IL_0027 + IL_0017: ldloca.s V_2 + IL_0019: call "string System.Collections.Generic.List.Enumerator.Current.get" + IL_001e: stloc.3 + IL_001f: ldloc.1 + IL_0020: ldloc.0 + IL_0021: ldloc.3 + IL_0022: stelem.ref + IL_0023: ldloc.0 + IL_0024: ldc.i4.1 + IL_0025: add + IL_0026: stloc.0 + IL_0027: ldloca.s V_2 + IL_0029: call "bool System.Collections.Generic.List.Enumerator.MoveNext()" + IL_002e: brtrue.s IL_0017 + IL_0030: leave.s IL_0040 + } + finally + { + IL_0032: ldloca.s V_2 + IL_0034: constrained. "System.Collections.Generic.List.Enumerator" + IL_003a: callvirt "void System.IDisposable.Dispose()" + IL_003f: endfinally + } + IL_0040: ldloc.1 + IL_0041: newobj "<>z__ReadOnlyArray..ctor(object[])" + IL_0046: ret + } + """); + } + + [Fact] + public void SingleSpread_ArrayToArray_Boxing() + { + var source = """ + using System; + + Console.Write(C.M([1])[0]); + + class C + { + public static object[] M(int[] arr) => [..arr]; + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "1"); + verifier.VerifyIL("C.M", """ + { + // Code size 48 (0x30) + .maxstack 3 + .locals init (int V_0, + object[] V_1, + int[] V_2, + int V_3, + int V_4) + IL_0000: ldarg.0 + IL_0001: ldc.i4.0 + IL_0002: stloc.0 + IL_0003: dup + IL_0004: ldlen + IL_0005: conv.i4 + IL_0006: newarr "object" + IL_000b: stloc.1 + IL_000c: stloc.2 + IL_000d: ldc.i4.0 + IL_000e: stloc.3 + IL_000f: br.s IL_0028 + IL_0011: ldloc.2 + IL_0012: ldloc.3 + IL_0013: ldelem.i4 + IL_0014: stloc.s V_4 + IL_0016: ldloc.1 + IL_0017: ldloc.0 + IL_0018: ldloc.s V_4 + IL_001a: box "int" + IL_001f: stelem.ref + IL_0020: ldloc.0 + IL_0021: ldc.i4.1 + IL_0022: add + IL_0023: stloc.0 + IL_0024: ldloc.3 + IL_0025: ldc.i4.1 + IL_0026: add + IL_0027: stloc.3 + IL_0028: ldloc.3 + IL_0029: ldloc.2 + IL_002a: ldlen + IL_002b: conv.i4 + IL_002c: blt.s IL_0011 + IL_002e: ldloc.1 + IL_002f: ret + } + """); + } + + [Fact] + public void SingleSpread_ArrayToIEnumerable_Covariant() + { + var source = """ + using System; + using System.Collections.Generic; + + foreach (var item in C.M(["a"])) + Console.Write(item); + + class C + { + public static IEnumerable M(string[] arr) => [..arr]; + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "a"); + verifier.VerifyIL("C.M", """ + { + // Code size 12 (0xc) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call "object[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0006: newobj "<>z__ReadOnlyArray..ctor(object[])" + IL_000b: ret + } + """); + } + + [Fact] + public void SingleSpread_IEnumerableToIEnumerable_Covariant() + { + var source = """ + using System; + using System.Collections.Generic; + + foreach (var item in C.M(["a"])) + Console.Write(item); + + class C + { + public static IEnumerable M(IEnumerable enumerable) => [..enumerable]; + } + """; + + // Note: We could use the Linq ToArray method here and save an allocation compared to making a List. + // However, it's not that significant, since the current codegen doesn't redundantly copy the elements themselves. + var verifier = CompileAndVerify(source, expectedOutput: "a"); + verifier.VerifyIL("C.M", """ + { + // Code size 18 (0x12) + .maxstack 3 + IL_0000: newobj "System.Collections.Generic.List..ctor()" + IL_0005: dup + IL_0006: ldarg.0 + IL_0007: callvirt "void System.Collections.Generic.List.AddRange(System.Collections.Generic.IEnumerable)" + IL_000c: newobj "<>z__ReadOnlyList..ctor(System.Collections.Generic.List)" + IL_0011: ret + } + """); + } + + [Fact] + public void MultipleSpreads_WellKnownMemberMissing() + { + var source = """ + class C + { + static void Main() + { + int[] arr = [1, 2]; + arr.Report(); + int[] arr1 = [..arr, ..arr]; + arr1.Report(); + } } """; @@ -35547,7 +35761,7 @@ static void Main() verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { - // Code size 65 (0x41) + // Code size 57 (0x39) .maxstack 3 .locals init (int[] V_0, //arr System.ReadOnlySpan V_1) @@ -35563,16 +35777,13 @@ .locals init (int[] V_0, //arr IL_0019: ldloca.s V_1 IL_001b: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" IL_0020: ldloc.0 - IL_0021: newobj "System.ReadOnlySpan..ctor(int[])" - IL_0026: stloc.1 - IL_0027: ldloca.s V_1 - IL_0029: call "int[] System.ReadOnlySpan.ToArray()" - IL_002e: newobj "System.Span..ctor(int[])" - IL_0033: call "System.ReadOnlySpan System.Span.op_Implicit(System.Span)" - IL_0038: stloc.1 - IL_0039: ldloca.s V_1 - IL_003b: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" - IL_0040: ret + IL_0021: call "int[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0026: newobj "System.Span..ctor(int[])" + IL_002b: call "System.ReadOnlySpan System.Span.op_Implicit(System.Span)" + IL_0030: stloc.1 + IL_0031: ldloca.s V_1 + IL_0033: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_0038: ret } """); } @@ -35598,7 +35809,7 @@ static void Main() verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { - // Code size 56 (0x38) + // Code size 48 (0x30) .maxstack 3 .locals init (System.ReadOnlySpan V_0) IL_0000: ldc.i4.3 @@ -35611,14 +35822,11 @@ .locals init (System.ReadOnlySpan V_0) IL_0017: stloc.0 IL_0018: ldloca.s V_0 IL_001a: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" - IL_001f: newobj "System.ReadOnlySpan..ctor(int[])" - IL_0024: stloc.0 - IL_0025: ldloca.s V_0 - IL_0027: call "int[] System.ReadOnlySpan.ToArray()" - IL_002c: newobj "<>z__ReadOnlyArray..ctor(int[])" - IL_0031: ldc.i4.0 - IL_0032: call "void CollectionExtensions.Report(object, bool)" - IL_0037: ret + IL_001f: call "int[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0024: newobj "<>z__ReadOnlyArray..ctor(int[])" + IL_0029: ldc.i4.0 + IL_002a: call "void CollectionExtensions.Report(object, bool)" + IL_002f: ret } """); } @@ -36613,6 +36821,226 @@ .locals init (System.Collections.Generic.List V_0, """); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74615")] + public void Array_SingleSpread_CustomCollection_NotICollectionAndStructEnumerator() + { + var source = """ + using System.Collections; + using System.Collections.Generic; + + class MyCollection(List list) : IEnumerable + { + public Enumerator GetEnumerator() => new(list.GetEnumerator()); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public int Count => list.Count; + + public struct Enumerator(List.Enumerator enumerator) : IEnumerator + { + public int Current => enumerator.Current; + + object IEnumerator.Current => Current; + + public bool MoveNext() => enumerator.MoveNext(); + + public void Dispose() => enumerator.Dispose(); + + public void Reset() { } + } + } + + class C + { + static void Main() + { + M(new([1, 2, 3])).Report(); + } + + static int[] M(MyCollection c) => [..c]; + } + """; + + var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3],", verify: Verification.Skipped); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("C.M", """ + { + // Code size 66 (0x42) + .maxstack 3 + .locals init (int V_0, + int[] V_1, + MyCollection.Enumerator V_2, + int V_3) + IL_0000: ldarg.0 + IL_0001: ldc.i4.0 + IL_0002: stloc.0 + IL_0003: dup + IL_0004: callvirt "int MyCollection.Count.get" + IL_0009: newarr "int" + IL_000e: stloc.1 + IL_000f: callvirt "MyCollection.Enumerator MyCollection.GetEnumerator()" + IL_0014: stloc.2 + .try + { + IL_0015: br.s IL_0027 + IL_0017: ldloca.s V_2 + IL_0019: call "int MyCollection.Enumerator.Current.get" + IL_001e: stloc.3 + IL_001f: ldloc.1 + IL_0020: ldloc.0 + IL_0021: ldloc.3 + IL_0022: stelem.i4 + IL_0023: ldloc.0 + IL_0024: ldc.i4.1 + IL_0025: add + IL_0026: stloc.0 + IL_0027: ldloca.s V_2 + IL_0029: call "bool MyCollection.Enumerator.MoveNext()" + IL_002e: brtrue.s IL_0017 + IL_0030: leave.s IL_0040 + } + finally + { + IL_0032: ldloca.s V_2 + IL_0034: constrained. "MyCollection.Enumerator" + IL_003a: callvirt "void System.IDisposable.Dispose()" + IL_003f: endfinally + } + IL_0040: ldloc.1 + IL_0041: ret + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74615")] + public void Array_SingleSpread_CustomCollection_NotICollectionAndStructEnumerator_NoCountProperty() + { + var source = """ + using System.Collections; + using System.Collections.Generic; + + class MyCollection(List list) : IEnumerable + { + public Enumerator GetEnumerator() => new(list.GetEnumerator()); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public struct Enumerator(List.Enumerator enumerator) : IEnumerator + { + public int Current => enumerator.Current; + + object IEnumerator.Current => Current; + + public bool MoveNext() => enumerator.MoveNext(); + + public void Dispose() => enumerator.Dispose(); + + public void Reset() { } + } + } + + class C + { + static void Main() + { + M(new([1, 2, 3])).Report(); + } + + static int[] M(MyCollection c) => [..c]; + } + """; + + var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3],", verify: Verification.Skipped); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("C.M", """ + { + // Code size 62 (0x3e) + .maxstack 2 + .locals init (System.Collections.Generic.List V_0, + MyCollection.Enumerator V_1, + int V_2) + IL_0000: newobj "System.Collections.Generic.List..ctor()" + IL_0005: stloc.0 + IL_0006: ldarg.0 + IL_0007: callvirt "MyCollection.Enumerator MyCollection.GetEnumerator()" + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_001e + IL_000f: ldloca.s V_1 + IL_0011: call "int MyCollection.Enumerator.Current.get" + IL_0016: stloc.2 + IL_0017: ldloc.0 + IL_0018: ldloc.2 + IL_0019: callvirt "void System.Collections.Generic.List.Add(int)" + IL_001e: ldloca.s V_1 + IL_0020: call "bool MyCollection.Enumerator.MoveNext()" + IL_0025: brtrue.s IL_000f + IL_0027: leave.s IL_0037 + } + finally + { + IL_0029: ldloca.s V_1 + IL_002b: constrained. "MyCollection.Enumerator" + IL_0031: callvirt "void System.IDisposable.Dispose()" + IL_0036: endfinally + } + IL_0037: ldloc.0 + IL_0038: callvirt "int[] System.Collections.Generic.List.ToArray()" + IL_003d: ret + } + """); + } + + [Fact] + public void SingleSpread_ToArray_MustLowerSpreadOperand() + { + var source = """ + using System; + using System.Collections.Generic; + + class C + { + static Action[] M1() + { + return [..new[] { () => Console.Write(1) }]; + } + + static Action[] M2() + { + return [..new List { () => Console.Write(2) }]; + } + + static Action[] M3() + { + return [..new Span(new[] { () => Console.Write(3) })]; + } + + static Action[] M4() + { + return [..new ReadOnlySpan(new[] { () => Console.Write(4) })]; + } + + static void Main() + { + M1()[0](); + M2()[0](); + M3()[0](); + M4()[0](); + } + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: IncludeExpectedOutput("1234"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); + verifier.VerifyDiagnostics(); + } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74615")] public void List_SingleSpread_CustomCollection_NotICollectionAndStructEnumerator_MissingICollectionOfTType() { diff --git a/src/Compilers/Core/Portable/WellKnownMember.cs b/src/Compilers/Core/Portable/WellKnownMember.cs index 8970c644528ed..33ece21e2439e 100644 --- a/src/Compilers/Core/Portable/WellKnownMember.cs +++ b/src/Compilers/Core/Portable/WellKnownMember.cs @@ -625,6 +625,7 @@ internal enum WellKnownMember System_Runtime_CompilerServices_ParamCollectionAttribute__ctor, System_Linq_Enumerable__ToList, + System_Linq_Enumerable__ToArray, System_Linq_Expressions_Expression__ArrayIndex_Expression_Expression, System_Linq_Expressions_Expression__ArrayIndex_Expression_Expressions, diff --git a/src/Compilers/Core/Portable/WellKnownMembers.cs b/src/Compilers/Core/Portable/WellKnownMembers.cs index 4bf9c0c9905f6..37cf53ebccdb4 100644 --- a/src/Compilers/Core/Portable/WellKnownMembers.cs +++ b/src/Compilers/Core/Portable/WellKnownMembers.cs @@ -4352,6 +4352,17 @@ static WellKnownMembers() 1, (byte)SignatureTypeCode.GenericMethodParameter, 0, + // System_Linq_Enumerable__ToArray + (byte)(MemberFlags.Method | MemberFlags.Static), // Flags + (byte)WellKnownType.System_Linq_Enumerable, // DeclaringTypeId + 1, // Arity + 1, // Method Signature + (byte)SignatureTypeCode.SZArray, (byte)SignatureTypeCode.GenericMethodParameter, 0, // Return Type + (byte)SignatureTypeCode.GenericTypeInstance, + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Collections_Generic_IEnumerable_T, + 1, + (byte)SignatureTypeCode.GenericMethodParameter, 0, + // System_Linq_Expressions_Expression__ArrayIndex_Expression_Expression, (byte)(MemberFlags.Method | MemberFlags.Static), // Flags (byte)WellKnownType.System_Linq_Expressions_Expression, // DeclaringTypeId @@ -5706,6 +5717,7 @@ static WellKnownMembers() "AddRange", // System_Collections_Generic_List_T__AddRange ".ctor", // System_Runtime_CompilerServices_ParamCollectionAttribute__ctor "ToList", // System_Linq_Enumerable__ToList + "ToArray", // System_Linq_Enumerable__ToArray "ArrayIndex", // System_Linq_Expressions_Expression__ArrayIndex_Expression_Expression "ArrayIndex", // System_Linq_Expressions_Expression__ArrayIndex_Expression_Expressions "Constant", // System_Linq_Expressions_Expression__Constant From 80dd9f689615babcb8131fae64afe8fd6543cc5a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 15 Nov 2024 12:01:10 -0800 Subject: [PATCH 390/508] Add docs --- .../RawStringLiteral/RawStringLiteralCommandHandler_Return.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs index bc7d71210c537..5bb3322b273f6 100644 --- a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs +++ b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs @@ -92,6 +92,7 @@ bool ExecuteReturnCommandBeforeQuoteCharacter() if (quotesAfter < 3) return false; + // Looks promising based on text alone. Now ensure we're actually on a raw string token/expression. var parsedDocument = ParsedDocument.CreateSynchronously(document, cancellationToken); var token = parsedDocument.Root.FindToken(position); From 9f81d7b21f8664c6f4a75adfa2976fa3e7df6685 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 15 Nov 2024 12:04:27 -0800 Subject: [PATCH 391/508] Handle empty case --- .../RawStringLiteralCommandHandler_Return.cs | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs index 5bb3322b273f6..d972c17c5fd72 100644 --- a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs +++ b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs @@ -171,13 +171,18 @@ bool MakeEdit( var openingEnd = GetEndPositionOfOpeningDelimiter(token, isEmpty); - // We're always at least inserting one new line on enter. - var insertedLinesBeforeCaret = 1; if (isEmpty) { - // If the literal is empty, we just want to help the user transform it into a multiline raw string literal - // with the extra empty newline between the delimiters to place the caret at + // If the literal is empty, we just want to help the user transform it into a multiline raw string + // literal with the extra empty newline between the delimiters to place the caret at edit.Insert(position, newLine + newLine + indentation); + + var snapshot = edit.Apply(); + + // move caret to the right location in virtual space for the blank line we added. + var lineInNewSnapshot = snapshot.GetLineFromPosition(position); + var nextLine = snapshot.GetLineFromLineNumber(lineInNewSnapshot.LineNumber + 1); + textView.Caret.MoveTo(new VirtualSnapshotPoint(nextLine, indentation.Length)); } else { @@ -195,19 +200,20 @@ bool MakeEdit( edit.Insert(position, newLineAndIndentation); // Also add a newline at the start of the text, only if there is text before the caret's position + var insertedLinesBeforeCaret = 1; if (openingEnd != position) { insertedLinesBeforeCaret++; edit.Insert(openingEnd, newLineAndIndentation); } - } - var snapshot = edit.Apply(); + var snapshot = edit.Apply(); - // move caret: - var lineInNewSnapshot = snapshot.GetLineFromPosition(openingEnd); - var nextLine = snapshot.GetLineFromLineNumber(lineInNewSnapshot.LineNumber + insertedLinesBeforeCaret); - textView.Caret.MoveTo(new VirtualSnapshotPoint(nextLine, indentation.Length)); + // move caret: + var lineInNewSnapshot = snapshot.GetLineFromPosition(openingEnd); + var nextLine = snapshot.GetLineFromLineNumber(lineInNewSnapshot.LineNumber + insertedLinesBeforeCaret); + textView.Caret.MoveTo(new VirtualSnapshotPoint(nextLine, indentation.Length)); + } transaction?.Complete(); return true; From c66154b091dd4835b4dafa74505734c876078ddb Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 15 Nov 2024 12:12:46 -0800 Subject: [PATCH 392/508] In progress --- .../RawStringLiteralCommandHandler_Return.cs | 64 +++++++------------ 1 file changed, 23 insertions(+), 41 deletions(-) diff --git a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs index d972c17c5fd72..6ceb5afbc98c8 100644 --- a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs +++ b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs @@ -169,19 +169,17 @@ bool MakeEdit( CSharpEditorResources.Split_raw_string, textView, _undoHistoryRegistry, _editorOperationsFactoryService); var edit = subjectBuffer.CreateEdit(); - var openingEnd = GetEndPositionOfOpeningDelimiter(token, isEmpty); - if (isEmpty) { // If the literal is empty, we just want to help the user transform it into a multiline raw string // literal with the extra empty newline between the delimiters to place the caret at edit.Insert(position, newLine + newLine + indentation); - var snapshot = edit.Apply(); + var finalSnapshot = edit.Apply(); // move caret to the right location in virtual space for the blank line we added. - var lineInNewSnapshot = snapshot.GetLineFromPosition(position); - var nextLine = snapshot.GetLineFromLineNumber(lineInNewSnapshot.LineNumber + 1); + var lineInNewSnapshot = finalSnapshot.GetLineFromPosition(position); + var nextLine = finalSnapshot.GetLineFromLineNumber(lineInNewSnapshot.LineNumber + 1); textView.Caret.MoveTo(new VirtualSnapshotPoint(nextLine, indentation.Length)); } else @@ -193,7 +191,7 @@ bool MakeEdit( var newLineAndIndentation = newLine + indentation; // Add a newline at the position of the end literal - var closingStart = GetStartPositionOfClosingDelimiter(token); + var closingStart = GetStartPositionOfClosingDelimiter(currentSnapshot, token); edit.Insert(closingStart, newLineAndIndentation); // Add a newline at the caret's position, to insert the newline that the user requested @@ -201,17 +199,18 @@ bool MakeEdit( // Also add a newline at the start of the text, only if there is text before the caret's position var insertedLinesBeforeCaret = 1; + var openingEnd = GetEndPositionOfOpeningDelimiter(currentSnapshot, token); if (openingEnd != position) { - insertedLinesBeforeCaret++; + insertedLinesBeforeCaret = 2; edit.Insert(openingEnd, newLineAndIndentation); } - var snapshot = edit.Apply(); + var finalSnapshot = edit.Apply(); // move caret: - var lineInNewSnapshot = snapshot.GetLineFromPosition(openingEnd); - var nextLine = snapshot.GetLineFromLineNumber(lineInNewSnapshot.LineNumber + insertedLinesBeforeCaret); + var lineInNewSnapshot = finalSnapshot.GetLineFromPosition(openingEnd); + var nextLine = finalSnapshot.GetLineFromLineNumber(lineInNewSnapshot.LineNumber + insertedLinesBeforeCaret); textView.Caret.MoveTo(new VirtualSnapshotPoint(nextLine, indentation.Length)); } @@ -220,14 +219,12 @@ bool MakeEdit( } } - private static int GetEndPositionOfOpeningDelimiter(SyntaxToken currentStringLiteralToken, bool isEmpty) + private static int GetEndPositionOfOpeningDelimiter(ITextSnapshot snapshot, SyntaxToken currentStringLiteralToken) { switch (currentStringLiteralToken.Kind()) { case SyntaxKind.SingleLineRawStringLiteralToken: - case SyntaxKind.MultiLineRawStringLiteralToken: { - var text = currentStringLiteralToken.Text; var tokenSpan = currentStringLiteralToken.Span; var tokenStart = tokenSpan.Start; var length = tokenSpan.Length; @@ -242,7 +239,7 @@ private static int GetEndPositionOfOpeningDelimiter(SyntaxToken currentStringLit // where in the worst case a single quote is missing in the end // So for example, """""" is an emtpy raw string literal where the user intends to delimit it // with 3 double quotes, where the contents would be placed after the first 3 quotes only - var quotes = isEmpty ? index / 2 : index; + var quotes = index; return tokenStart + quotes; } @@ -286,54 +283,39 @@ private static int GetEndPositionOfOpeningDelimiter(SyntaxToken currentStringLit } } - private static int GetStartPositionOfClosingDelimiter(SyntaxToken currentStringLiteralToken) + private static int GetStartPositionOfClosingDelimiter(ITextSnapshot snapshot, SyntaxToken stringLiteralToken) { - switch (currentStringLiteralToken.Kind()) + switch (stringLiteralToken.Kind()) { case SyntaxKind.SingleLineRawStringLiteralToken: - case SyntaxKind.MultiLineRawStringLiteralToken: { - var text = currentStringLiteralToken.Text; - var tokenSpan = currentStringLiteralToken.Span; - var tokenStart = tokenSpan.Start; - var index = tokenSpan.Length - 1; - // Traverse through the literal's text from the end to discover the first position that is not a double quote - while (index > 0) - { - var c = text[index]; - if (c != '"') - return tokenStart + index + 1; + var index = stringLiteralToken.Span.End; + + // Traverse backwards through the through the literal's text from the end to discover the first position that is not a double quote + while (snapshot[index - 1] == '"') index--; - } - // We have evaluated an empty raw string literal here and so we split the continuous double quotes into the start and end delimiters - return tokenStart + tokenSpan.Length / 2; + return index; } case SyntaxKind.InterpolatedStringTextToken: case SyntaxKind.OpenBraceToken: case SyntaxKind.CloseBraceToken: - var tokenParent = currentStringLiteralToken.Parent?.Parent; - if (tokenParent is not InterpolatedStringExpressionSyntax interpolatedStringExpression) - { - Contract.Fail("This token should only be contained in an interpolated string expression syntax"); - return -1; - } - + var interpolatedStringExpression = (InterpolatedStringExpressionSyntax)stringLiteralToken.GetRequiredParent().GetRequiredParent(); return interpolatedStringExpression.StringEndToken.SpanStart; case SyntaxKind.InterpolatedRawStringEndToken: - return currentStringLiteralToken.SpanStart; + return stringLiteralToken.SpanStart; // This represents the case of a seemingly empty single-line interpolated raw string literal // looking like this: $"""""", where all the quotes are parsed as the start delimiter // We handle this as an empty interpolated string, so we return the index at where the text would begin case SyntaxKind.InterpolatedSingleLineRawStringStartToken: { - var firstQuoteOffset = currentStringLiteralToken.Text.IndexOf('"'); - var length = currentStringLiteralToken.Span.Length; + var firstQuoteOffset = stringLiteralToken.Text.IndexOf('"'); + var length = stringLiteralToken.Span.Length; var quotes = length - firstQuoteOffset; - return currentStringLiteralToken.SpanStart + firstQuoteOffset + quotes / 2; + return stringLiteralToken.SpanStart + firstQuoteOffset + quotes / 2; } default: From b7fd7e7fc8e6927591cd198ac67dc920917b49fc Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 15 Nov 2024 12:20:07 -0800 Subject: [PATCH 393/508] delete --- .../RawStringLiteralCommandHandler_Return.cs | 142 ++++-------------- 1 file changed, 28 insertions(+), 114 deletions(-) diff --git a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs index 6ceb5afbc98c8..066986bdf3442 100644 --- a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs +++ b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs @@ -104,7 +104,7 @@ SyntaxKind.InterpolatedSingleLineRawStringStartToken or return false; } - return MakeEdit(parsedDocument, token, preferredIndentationToken: token, isEmpty); + return MakeEdit(parsedDocument, token.Parent as ExpressionSyntax, isEmpty); } bool ExecuteReturnCommandNotBeforeQuoteCharacter() @@ -118,10 +118,11 @@ bool ExecuteReturnCommandNotBeforeQuoteCharacter() var parsedDocument = ParsedDocument.CreateSynchronously(document, cancellationToken); var token = parsedDocument.Root.FindToken(position); - var preferredIndentationToken = token; + ExpressionSyntax? expression; switch (token.Kind()) { case SyntaxKind.SingleLineRawStringLiteralToken: + expression = token.Parent as ExpressionSyntax; break; case SyntaxKind.InterpolatedStringTextToken: @@ -132,36 +133,30 @@ bool ExecuteReturnCommandNotBeforeQuoteCharacter() return false; } - if (token.Kind() is SyntaxKind.OpenBraceToken) - { - // If we are not at the start of the interpolation braces, we do not intend to handle converting the raw string - // into a new one - if (position != token.SpanStart) - return false; - - // We prefer the indentation options of the string start delimiter because the indentation of the interpolation - // is empty and thus we cannot properly indent the lines that we insert - preferredIndentationToken = interpolated.StringStartToken; - } + if (token.Kind() is SyntaxKind.OpenBraceToken && position != token.SpanStart) + return false; + expression = interpolated; break; default: return false; } - return MakeEdit(parsedDocument, token, preferredIndentationToken, isEmpty: false); + return MakeEdit(parsedDocument, expression, isEmpty: false); } bool MakeEdit( ParsedDocument parsedDocument, - SyntaxToken token, - SyntaxToken preferredIndentationToken, + ExpressionSyntax? expression, bool isEmpty) { + if (expression is null) + return false; + var project = document.Project; var indentationOptions = subjectBuffer.GetIndentationOptions(_editorOptionsService, project.GetFallbackAnalyzerOptions(), project.Services, explicitFormat: false); - var indentation = preferredIndentationToken.GetPreferredIndentation(parsedDocument, indentationOptions, cancellationToken); + var indentation = expression.GetFirstToken().GetPreferredIndentation(parsedDocument, indentationOptions, cancellationToken); var newLine = indentationOptions.FormattingOptions.NewLine; @@ -191,7 +186,7 @@ bool MakeEdit( var newLineAndIndentation = newLine + indentation; // Add a newline at the position of the end literal - var closingStart = GetStartPositionOfClosingDelimiter(currentSnapshot, token); + var closingStart = GetStartPositionOfClosingDelimiter(expression); edit.Insert(closingStart, newLineAndIndentation); // Add a newline at the caret's position, to insert the newline that the user requested @@ -199,7 +194,7 @@ bool MakeEdit( // Also add a newline at the start of the text, only if there is text before the caret's position var insertedLinesBeforeCaret = 1; - var openingEnd = GetEndPositionOfOpeningDelimiter(currentSnapshot, token); + var openingEnd = GetEndPositionOfOpeningDelimiter(expression); if (openingEnd != position) { insertedLinesBeforeCaret = 2; @@ -217,110 +212,29 @@ bool MakeEdit( transaction?.Complete(); return true; } - } - private static int GetEndPositionOfOpeningDelimiter(ITextSnapshot snapshot, SyntaxToken currentStringLiteralToken) - { - switch (currentStringLiteralToken.Kind()) + int GetStartPositionOfClosingDelimiter(ExpressionSyntax expression) { - case SyntaxKind.SingleLineRawStringLiteralToken: - { - var tokenSpan = currentStringLiteralToken.Span; - var tokenStart = tokenSpan.Start; - var length = tokenSpan.Length; - // Traverse through the literal's text to discover the first position that is not a double quote - var index = 0; - while (index < length) - { - var c = text[index]; - if (c != '"') - { - // If the literal is empty, we expect a continuous segment of double quotes - // where in the worst case a single quote is missing in the end - // So for example, """""" is an emtpy raw string literal where the user intends to delimit it - // with 3 double quotes, where the contents would be placed after the first 3 quotes only - var quotes = index; - return tokenStart + quotes; - } - - index++; - } - - // We have evaluated an emtpy raw string literal here and so we split the continuous double quotes into the start and end delimiters - Contract.ThrowIfFalse(isEmpty); - return tokenStart + length / 2; - } + if (expression is InterpolatedStringExpressionSyntax interpolatedStringExpression) + return interpolatedStringExpression.StringEndToken.Span.Start; - case SyntaxKind.InterpolatedStringTextToken: - case SyntaxKind.OpenBraceToken: - case SyntaxKind.CloseBraceToken: - var tokenParent = currentStringLiteralToken.Parent?.Parent; - if (tokenParent is not InterpolatedStringExpressionSyntax interpolatedStringExpression) - { - Contract.Fail("This token should only be contained in an interpolated string expression syntax"); - return -1; - } - - return interpolatedStringExpression.StringStartToken.Span.End; + var index = expression.Span.End; + while (currentSnapshot[index - 1] == '"') + index--; - case SyntaxKind.InterpolatedRawStringEndToken: - return currentStringLiteralToken.SpanStart; - - // This represents the case of a seemingly empty single-line interpolated raw string literal - // looking like this: $"""""", where all the quotes are parsed as the start delimiter - // We handle this as an empty interpolated string, so we return the index at where the text would begin - case SyntaxKind.InterpolatedSingleLineRawStringStartToken: - { - var firstQuoteOffset = currentStringLiteralToken.Text.IndexOf('"'); - var length = currentStringLiteralToken.Span.Length; - var quotes = length - firstQuoteOffset; - return currentStringLiteralToken.SpanStart + firstQuoteOffset + quotes / 2; - } - - default: - Contract.Fail("This should only be triggered on a known raw string literal kind"); - return -1; + return index; } - } - private static int GetStartPositionOfClosingDelimiter(ITextSnapshot snapshot, SyntaxToken stringLiteralToken) - { - switch (stringLiteralToken.Kind()) + int GetEndPositionOfOpeningDelimiter(ExpressionSyntax expression) { - case SyntaxKind.SingleLineRawStringLiteralToken: - { - var index = stringLiteralToken.Span.End; - - // Traverse backwards through the through the literal's text from the end to discover the first position that is not a double quote - while (snapshot[index - 1] == '"') - index--; - - return index; - } - - case SyntaxKind.InterpolatedStringTextToken: - case SyntaxKind.OpenBraceToken: - case SyntaxKind.CloseBraceToken: - var interpolatedStringExpression = (InterpolatedStringExpressionSyntax)stringLiteralToken.GetRequiredParent().GetRequiredParent(); - return interpolatedStringExpression.StringEndToken.SpanStart; - - case SyntaxKind.InterpolatedRawStringEndToken: - return stringLiteralToken.SpanStart; + if (expression is InterpolatedStringExpressionSyntax interpolatedStringExpression) + return interpolatedStringExpression.StringStartToken.Span.End; - // This represents the case of a seemingly empty single-line interpolated raw string literal - // looking like this: $"""""", where all the quotes are parsed as the start delimiter - // We handle this as an empty interpolated string, so we return the index at where the text would begin - case SyntaxKind.InterpolatedSingleLineRawStringStartToken: - { - var firstQuoteOffset = stringLiteralToken.Text.IndexOf('"'); - var length = stringLiteralToken.Span.Length; - var quotes = length - firstQuoteOffset; - return stringLiteralToken.SpanStart + firstQuoteOffset + quotes / 2; - } + var index = expression.Span.Start; + while (currentSnapshot[index] == '"') + index++; - default: - Contract.Fail("This should only be triggered on a known raw string literal kind"); - return -1; + return index; } } } From 72fd45f10a37c0a277fe55c283ca1d043724cd2e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 15 Nov 2024 12:23:01 -0800 Subject: [PATCH 394/508] simplify --- .../RawStringLiteralCommandHandler_Return.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs index 066986bdf3442..9cfc0e446848a 100644 --- a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs +++ b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs @@ -127,8 +127,13 @@ bool ExecuteReturnCommandNotBeforeQuoteCharacter() case SyntaxKind.InterpolatedStringTextToken: case SyntaxKind.OpenBraceToken: - if (token.Parent?.Parent is not InterpolatedStringExpressionSyntax interpolated || - interpolated.StringStartToken.Kind() is not SyntaxKind.InterpolatedSingleLineRawStringStartToken) + if (token is not + { + Parent.Parent: InterpolatedStringExpressionSyntax + { + StringStartToken.RawKind: (int)SyntaxKind.InterpolatedSingleLineRawStringStartToken, + } interpolatedStringExpression, + }) { return false; } @@ -136,7 +141,7 @@ bool ExecuteReturnCommandNotBeforeQuoteCharacter() if (token.Kind() is SyntaxKind.OpenBraceToken && position != token.SpanStart) return false; - expression = interpolated; + expression = interpolatedStringExpression; break; default: From 82684be77ad8524ce5d2097f333bee832b381202 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 15 Nov 2024 12:31:16 -0800 Subject: [PATCH 395/508] simplify --- .../RawStringLiteralCommandHandler_Return.cs | 22 ++++++++++--------- .../RawStringLiteralCommandHandlerTests.cs | 12 +++++----- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs index 9cfc0e446848a..228e068cc533c 100644 --- a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs +++ b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs @@ -85,6 +85,11 @@ bool ExecuteReturnCommandBeforeQuoteCharacter() quotesBefore++; } + // We support two cases here. Something simple like `"""$$"""`. In this case, we have to be hitting enter + // inside balanced quotes. But we also support `"""goo$$"""`. In this case it's ok if quotes are not + // balanced. We're going to go through the non-empty path involving adding multiple newlines to the final + // text. + var isEmpty = quotesBefore > 0; if (isEmpty && quotesAfter != quotesBefore) return false; @@ -99,12 +104,13 @@ bool ExecuteReturnCommandBeforeQuoteCharacter() if (token.Kind() is not (SyntaxKind.SingleLineRawStringLiteralToken or SyntaxKind.MultiLineRawStringLiteralToken or SyntaxKind.InterpolatedSingleLineRawStringStartToken or - SyntaxKind.InterpolatedMultiLineRawStringStartToken)) + SyntaxKind.InterpolatedMultiLineRawStringStartToken) || + token.Parent is not ExpressionSyntax expression) { return false; } - return MakeEdit(parsedDocument, token.Parent as ExpressionSyntax, isEmpty); + return MakeEdit(parsedDocument, expression, isEmpty: true); } bool ExecuteReturnCommandNotBeforeQuoteCharacter() @@ -118,11 +124,11 @@ bool ExecuteReturnCommandNotBeforeQuoteCharacter() var parsedDocument = ParsedDocument.CreateSynchronously(document, cancellationToken); var token = parsedDocument.Root.FindToken(position); - ExpressionSyntax? expression; + ExpressionSyntax expression; switch (token.Kind()) { - case SyntaxKind.SingleLineRawStringLiteralToken: - expression = token.Parent as ExpressionSyntax; + case SyntaxKind.SingleLineRawStringLiteralToken when token.Parent is ExpressionSyntax parentExpression: + expression = parentExpression; break; case SyntaxKind.InterpolatedStringTextToken: @@ -153,12 +159,9 @@ bool ExecuteReturnCommandNotBeforeQuoteCharacter() bool MakeEdit( ParsedDocument parsedDocument, - ExpressionSyntax? expression, + ExpressionSyntax expression, bool isEmpty) { - if (expression is null) - return false; - var project = document.Project; var indentationOptions = subjectBuffer.GetIndentationOptions(_editorOptionsService, project.GetFallbackAnalyzerOptions(), project.Services, explicitFormat: false); var indentation = expression.GetFirstToken().GetPreferredIndentation(parsedDocument, indentationOptions, cancellationToken); @@ -208,7 +211,6 @@ bool MakeEdit( var finalSnapshot = edit.Apply(); - // move caret: var lineInNewSnapshot = finalSnapshot.GetLineFromPosition(openingEnd); var nextLine = finalSnapshot.GetLineFromLineNumber(lineInNewSnapshot.LineNumber + insertedLinesBeforeCaret); textView.Caret.MoveTo(new VirtualSnapshotPoint(nextLine, indentation.Length)); diff --git a/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs b/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs index 4d91c5f425cac..64fe211bae3a3 100644 --- a/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs +++ b/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs @@ -687,7 +687,7 @@ public void TestDoNotGrowEmptyInsideSixQuotesWhenNotInMiddle1() #region grow delimiters [WpfFact] - public void TestGrowDelimetersWhenEndExists_SingleLine() + public void TestGrowDelimitersWhenEndExists_SingleLine() { using var testState = RawStringLiteralTestState.CreateTestState( """" @@ -702,7 +702,7 @@ public void TestGrowDelimetersWhenEndExists_SingleLine() } [WpfFact] - public void TestGrowDelimetersWhenEndExists_MultiLine() + public void TestGrowDelimitersWhenEndExists_MultiLine() { using var testState = RawStringLiteralTestState.CreateTestState( """" @@ -721,8 +721,10 @@ public void TestGrowDelimetersWhenEndExists_MultiLine() } [WpfFact] - public void TestGrowDelimetersWhenEndExists_Interpolated() + public void TestGrowDelimitersWhenEndExists_Interpolated() { + // Delimiter is right + // Delimiter is not. using var testState = RawStringLiteralTestState.CreateTestState( """" var v = $"""$$ @@ -740,7 +742,7 @@ public void TestGrowDelimetersWhenEndExists_Interpolated() } [WpfFact] - public void TestDoNotGrowDelimetersWhenEndNotThere() + public void TestDoNotGrowDelimitersWhenEndNotThere() { using var testState = RawStringLiteralTestState.CreateTestState( @"var v = """"""$$"); @@ -751,7 +753,7 @@ public void TestDoNotGrowDelimetersWhenEndNotThere() } [WpfFact] - public void TestDoNotGrowDelimetersWhenEndTooShort() + public void TestDoNotGrowDelimitersWhenEndTooShort() { using var testState = RawStringLiteralTestState.CreateTestState( """" From aa7a3488687925265e6771ff710ae583ea7d5aea Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 15 Nov 2024 12:31:43 -0800 Subject: [PATCH 396/508] simplify --- .../RawStringLiteral/RawStringLiteralCommandHandler_Return.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs index 228e068cc533c..22c871fa7b1b8 100644 --- a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs +++ b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs @@ -110,7 +110,7 @@ SyntaxKind.InterpolatedSingleLineRawStringStartToken or return false; } - return MakeEdit(parsedDocument, expression, isEmpty: true); + return MakeEdit(parsedDocument, expression, isEmpty); } bool ExecuteReturnCommandNotBeforeQuoteCharacter() From 1ae11709952945257dbc92f6fa2b0bbf1d61e382 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 15 Nov 2024 12:34:31 -0800 Subject: [PATCH 397/508] Add tests --- .../RawStringLiteralCommandHandlerTests.cs | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs b/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs index 64fe211bae3a3..1ecaffdbc0682 100644 --- a/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs +++ b/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs @@ -403,7 +403,34 @@ before text } [WpfFact] - public void TestReturnInsideInterpolationInRawString() + public void TestReturnAfterInterpolationOpenBraceInRawString() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = $"""before text{0}$$following text"""; + """"); + + testState.SendReturn(handled: true); + testState.AssertCodeIs( + """" + var v = $""" + before text{0} + $$following text + """; + """"); + } + + [WpfFact] + public void TestReturnInsideInterpolationInRawString1() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = $"""before text{$$0} following text"""; + """"); + + testState.SendReturn(handled: false); + } + + [WpfFact] + public void TestReturnInsideInterpolationInRawString2() { using var testState = RawStringLiteralTestState.CreateTestState("""" var v = $"""before text{0$$} following text"""; From dee171d096f781da3647e8d323a1e1b5624ea077 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 15 Nov 2024 12:37:15 -0800 Subject: [PATCH 398/508] Raw strings --- .../RawStringLiteralCommandHandlerTests.cs | 59 +++++++++---------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs b/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs index 1ecaffdbc0682..8853612a0fd7d 100644 --- a/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs +++ b/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs @@ -105,7 +105,7 @@ public void TestReturnInSixQuotes() public void TestReturnInSixQuotesWithSemicolonAfter() { using var testState = RawStringLiteralTestState.CreateTestState( -@"var v = """"""$$"""""";"); + """"var v = """$$""";""""); testState.SendReturn(handled: true); testState.AssertCodeIs( @@ -188,7 +188,7 @@ public void TestReturnInSixQuotesAsArgument1() public void TestReturnInSixQuotesAsArgument2() { using var testState = RawStringLiteralTestState.CreateTestState( -@"var v = WriteLine(""""""$$"""""")"); + """"var v = WriteLine("""$$""")""""); testState.SendReturn(handled: true); testState.AssertCodeIs( @@ -203,7 +203,7 @@ public void TestReturnInSixQuotesAsArgument2() public void TestReturnInSixQuotesAsArgument3() { using var testState = RawStringLiteralTestState.CreateTestState( -@"var v = WriteLine(""""""$$"""""");"); + """"var v = WriteLine("""$$""");""""); testState.SendReturn(handled: true); testState.AssertCodeIs( @@ -275,7 +275,7 @@ public void TestReturnInSixQuotesAsArgument6() public void TestReturnInSixQuotesWithSemicolonAfter_Interpolated() { using var testState = RawStringLiteralTestState.CreateTestState( -@"var v = $""""""$$"""""";"); + """"var v = $"""$$""";""""); testState.SendReturn(handled: true); testState.AssertCodeIs( @@ -305,7 +305,7 @@ public void TestReturnInSixQuotesNotAtMiddle_Interpolated() public void TestReturnEndOfFile() { using var testState = RawStringLiteralTestState.CreateTestState( -@"var v = """"""$$"); + """"var v = """$$""""); testState.SendReturn(handled: false); } @@ -314,7 +314,7 @@ public void TestReturnEndOfFile() public void TestReturnInEmptyFile() { using var testState = RawStringLiteralTestState.CreateTestState( -@"$$"); + @"$$"); testState.SendReturn(handled: false); } @@ -527,7 +527,7 @@ public void TestReturnWithinEndQuotesInMultilineRawString() public void TestGenerateAtEndOfFile() { using var testState = RawStringLiteralTestState.CreateTestState( -@"var v = """"$$"); + """var v = ""$$"""); testState.SendTypeChar('"'); testState.AssertCodeIs( @@ -540,18 +540,18 @@ public void TestGenerateAtEndOfFile() public void TestGenerateWithSemicolonAfter() { using var testState = RawStringLiteralTestState.CreateTestState( -@"var v = """"$$;"); + """var v = ""$$;"""); testState.SendTypeChar('"'); testState.AssertCodeIs( -@"var v = """"""$$"""""";"); + """"var v = """$$""";""""); } [WpfFact] public void TestGenerateWithInterpolatedString() { using var testState = RawStringLiteralTestState.CreateTestState( -@"var v = $""""$$"); + """var v = $""$$"""); testState.SendTypeChar('"'); testState.AssertCodeIs( @@ -564,70 +564,70 @@ public void TestGenerateWithInterpolatedString() public void TestGenerateWithInterpolatedString_TwoDollarSigns() { using var testState = RawStringLiteralTestState.CreateTestState( -"""var v = $$""[||]""", withSpansOnly: true); + """var v = $$""[||]""", withSpansOnly: true); testState.SendTypeChar('"'); testState.AssertCodeIs( -"""" -var v = $$"""[||]""" -"""", withSpansOnly: true); + """" + var v = $$"""[||]""" + """", withSpansOnly: true); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/66538")] public void TestGenerateWithInterpolatedString_TwoDollarSigns_FourthDoubleQuote() { using var testState = RawStringLiteralTestState.CreateTestState( -""""var v = $$"""[||]""";"""", withSpansOnly: true); + """"var v = $$"""[||]""";"""", withSpansOnly: true); testState.SendTypeChar('"'); testState.AssertCodeIs( -"""""var v = $$""""[||]"""";""""", withSpansOnly: true); + """""var v = $$""""[||]"""";""""", withSpansOnly: true); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/66538")] public void TestGenerateWithInterpolatedString_ThreeDollarSigns() { using var testState = RawStringLiteralTestState.CreateTestState( -"""var v = $$$""[||]""", withSpansOnly: true); + """var v = $$$""[||]""", withSpansOnly: true); testState.SendTypeChar('"'); testState.AssertCodeIs( -"""" -var v = $$$"""[||]""" -"""", withSpansOnly: true); + """" + var v = $$$"""[||]""" + """", withSpansOnly: true); } [WpfFact] public void TestNoGenerateWithVerbatimString() { using var testState = RawStringLiteralTestState.CreateTestState( -@"var v = @""""$$"); + """var v = @""$$"""); testState.SendTypeChar('"'); testState.AssertCodeIs( -@"var v = @""""""$$"); + """"var v = @"""$$""""); } [WpfFact] public void TestNoGenerateWithVerbatimInterpolatedString1() { using var testState = RawStringLiteralTestState.CreateTestState( -@"var v = @$""""$$"); + """var v = @$""$$"""); testState.SendTypeChar('"'); testState.AssertCodeIs( -@"var v = @$""""""$$"); + """"var v = @$"""$$""""); } [WpfFact] public void TestNoGenerateWithVerbatimInterpolatedString2() { using var testState = RawStringLiteralTestState.CreateTestState( -@"var v = $@""""$$"); + """var v = $@""$$"""); testState.SendTypeChar('"'); testState.AssertCodeIs( -@"var v = $@""""""$$"); + """"var v = $@"""$$""""); } #endregion @@ -772,11 +772,11 @@ public void TestGrowDelimitersWhenEndExists_Interpolated() public void TestDoNotGrowDelimitersWhenEndNotThere() { using var testState = RawStringLiteralTestState.CreateTestState( -@"var v = """"""$$"); + @"var v = """"""$$"); testState.SendTypeChar('"'); testState.AssertCodeIs( -@"var v = """"""""$$"); + @"var v = """"""""$$"); } [WpfFact] @@ -803,8 +803,7 @@ public void TestDoNotGrowDelimitersWhenEndTooShort() [WpfFact] public void TestTypeQuoteEmptyFile() { - using var testState = RawStringLiteralTestState.CreateTestState( -@"$$"); + using var testState = RawStringLiteralTestState.CreateTestState(@"$$"); testState.SendTypeChar('"'); testState.AssertCodeIs( From 535ef6a26d9dfa09e1a7cd748573903513c7109d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 15 Nov 2024 12:37:55 -0800 Subject: [PATCH 399/508] Raw strings --- .../RawStringLiteralCommandHandlerTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs b/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs index 8853612a0fd7d..7b68ceee7e8d7 100644 --- a/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs +++ b/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs @@ -314,7 +314,7 @@ public void TestReturnEndOfFile() public void TestReturnInEmptyFile() { using var testState = RawStringLiteralTestState.CreateTestState( - @"$$"); + "$$"); testState.SendReturn(handled: false); } @@ -772,11 +772,11 @@ public void TestGrowDelimitersWhenEndExists_Interpolated() public void TestDoNotGrowDelimitersWhenEndNotThere() { using var testState = RawStringLiteralTestState.CreateTestState( - @"var v = """"""$$"); + """"var v = """$$""""); testState.SendTypeChar('"'); testState.AssertCodeIs( - @"var v = """"""""$$"); + """""var v = """"$$"""""); } [WpfFact] @@ -803,7 +803,7 @@ public void TestDoNotGrowDelimitersWhenEndTooShort() [WpfFact] public void TestTypeQuoteEmptyFile() { - using var testState = RawStringLiteralTestState.CreateTestState(@"$$"); + using var testState = RawStringLiteralTestState.CreateTestState("$$"); testState.SendTypeChar('"'); testState.AssertCodeIs( From c7b24ec431d9ea2396770a1c3b3c31a7aeb2d23c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 15 Nov 2024 12:38:43 -0800 Subject: [PATCH 400/508] String syntax --- .../RawStringLiteral/RawStringLiteralCommandHandlerTests.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs b/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs index 7b68ceee7e8d7..c91609f2d7a65 100644 --- a/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs +++ b/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Xml.Linq; using Microsoft.CodeAnalysis.Editor.CSharp.RawStringLiteral; @@ -30,7 +31,7 @@ public RawStringLiteralTestState(XElement workspaceElement) Single(c => c is RawStringLiteralCommandHandler); } - public static RawStringLiteralTestState CreateTestState(string markup, bool withSpansOnly = false) + public static RawStringLiteralTestState CreateTestState([StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, bool withSpansOnly = false) => new(GetWorkspaceXml(markup, withSpansOnly)); public static XElement GetWorkspaceXml(string markup, bool withSpansOnly) @@ -45,7 +46,7 @@ public static XElement GetWorkspaceXml(string markup, bool withSpansOnly) """); } - internal void AssertCodeIs(string expectedCode, bool withSpansOnly = false) + internal void AssertCodeIs([StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string expectedCode, bool withSpansOnly = false) { if (withSpansOnly) expectedCode = expectedCode.Replace("$", "\uD7FF"); From 950023701d9d6c9d9c9084df4a1627ba2b938b58 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 15 Nov 2024 12:56:19 -0800 Subject: [PATCH 401/508] Do not show structure guides on raw string literals --- .../StringLiteralExpressionStructureTests.cs | 2 +- .../Structure/CSharpBlockStructureProvider.cs | 2 +- ...tringLiteralExpressionStructureProvider.cs | 38 +++++++++++-------- .../VisualBasicBlockStructureProvider.vb | 2 +- 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Structure/StringLiteralExpressionStructureTests.cs b/src/EditorFeatures/CSharpTest/Structure/StringLiteralExpressionStructureTests.cs index d2583d0062f88..51e532c65c114 100644 --- a/src/EditorFeatures/CSharpTest/Structure/StringLiteralExpressionStructureTests.cs +++ b/src/EditorFeatures/CSharpTest/Structure/StringLiteralExpressionStructureTests.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Structure; [Trait(Traits.Feature, Traits.Features.Outlining)] -public class StringLiteralExpressionStructureTests : AbstractCSharpSyntaxNodeStructureTests +public sealed class StringLiteralExpressionStructureTests : AbstractCSharpSyntaxNodeStructureTests { internal override AbstractSyntaxStructureProvider CreateProvider() => new StringLiteralExpressionStructureProvider(); diff --git a/src/Features/CSharp/Portable/Structure/CSharpBlockStructureProvider.cs b/src/Features/CSharp/Portable/Structure/CSharpBlockStructureProvider.cs index e06dd0d316c72..e918319709363 100644 --- a/src/Features/CSharp/Portable/Structure/CSharpBlockStructureProvider.cs +++ b/src/Features/CSharp/Portable/Structure/CSharpBlockStructureProvider.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Structure; -internal class CSharpBlockStructureProvider : AbstractBlockStructureProvider +internal sealed class CSharpBlockStructureProvider : AbstractBlockStructureProvider { private static ImmutableDictionary> CreateDefaultNodeProviderMap() { diff --git a/src/Features/CSharp/Portable/Structure/Providers/StringLiteralExpressionStructureProvider.cs b/src/Features/CSharp/Portable/Structure/Providers/StringLiteralExpressionStructureProvider.cs index 2e3f14d80f49d..4f73a74cf0940 100644 --- a/src/Features/CSharp/Portable/Structure/Providers/StringLiteralExpressionStructureProvider.cs +++ b/src/Features/CSharp/Portable/Structure/Providers/StringLiteralExpressionStructureProvider.cs @@ -18,35 +18,43 @@ protected override void CollectBlockSpans( BlockStructureOptions options, CancellationToken cancellationToken) { - if (node.IsKind(SyntaxKind.StringLiteralExpression) && - !node.ContainsDiagnostics && - CouldBeMultiLine()) + if (node.IsKind(SyntaxKind.StringLiteralExpression) && !node.ContainsDiagnostics) { - spans.Add(new BlockSpan( - isCollapsible: true, - textSpan: node.Span, - hintSpan: node.Span, - type: BlockTypes.Expression, - autoCollapse: true, - isDefaultCollapsed: false)); + var type = GetStringLiteralType(); + if (type != null) + { + spans.Add(new BlockSpan( + type, + isCollapsible: true, + textSpan: node.Span, + hintSpan: node.Span, + autoCollapse: true, + isDefaultCollapsed: false)); + } } return; - bool CouldBeMultiLine() + string? GetStringLiteralType() { + // We explicitly pick non-structural here as we don't want 'structure' guides shown for raw string literals. + // We already have a specialized tagger for those showing the user the left side of it. So having a + // structure guide as well is redundant. if (node.Token.Kind() is SyntaxKind.MultiLineRawStringLiteralToken or SyntaxKind.Utf8MultiLineRawStringLiteralToken) - return true; + return BlockTypes.Nonstructural; if (node.Token.IsVerbatimStringLiteral()) { var span = node.Span; var sourceText = node.SyntaxTree.GetText(cancellationToken); - return sourceText.Lines.GetLineFromPosition(span.Start).LineNumber != - sourceText.Lines.GetLineFromPosition(span.End).LineNumber; + if (sourceText.Lines.GetLineFromPosition(span.Start).LineNumber != + sourceText.Lines.GetLineFromPosition(span.End).LineNumber) + { + return BlockTypes.Expression; + } } - return false; + return null; } } } diff --git a/src/Features/VisualBasic/Portable/Structure/VisualBasicBlockStructureProvider.vb b/src/Features/VisualBasic/Portable/Structure/VisualBasicBlockStructureProvider.vb index 27f1be67f2a92..68801154fcc2b 100644 --- a/src/Features/VisualBasic/Portable/Structure/VisualBasicBlockStructureProvider.vb +++ b/src/Features/VisualBasic/Portable/Structure/VisualBasicBlockStructureProvider.vb @@ -7,7 +7,7 @@ Imports Microsoft.CodeAnalysis.Structure Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Structure - Friend Class VisualBasicBlockStructureProvider + Friend NotInheritable Class VisualBasicBlockStructureProvider Inherits AbstractBlockStructureProvider Public Shared Function CreateDefaultNodeStructureProviderMap() As ImmutableDictionary(Of Type, ImmutableArray(Of AbstractSyntaxStructureProvider)) From 20bc1a4239318acc25834674c5c530422e9a07b9 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Tue, 12 Nov 2024 17:41:15 -0800 Subject: [PATCH 402/508] Add server side support for refreshing source generated files --- .../Diagnostics/DiagnosticsPullCache.cs | 2 +- .../VersionedPullCache.CacheItem.cs | 2 +- .../PullHandlers/VersionedPullCache.cs | 8 +- .../SourceGeneratedDocumentCache.cs | 60 ++++++ .../SourceGeneratedDocumentGetTextHandler.cs | 45 +++- .../SourceGeneratedDocumentText.cs | 6 +- .../SourceGeneratorGetTextParams.cs | 8 +- .../SourceGeneratorRefreshQueue.cs | 140 +++++++++++++ .../SourceGeneratorRefreshQueueFactory.cs | 25 +++ .../Handler/SpellCheck/SpellCheckPullCache.cs | 2 +- .../SourceGeneratedDocumentTests.cs | 198 +++++++++++++++++- 11 files changed, 471 insertions(+), 25 deletions(-) create mode 100644 src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentCache.cs create mode 100644 src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorRefreshQueue.cs create mode 100644 src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorRefreshQueueFactory.cs diff --git a/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticsPullCache.cs b/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticsPullCache.cs index 41e00efaeae52..2d1b001f6f095 100644 --- a/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticsPullCache.cs +++ b/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticsPullCache.cs @@ -24,7 +24,7 @@ internal record struct DiagnosticsRequestState(Project Project, int GlobalStateV /// and works well for us in the normal case. The latter still allows us to reuse diagnostics when changes happen that /// update the version stamp but not the content (for example, forking LSP text). /// - private sealed class DiagnosticsPullCache(string uniqueKey) : VersionedPullCache<(int globalStateVersion, VersionStamp? dependentVersion), (int globalStateVersion, Checksum dependentChecksum), DiagnosticsRequestState, DiagnosticData>(uniqueKey) + private sealed class DiagnosticsPullCache(string uniqueKey) : VersionedPullCache<(int globalStateVersion, VersionStamp? dependentVersion), (int globalStateVersion, Checksum dependentChecksum), DiagnosticsRequestState, ImmutableArray>(uniqueKey) { public override async Task<(int globalStateVersion, VersionStamp? dependentVersion)> ComputeCheapVersionAsync(DiagnosticsRequestState state, CancellationToken cancellationToken) { diff --git a/src/LanguageServer/Protocol/Handler/PullHandlers/VersionedPullCache.CacheItem.cs b/src/LanguageServer/Protocol/Handler/PullHandlers/VersionedPullCache.CacheItem.cs index 4c7abb5d4ba86..6def0f52d7f81 100644 --- a/src/LanguageServer/Protocol/Handler/PullHandlers/VersionedPullCache.CacheItem.cs +++ b/src/LanguageServer/Protocol/Handler/PullHandlers/VersionedPullCache.CacheItem.cs @@ -52,7 +52,7 @@ private sealed class CacheItem(string uniqueKey) /// /// Returns if the previousPullResult can be re-used, otherwise returns a new resultId and the new data associated with it. /// - public async Task<(string, ImmutableArray)?> UpdateCacheItemAsync( + public async Task<(string, TComputedData)?> UpdateCacheItemAsync( VersionedPullCache cache, PreviousPullResult? previousPullResult, bool isFullyLoaded, diff --git a/src/LanguageServer/Protocol/Handler/PullHandlers/VersionedPullCache.cs b/src/LanguageServer/Protocol/Handler/PullHandlers/VersionedPullCache.cs index 740ee258b2a3c..aacaeca1cdc14 100644 --- a/src/LanguageServer/Protocol/Handler/PullHandlers/VersionedPullCache.cs +++ b/src/LanguageServer/Protocol/Handler/PullHandlers/VersionedPullCache.cs @@ -4,7 +4,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; -using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; @@ -19,7 +18,6 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler; /// with different computation costs to determine if the previous cached data is still valid. /// internal abstract partial class VersionedPullCache(string uniqueKey) - where TComputedData : notnull { /// /// Map of workspace and diagnostic source to the data used to make the last pull report. @@ -59,9 +57,9 @@ internal abstract partial class VersionedPullCache. /// - public abstract Task> ComputeDataAsync(TState state, CancellationToken cancellationToken); + public abstract Task ComputeDataAsync(TState state, CancellationToken cancellationToken); - public abstract Checksum ComputeChecksum(ImmutableArray data); + public abstract Checksum ComputeChecksum(TComputedData data); /// /// If results have changed since the last request this calculates and returns a new @@ -70,7 +68,7 @@ internal abstract partial class VersionedPullCachea map of roslyn document or project id to the previous result the client sent us for that doc. /// the id of the project or document that we are checking to see if it has changed. /// Null when results are unchanged, otherwise returns a non-null new resultId. - public async Task<(string ResultId, ImmutableArray Data)?> GetOrComputeNewDataAsync( + public async Task<(string ResultId, TComputedData Data)?> GetOrComputeNewDataAsync( Dictionary idToClientLastResult, ProjectOrDocumentId projectOrDocumentId, Project project, diff --git a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentCache.cs b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentCache.cs new file mode 100644 index 0000000000000..07844957981fc --- /dev/null +++ b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentCache.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Composition; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SourceGenerators; + +internal record struct SourceGeneratedDocumentGetTextState(Document Document); + +internal class SourceGeneratedDocumentCache(string uniqueKey) : VersionedPullCache<(SourceGeneratorExecutionVersion, VersionStamp), object?, SourceGeneratedDocumentGetTextState, SourceText?>(uniqueKey), ILspService +{ + public override async Task<(SourceGeneratorExecutionVersion, VersionStamp)> ComputeCheapVersionAsync(SourceGeneratedDocumentGetTextState state, CancellationToken cancellationToken) + { + // The execution version and the dependent version must be considered as one version cached together - + // it is not correct to say that if the execution version is the same then we can re-use results (as in automatic mode the execution version never changes). + var executionVersion = state.Document.Project.Solution.GetSourceGeneratorExecutionVersion(state.Document.Project.Id); + var dependentVersion = await state.Document.Project.GetDependentVersionAsync(cancellationToken).ConfigureAwait(false); + return (executionVersion, dependentVersion); + } + + public override Task ComputeExpensiveVersionAsync(SourceGeneratedDocumentGetTextState state, CancellationToken cancellationToken) + { + return SpecializedTasks.Null(); + } + + public override Checksum ComputeChecksum(SourceText? data) + { + return data is null ? Checksum.Null : Checksum.From(data.GetChecksum()); + } + + public override async Task ComputeDataAsync(SourceGeneratedDocumentGetTextState state, CancellationToken cancellationToken) + { + // When a user has a open source-generated file, we ensure that the contents in the LSP snapshot match the contents that we + // get through didOpen/didChanges, like any other file. That way operations in LSP file are in sync with the + // contents the user has. However in this case, we don't want to look at that frozen text, but look at what the + // generator would generate if we ran it again. Otherwise, we'll get "stuck" and never update the file with something new. + var unfrozenDocument = await state.Document.Project.Solution.WithoutFrozenSourceGeneratedDocuments().GetDocumentAsync(state.Document.Id, includeSourceGenerated: true, cancellationToken).ConfigureAwait(false); + return unfrozenDocument == null + ? null + : await unfrozenDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); + } +} + +[ExportCSharpVisualBasicLspServiceFactory(typeof(SourceGeneratedDocumentCache)), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class SourceGeneratedDocumentCacheFactory() : ILspServiceFactory +{ + public ILspService CreateILspService(LspServices lspServices, WellKnownLspServerKinds serverKind) + { + return new SourceGeneratedDocumentCache(this.GetType().Name); + } +} diff --git a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentGetTextHandler.cs b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentGetTextHandler.cs index 31402aaa61f25..70406a8547cae 100644 --- a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentGetTextHandler.cs +++ b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentGetTextHandler.cs @@ -3,14 +3,16 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Composition; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; using Roslyn.Utilities; using LSP = Roslyn.LanguageServer.Protocol; -namespace Microsoft.CodeAnalysis.LanguageServer.Handler; +namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SourceGenerators; [ExportCSharpVisualBasicStatelessLspService(typeof(SourceGeneratedDocumentGetTextHandler)), Shared] [Method(MethodName)] @@ -29,18 +31,45 @@ public async Task HandleRequestAsync(SourceGenerato { var document = context.Document; + if (document is null) + { + // The source generated file being asked about is not present. + // This is a rare case the request queue always gives us a frozen, non-null document for any opened sg document, + // even if the generator itself was removed and the document no longer exists in the host solution. + // + // We can only get a null document here if the sg document has not been opened and + // the source generated document does not exist in the workspace. + // + // Return a value indicating that the document is removed. + return new SourceGeneratedDocumentText(ResultId: null, Text: null); + } + // Nothing here strictly prevents this from working on any other document, but we'll assert we got a source-generated file, since // it wouldn't really make sense for the server to be asked for the contents of a regular file. Since this endpoint is intended for // source-generated files only, this would indicate that something else has gone wrong. Contract.ThrowIfFalse(document is SourceGeneratedDocument); - // When a user has a open source-generated file, we ensure that the contents in the LSP snapshot match the contents that we - // get through didOpen/didChanges, like any other file. That way operations in LSP file are in sync with the - // contents the user has. However in this case, we don't want to look at that frozen text, but look at what the - // generator would generate if we ran it again. Otherwise, we'll get "stuck" and never update the file with something new. - document = await document.Project.Solution.WithoutFrozenSourceGeneratedDocuments().GetDocumentAsync(document.Id, includeSourceGenerated: true, cancellationToken).ConfigureAwait(false); + var cache = context.GetRequiredLspService(); + var projectOrDocument = new ProjectOrDocumentId(document.Id); + + var previousPullResults = new Dictionary(); + if (request.ResultId is not null) + { + previousPullResults.Add(projectOrDocument, new PreviousPullResult(request.ResultId, request.TextDocument)); + } + + var newResult = await cache.GetOrComputeNewDataAsync(previousPullResults, projectOrDocument, document.Project, new SourceGeneratedDocumentGetTextState(document), cancellationToken).ConfigureAwait(false); - var text = document != null ? await document.GetTextAsync(cancellationToken).ConfigureAwait(false) : null; - return new SourceGeneratedDocumentText(text?.ToString()); + if (newResult is null) + { + Contract.ThrowIfNull(request.ResultId, "Attempted to reuse cache entry but given no resultId"); + // The generated document is the same, we can return the same resultId. + return new SourceGeneratedDocumentText(request.ResultId, null); + } + else + { + var data = newResult.Value.Data?.ToString(); + return new SourceGeneratedDocumentText(newResult.Value.ResultId, data); + } } } diff --git a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentText.cs b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentText.cs index 17c1105a0870e..192c50b73bfb4 100644 --- a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentText.cs +++ b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentText.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -6,4 +6,6 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler; -internal sealed record SourceGeneratedDocumentText([property: JsonPropertyName("text")] string? Text); \ No newline at end of file +internal sealed record SourceGeneratedDocumentText( + [property: JsonPropertyName("resultId")] string? ResultId, + [property: JsonPropertyName("text")] string? Text); diff --git a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorGetTextParams.cs b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorGetTextParams.cs index f930f1cc438c0..9537b6d607d2b 100644 --- a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorGetTextParams.cs +++ b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorGetTextParams.cs @@ -1,10 +1,12 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; using Roslyn.LanguageServer.Protocol; -namespace Microsoft.CodeAnalysis.LanguageServer.Handler; +namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SourceGenerators; -internal sealed record SourceGeneratorGetTextParams([property: JsonPropertyName("textDocument")] TextDocumentIdentifier TextDocument) : ITextDocumentParams; \ No newline at end of file +internal sealed record SourceGeneratorGetTextParams( + [property: JsonPropertyName("textDocument")] TextDocumentIdentifier TextDocument, + [property: JsonPropertyName("resultId")] string? ResultId) : ITextDocumentParams; diff --git a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorRefreshQueue.cs b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorRefreshQueue.cs new file mode 100644 index 0000000000000..2f3aa9ca7ab51 --- /dev/null +++ b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorRefreshQueue.cs @@ -0,0 +1,140 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Roslyn.LanguageServer.Protocol; +using Roslyn.Utilities; +using StreamJsonRpc; + +namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SourceGenerators; + +internal class SourceGeneratorRefreshQueue : + IOnInitialized, + ILspService, + IDisposable +{ + private const string RefreshSourceGeneratedDocumentName = "workspace/refreshSourceGeneratedDocument"; + + private readonly IAsynchronousOperationListener _asyncListener; + private readonly CancellationTokenSource _disposalTokenSource = new(); + private readonly LspWorkspaceRegistrationService _lspWorkspaceRegistrationService; + private readonly LspWorkspaceManager _lspWorkspaceManager; + private readonly IClientLanguageServerManager _notificationManager; + private readonly AsyncBatchingWorkQueue _refreshQueue; + + public SourceGeneratorRefreshQueue( + IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider, + LspWorkspaceRegistrationService lspWorkspaceRegistrationService, + LspWorkspaceManager lspWorkspaceManager, + IClientLanguageServerManager notificationManager) + { + _lspWorkspaceRegistrationService = lspWorkspaceRegistrationService; + _lspWorkspaceManager = lspWorkspaceManager; + _notificationManager = notificationManager; + _asyncListener = asynchronousOperationListenerProvider.GetListener(FeatureAttribute.SourceGenerators); + + // Batch up workspace notifications so that we only send a notification to refresh source generated files + // every 2 seconds - long enough to avoid spamming the client with notifications, but short enough to refresh + // the source generated files relatively frequently. + _refreshQueue = _refreshQueue = new AsyncBatchingWorkQueue( + delay: TimeSpan.FromMilliseconds(2000), + processBatchAsync: RefreshSourceGeneratedDocumentsAsync, + asyncListener: _asyncListener, + _disposalTokenSource.Token); + } + + public Task OnInitializedAsync(ClientCapabilities clientCapabilities, RequestContext context, CancellationToken cancellationToken) + { + if (clientCapabilities.HasVisualStudioLspCapability()) + { + // VS source generated document content is not provided by LSP. + return Task.CompletedTask; + } + + // After we have initialized we can start listening for workspace changes. + _lspWorkspaceRegistrationService.LspSolutionChanged += OnLspSolutionChanged; + return Task.CompletedTask; + } + + private void OnLspSolutionChanged(object? sender, WorkspaceChangeEventArgs e) + { + var projectId = e.ProjectId ?? e.DocumentId?.ProjectId; + if (projectId is not null) + { + // We have a specific changed project - do some additional checks to see if + // source generators possibly changed. Note that this overreports actual + // changes to the source generated text; we rely on resultIds in the text retrieval to avoid unnecessary serialization. + + // Trivial check. see if the SG version of these projects changed. If so, we definitely want to update + // this generated file. + if (e.OldSolution.GetSourceGeneratorExecutionVersion(projectId) != + e.NewSolution.GetSourceGeneratorExecutionVersion(projectId)) + { + _refreshQueue.AddWork(); + return; + } + + var oldProject = e.OldSolution.GetProject(projectId); + var newProject = e.NewSolution.GetProject(projectId); + + if (oldProject != null && newProject != null) + { + var asyncToken = _asyncListener.BeginAsyncOperation($"{nameof(SourceGeneratorRefreshQueue)}.{nameof(OnLspSolutionChanged)}"); + CheckDependentVersionsAsync(oldProject, newProject, _disposalTokenSource.Token).CompletesAsyncOperation(asyncToken); + } + } + else + { + // We don't have a specific project change - if this is a solution change we need to queue a refresh anyway. + if (e.Kind is WorkspaceChangeKind.SolutionChanged or WorkspaceChangeKind.SolutionAdded or WorkspaceChangeKind.SolutionRemoved or WorkspaceChangeKind.SolutionReloaded or WorkspaceChangeKind.SolutionCleared) + { + _refreshQueue.AddWork(); + } + } + + async Task CheckDependentVersionsAsync(Project oldProject, Project newProject, CancellationToken cancellationToken) + { + if (await oldProject.GetDependentVersionAsync(cancellationToken).ConfigureAwait(false) != + await newProject.GetDependentVersionAsync(cancellationToken).ConfigureAwait(false)) + { + _refreshQueue.AddWork(); + } + } + } + + private ValueTask RefreshSourceGeneratedDocumentsAsync( + CancellationToken cancellationToken) + { + var hasOpenSourceGeneratedDocuments = _lspWorkspaceManager.GetTrackedLspText().Keys.Any(uri => uri.Scheme == SourceGeneratedDocumentUri.Scheme); + if (!hasOpenSourceGeneratedDocuments) + { + // There are no opened source generated documents - we don't need to bother asking the client to refresh anything. + return ValueTaskFactory.CompletedTask; + } + + try + { + return _notificationManager.SendNotificationAsync(RefreshSourceGeneratedDocumentName, cancellationToken); + } + catch (Exception ex) when (ex is ObjectDisposedException or ConnectionLostException) + { + // It is entirely possible that we're shutting down and the connection is lost while we're trying to send a notification + // as this runs outside of the guaranteed ordering in the queue. We can safely ignore this exception. + } + + return ValueTaskFactory.CompletedTask; + } + + public void Dispose() + { + _lspWorkspaceRegistrationService.LspSolutionChanged -= OnLspSolutionChanged; + _disposalTokenSource.Cancel(); + _disposalTokenSource.Dispose(); + } +} diff --git a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorRefreshQueueFactory.cs b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorRefreshQueueFactory.cs new file mode 100644 index 0000000000000..4b8531d0bf5ff --- /dev/null +++ b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorRefreshQueueFactory.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Composition; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Shared.TestHooks; + +namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SourceGenerators; + +[ExportCSharpVisualBasicLspServiceFactory(typeof(SourceGeneratorRefreshQueue)), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class SourceGeneratorRefreshQueueFactory( + IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider, + LspWorkspaceRegistrationService lspWorkspaceRegistrationService) : ILspServiceFactory +{ + public ILspService CreateILspService(LspServices lspServices, WellKnownLspServerKinds serverKind) + { + var notificationManager = lspServices.GetRequiredService(); + var lspWorkspaceManager = lspServices.GetRequiredService(); + return new SourceGeneratorRefreshQueue(asynchronousOperationListenerProvider, lspWorkspaceRegistrationService, lspWorkspaceManager, notificationManager); + } +} \ No newline at end of file diff --git a/src/LanguageServer/Protocol/Handler/SpellCheck/SpellCheckPullCache.cs b/src/LanguageServer/Protocol/Handler/SpellCheck/SpellCheckPullCache.cs index 33df968335b8e..d997635b6cb60 100644 --- a/src/LanguageServer/Protocol/Handler/SpellCheck/SpellCheckPullCache.cs +++ b/src/LanguageServer/Protocol/Handler/SpellCheck/SpellCheckPullCache.cs @@ -16,7 +16,7 @@ internal record struct SpellCheckState(ISpellCheckSpanService Service, Document /// Simplified version of that only uses a /// single cheap key to check results against. /// -internal sealed class SpellCheckPullCache(string uniqueKey) : VersionedPullCache<(Checksum parseOptionsChecksum, Checksum textChecksum)?, object?, SpellCheckState, SpellCheckSpan>(uniqueKey) +internal sealed class SpellCheckPullCache(string uniqueKey) : VersionedPullCache<(Checksum parseOptionsChecksum, Checksum textChecksum)?, object?, SpellCheckState, ImmutableArray>(uniqueKey) { public override async Task<(Checksum parseOptionsChecksum, Checksum textChecksum)?> ComputeCheapVersionAsync(SpellCheckState state, CancellationToken cancellationToken) { diff --git a/src/LanguageServer/ProtocolUnitTests/Workspaces/SourceGeneratedDocumentTests.cs b/src/LanguageServer/ProtocolUnitTests/Workspaces/SourceGeneratedDocumentTests.cs index 1889e398f460e..1d6ae9f07598d 100644 --- a/src/LanguageServer/ProtocolUnitTests/Workspaces/SourceGeneratedDocumentTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Workspaces/SourceGeneratedDocumentTests.cs @@ -2,13 +2,18 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageServer.Handler; +using Microsoft.CodeAnalysis.LanguageServer.Handler.SourceGenerators; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; using Roslyn.Test.Utilities.TestGenerators; +using Roslyn.Utilities; using Xunit; using Xunit.Abstractions; using LSP = Roslyn.LanguageServer.Protocol; @@ -27,7 +32,7 @@ public async Task ReturnsTextForSourceGeneratedDocument(bool mutatingLspWorkspac var sourceGeneratorDocumentUri = SourceGeneratedDocumentUri.Create(sourceGeneratedDocumentIdentity); var text = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, - new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }), CancellationToken.None); + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: null), CancellationToken.None); AssertEx.NotNull(text); Assert.Equal("// Hello, World", text.Text); @@ -43,7 +48,7 @@ public async Task OpenCloseSourceGeneratedDocument(bool mutatingLspWorkspace) var sourceGeneratorDocumentUri = SourceGeneratedDocumentUri.Create(sourceGeneratedDocumentIdentity); var text = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, - new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }), CancellationToken.None); + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: null), CancellationToken.None); AssertEx.NotNull(text); Assert.Equal("// Hello, World", text.Text); @@ -68,7 +73,7 @@ public async Task OpenMultipleSourceGeneratedDocument(bool mutatingLspWorkspace) foreach (var sourceGeneratorDocumentUri in sourceGeneratorDocumentUris) { var text = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, - new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }), CancellationToken.None); + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: null), CancellationToken.None); AssertEx.NotNull(text?.Text); await testLspServer.OpenDocumentAsync(sourceGeneratorDocumentUri, text.Text); } @@ -97,6 +102,191 @@ public async Task RequestOnSourceGeneratedDocument(bool mutatingLspWorkspace) Assert.Contains("class A", hover.Contents.Fourth.Value); } + [Theory, CombinatorialData] + public async Task ReturnsGeneratedSourceForOpenDocument(bool mutatingLspWorkspace) + { + var sourceGeneratorSource = "// Hello, World"; + await using var testLspServer = await CreateTestLspServerWithGeneratorAsync(mutatingLspWorkspace, sourceGeneratorSource); + + var sourceGeneratedDocuments = await testLspServer.GetCurrentSolution().Projects.Single().GetSourceGeneratedDocumentsAsync(); + var sourceGeneratedDocumentIdentity = sourceGeneratedDocuments.Single().Identity; + var sourceGeneratorDocumentUri = SourceGeneratedDocumentUri.Create(sourceGeneratedDocumentIdentity); + + // Open the document with different text - this will cause the queue to generate frozen sg documents using this value. + // However the get text handler should return the real source generator source. + await testLspServer.OpenDocumentAsync(sourceGeneratorDocumentUri, "LSP Open Document Text"); + + var text = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: null), CancellationToken.None); + + AssertEx.NotNull(text); + Assert.Equal(sourceGeneratorSource, text.Text); + } + + [Theory, CombinatorialData] + public async Task TestReturnsUnchangedResult(bool mutatingLspWorkspace) + { + await using var testLspServer = await CreateTestLspServerWithGeneratorAsync(mutatingLspWorkspace, "// Hello, World"); + + var sourceGeneratedDocuments = await testLspServer.GetCurrentSolution().Projects.Single().GetSourceGeneratedDocumentsAsync(); + var sourceGeneratedDocumentIdentity = sourceGeneratedDocuments.Single().Identity; + var sourceGeneratorDocumentUri = SourceGeneratedDocumentUri.Create(sourceGeneratedDocumentIdentity); + + var text = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: null), CancellationToken.None); + + AssertEx.NotNull(text); + Assert.Equal("// Hello, World", text.Text); + + // Make a second request - since nothing has changed we should get back the same resultId. + var secondRequest = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: text.ResultId), CancellationToken.None); + AssertEx.NotNull(secondRequest); + Assert.Null(secondRequest.Text); + Assert.Equal(text.ResultId, secondRequest.ResultId); + } + + [Theory, CombinatorialData] + internal async Task TestReturnsGeneratedSourceWhenDocumentChanges(bool mutatingLspWorkspace, SourceGeneratorExecutionPreference sourceGeneratorExecution) + { + await using var testLspServer = await CreateTestLspServerAsync(string.Empty, mutatingLspWorkspace); + + var configService = testLspServer.TestWorkspace.ExportProvider.GetExportedValue(); + configService.Options = new WorkspaceConfigurationOptions(SourceGeneratorExecution: sourceGeneratorExecution); + + var callCount = 0; + var generatorReference = await AddGeneratorAsync(new CallbackGenerator(() => ("hintName.cs", "// callCount: " + callCount++)), testLspServer.TestWorkspace); + + var sourceGeneratedDocuments = await testLspServer.GetCurrentSolution().Projects.Single().GetSourceGeneratedDocumentsAsync(); + var sourceGeneratedDocumentIdentity = sourceGeneratedDocuments.Single().Identity; + var sourceGeneratorDocumentUri = SourceGeneratedDocumentUri.Create(sourceGeneratedDocumentIdentity); + + var text = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: null), CancellationToken.None); + + AssertEx.NotNull(text); + Assert.Equal("// callCount: 0", text.Text); + + // Modify a normal document in the workspace. + // In automatic mode this should trigger generators to re-run. + // In balanced mode generators should not re-run. + await testLspServer.TestWorkspace.ChangeDocumentAsync(testLspServer.TestWorkspace.Documents.Single(d => !d.IsSourceGenerated).Id, SourceText.From("new text")); + await WaitForSourceGeneratorsAsync(testLspServer.TestWorkspace); + + // Ask for the source generated text again. + var secondRequest = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: text.ResultId), CancellationToken.None); + + if (sourceGeneratorExecution == SourceGeneratorExecutionPreference.Automatic) + { + // We should get newly generated text + AssertEx.NotNull(secondRequest); + Assert.NotEqual(text.ResultId, secondRequest.ResultId); + Assert.Equal("// callCount: 1", secondRequest.Text); + } + else + { + // We should get an unchanged result + AssertEx.NotNull(secondRequest); + Assert.Equal(text.ResultId, secondRequest.ResultId); + Assert.Null(secondRequest.Text); + } + } + + [Theory, CombinatorialData] + internal async Task TestReturnsGeneratedSourceWhenManuallyRefreshed(bool mutatingLspWorkspace, SourceGeneratorExecutionPreference sourceGeneratorExecution) + { + await using var testLspServer = await CreateTestLspServerAsync(string.Empty, mutatingLspWorkspace); + + var configService = testLspServer.TestWorkspace.ExportProvider.GetExportedValue(); + configService.Options = new WorkspaceConfigurationOptions(SourceGeneratorExecution: sourceGeneratorExecution); + + var callCount = 0; + var generatorReference = await AddGeneratorAsync(new CallbackGenerator(() => ("hintName.cs", "// callCount: " + callCount++)), testLspServer.TestWorkspace); + + var sourceGeneratedDocuments = await testLspServer.GetCurrentSolution().Projects.Single().GetSourceGeneratedDocumentsAsync(); + var sourceGeneratedDocumentIdentity = sourceGeneratedDocuments.Single().Identity; + var sourceGeneratorDocumentUri = SourceGeneratedDocumentUri.Create(sourceGeneratedDocumentIdentity); + + var text = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: null), CancellationToken.None); + + AssertEx.NotNull(text); + Assert.Equal("// callCount: 0", text.Text); + + // Updating the execution version should trigger source generators to run in both automatic and balanced mode. + testLspServer.TestWorkspace.EnqueueUpdateSourceGeneratorVersion(projectId: null, forceRegeneration: true); + await WaitForSourceGeneratorsAsync(testLspServer.TestWorkspace); + + var secondRequest = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: text.ResultId), CancellationToken.None); + AssertEx.NotNull(secondRequest); + Assert.NotEqual(text.ResultId, secondRequest.ResultId); + Assert.Equal("// callCount: 1", secondRequest.Text); + } + + [Theory, CombinatorialData] + public async Task TestReturnsNullForRemovedClosedGeneratedFile(bool mutatingLspWorkspace) + { + var generatorText = "// Hello, World"; + await using var testLspServer = await CreateTestLspServerAsync(string.Empty, mutatingLspWorkspace); + var generatorReference = await AddGeneratorAsync(new SingleFileTestGenerator(generatorText), testLspServer.TestWorkspace); + + var sourceGeneratedDocuments = await testLspServer.GetCurrentSolution().Projects.Single().GetSourceGeneratedDocumentsAsync(); + var sourceGeneratedDocumentIdentity = sourceGeneratedDocuments.Single().Identity; + var sourceGeneratorDocumentUri = SourceGeneratedDocumentUri.Create(sourceGeneratedDocumentIdentity); + + var text = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: null), CancellationToken.None); + AssertEx.NotNull(text); + Assert.Equal("// Hello, World", text.Text); + + // Remove the generator and verify that we get null text back. + await RemoveGeneratorAsync(generatorReference, testLspServer.TestWorkspace); + + var secondRequest = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: text.ResultId), CancellationToken.None); + + Assert.NotNull(secondRequest); + Assert.Null(secondRequest.Text); + } + + [Theory, CombinatorialData] + public async Task TestReturnsNullForRemovedOpenedGeneratedFile(bool mutatingLspWorkspace) + { + var generatorText = "// Hello, World"; + await using var testLspServer = await CreateTestLspServerAsync(string.Empty, mutatingLspWorkspace); + var generatorReference = await AddGeneratorAsync(new SingleFileTestGenerator(generatorText), testLspServer.TestWorkspace); + + var sourceGeneratedDocuments = await testLspServer.GetCurrentSolution().Projects.Single().GetSourceGeneratedDocumentsAsync(); + var sourceGeneratedDocumentIdentity = sourceGeneratedDocuments.Single().Identity; + var sourceGeneratorDocumentUri = SourceGeneratedDocumentUri.Create(sourceGeneratedDocumentIdentity); + + var text = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: null), CancellationToken.None); + AssertEx.NotNull(text); + Assert.Equal("// Hello, World", text.Text); + + // Open the document - this will cause the queue to generate frozen sg documents based on the LSP open text + // even if the source generator is removed entirely. + await testLspServer.OpenDocumentAsync(sourceGeneratorDocumentUri, text.Text); + + // Remove the generator - the handler should return null text. + await RemoveGeneratorAsync(generatorReference, testLspServer.TestWorkspace); + + var secondRequest = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: text.ResultId), CancellationToken.None); + + Assert.NotNull(secondRequest); + Assert.Null(secondRequest.Text); + } + + private static async Task WaitForSourceGeneratorsAsync(EditorTestWorkspace workspace) + { + var operations = workspace.ExportProvider.GetExportedValue(); + await operations.WaitAllAsync(workspace, [FeatureAttribute.Workspace, FeatureAttribute.SourceGenerators]); + } + private async Task CreateTestLspServerWithGeneratorAsync(bool mutatingLspWorkspace, string generatedDocumentText) { var testLspServer = await CreateTestLspServerAsync(string.Empty, mutatingLspWorkspace); From 401ec47c51cc44ad948bc6a4e03798b19a4158a7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 15 Nov 2024 13:15:14 -0800 Subject: [PATCH 403/508] Add more checks --- src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs | 6 ++++++ src/Compilers/Test/Utilities/CSharp/Extensions.cs | 4 +--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index 1024d22990f6c..74e55cf4e90fa 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -2860,6 +2860,12 @@ interface IGoo { public T Count { get; } } // latter has members for example). Assert.NotEqual(nameofType, typeofType); + Assert.True(nameofType.IsDefinition); + Assert.False(nameofType.IsUnboundGenericType()); + + Assert.False(typeofType.IsDefinition); + Assert.True(typeofType.IsUnboundGenericType()); + Assert.Empty(typeofType.GetMembers("Count")); Assert.Single(nameofType.GetMembers("Count")); diff --git a/src/Compilers/Test/Utilities/CSharp/Extensions.cs b/src/Compilers/Test/Utilities/CSharp/Extensions.cs index 6b4e838a76726..13cd405a7b597 100644 --- a/src/Compilers/Test/Utilities/CSharp/Extensions.cs +++ b/src/Compilers/Test/Utilities/CSharp/Extensions.cs @@ -764,9 +764,7 @@ public static ImmutableArray GetParameters(this ISymbol member } public static bool IsUnboundGenericType(this ITypeSymbol type) - { - return type is INamedTypeSymbol namedType && namedType.IsUnboundGenericType; - } + => type is INamedTypeSymbol { IsUnboundGenericType: true }; public static bool GivesAccessTo(this AssemblySymbol first, AssemblySymbol second) { From c3f434be3240b37ffe028ea8a70402447d7b8d4c Mon Sep 17 00:00:00 2001 From: David Barbet Date: Fri, 15 Nov 2024 13:50:10 -0800 Subject: [PATCH 404/508] review feedback --- .../SourceGeneratedDocumentCache.cs | 3 +- .../SourceGeneratedDocumentGetTextHandler.cs | 6 ++-- .../SourceGeneratedDocumentText.cs | 10 ++++++ .../SourceGeneratorRefreshQueue.cs | 35 +++++++++++-------- 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentCache.cs b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentCache.cs index 07844957981fc..4bbdc4e175db9 100644 --- a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentCache.cs +++ b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentCache.cs @@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SourceGenerators; internal record struct SourceGeneratedDocumentGetTextState(Document Document); -internal class SourceGeneratedDocumentCache(string uniqueKey) : VersionedPullCache<(SourceGeneratorExecutionVersion, VersionStamp), object?, SourceGeneratedDocumentGetTextState, SourceText?>(uniqueKey), ILspService +internal sealed class SourceGeneratedDocumentCache(string uniqueKey) : VersionedPullCache<(SourceGeneratorExecutionVersion, VersionStamp), object?, SourceGeneratedDocumentGetTextState, SourceText?>(uniqueKey), ILspService { public override async Task<(SourceGeneratorExecutionVersion, VersionStamp)> ComputeCheapVersionAsync(SourceGeneratedDocumentGetTextState state, CancellationToken cancellationToken) { @@ -41,6 +41,7 @@ public override Checksum ComputeChecksum(SourceText? data) // get through didOpen/didChanges, like any other file. That way operations in LSP file are in sync with the // contents the user has. However in this case, we don't want to look at that frozen text, but look at what the // generator would generate if we ran it again. Otherwise, we'll get "stuck" and never update the file with something new. + // This can return null when the source generated file has been removed (but the queue itself is using the frozen non-null document). var unfrozenDocument = await state.Document.Project.Solution.WithoutFrozenSourceGeneratedDocuments().GetDocumentAsync(state.Document.Id, includeSourceGenerated: true, cancellationToken).ConfigureAwait(false); return unfrozenDocument == null ? null diff --git a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentGetTextHandler.cs b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentGetTextHandler.cs index 70406a8547cae..6354c26e9b464 100644 --- a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentGetTextHandler.cs +++ b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentGetTextHandler.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; +using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; using LSP = Roslyn.LanguageServer.Protocol; @@ -52,7 +53,7 @@ public async Task HandleRequestAsync(SourceGenerato var cache = context.GetRequiredLspService(); var projectOrDocument = new ProjectOrDocumentId(document.Id); - var previousPullResults = new Dictionary(); + using var _ = PooledDictionary.GetInstance(out var previousPullResults); if (request.ResultId is not null) { previousPullResults.Add(projectOrDocument, new PreviousPullResult(request.ResultId, request.TextDocument)); @@ -64,10 +65,11 @@ public async Task HandleRequestAsync(SourceGenerato { Contract.ThrowIfNull(request.ResultId, "Attempted to reuse cache entry but given no resultId"); // The generated document is the same, we can return the same resultId. - return new SourceGeneratedDocumentText(request.ResultId, null); + return new SourceGeneratedDocumentText(request.ResultId, Text: null); } else { + // We may get no text back if the unfrozen source generated file no longer exists. var data = newResult.Value.Data?.ToString(); return new SourceGeneratedDocumentText(newResult.Value.ResultId, data); } diff --git a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentText.cs b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentText.cs index 192c50b73bfb4..10fc9991c6c93 100644 --- a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentText.cs +++ b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentText.cs @@ -6,6 +6,16 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler; +/// +/// Source generated file text result. The client uses the resultId to inform what the text value is. +/// +/// An unchanged result has a non-null resultId (same as client request resultId) + null text. +/// +/// A changed result has a new non-null resultId + possibly null text (if the sg document no longer exists). +/// +/// In rare circumstances it is possible to get a null resultId + null text - this happens when +/// the source generated document is not open AND the source generated document no longer exists +/// internal sealed record SourceGeneratedDocumentText( [property: JsonPropertyName("resultId")] string? ResultId, [property: JsonPropertyName("text")] string? Text); diff --git a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorRefreshQueue.cs b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorRefreshQueue.cs index 2f3aa9ca7ab51..e88c75b25ae9c 100644 --- a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorRefreshQueue.cs +++ b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorRefreshQueue.cs @@ -14,10 +14,10 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SourceGenerators; -internal class SourceGeneratorRefreshQueue : - IOnInitialized, - ILspService, - IDisposable +internal sealed class SourceGeneratorRefreshQueue : + IOnInitialized, + ILspService, + IDisposable { private const string RefreshSourceGeneratedDocumentName = "workspace/refreshSourceGeneratedDocument"; @@ -29,10 +29,10 @@ internal class SourceGeneratorRefreshQueue : private readonly AsyncBatchingWorkQueue _refreshQueue; public SourceGeneratorRefreshQueue( - IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider, - LspWorkspaceRegistrationService lspWorkspaceRegistrationService, - LspWorkspaceManager lspWorkspaceManager, - IClientLanguageServerManager notificationManager) + IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider, + LspWorkspaceRegistrationService lspWorkspaceRegistrationService, + LspWorkspaceManager lspWorkspaceManager, + IClientLanguageServerManager notificationManager) { _lspWorkspaceRegistrationService = lspWorkspaceRegistrationService; _lspWorkspaceManager = lspWorkspaceManager; @@ -43,7 +43,7 @@ public SourceGeneratorRefreshQueue( // every 2 seconds - long enough to avoid spamming the client with notifications, but short enough to refresh // the source generated files relatively frequently. _refreshQueue = _refreshQueue = new AsyncBatchingWorkQueue( - delay: TimeSpan.FromMilliseconds(2000), + delay: DelayTimeSpan.Idle, processBatchAsync: RefreshSourceGeneratedDocumentsAsync, asyncListener: _asyncListener, _disposalTokenSource.Token); @@ -63,6 +63,14 @@ public Task OnInitializedAsync(ClientCapabilities clientCapabilities, RequestCon } private void OnLspSolutionChanged(object? sender, WorkspaceChangeEventArgs e) + { + var asyncToken = _asyncListener.BeginAsyncOperation($"{nameof(SourceGeneratorRefreshQueue)}.{nameof(OnLspSolutionChanged)}"); + _ = OnLspSolutionChangedAsync(e) + .CompletesAsyncOperation(asyncToken) + .ReportNonFatalErrorUnlessCancelledAsync(_disposalTokenSource.Token); + } + + private async Task OnLspSolutionChangedAsync(WorkspaceChangeEventArgs e) { var projectId = e.ProjectId ?? e.DocumentId?.ProjectId; if (projectId is not null) @@ -85,8 +93,7 @@ private void OnLspSolutionChanged(object? sender, WorkspaceChangeEventArgs e) if (oldProject != null && newProject != null) { - var asyncToken = _asyncListener.BeginAsyncOperation($"{nameof(SourceGeneratorRefreshQueue)}.{nameof(OnLspSolutionChanged)}"); - CheckDependentVersionsAsync(oldProject, newProject, _disposalTokenSource.Token).CompletesAsyncOperation(asyncToken); + await CheckDependentVersionsAsync(oldProject, newProject).ConfigureAwait(false); } } else @@ -98,10 +105,10 @@ private void OnLspSolutionChanged(object? sender, WorkspaceChangeEventArgs e) } } - async Task CheckDependentVersionsAsync(Project oldProject, Project newProject, CancellationToken cancellationToken) + async Task CheckDependentVersionsAsync(Project oldProject, Project newProject) { - if (await oldProject.GetDependentVersionAsync(cancellationToken).ConfigureAwait(false) != - await newProject.GetDependentVersionAsync(cancellationToken).ConfigureAwait(false)) + if (await oldProject.GetDependentVersionAsync(_disposalTokenSource.Token).ConfigureAwait(false) != + await newProject.GetDependentVersionAsync(_disposalTokenSource.Token).ConfigureAwait(false)) { _refreshQueue.AddWork(); } From c9dd27cd17e5ec088e1501bba16eab1bf73c5fcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Fri, 15 Nov 2024 14:16:39 -0800 Subject: [PATCH 405/508] Fix UpdateBaseline failure when no valid change has been made before baseline update (#75934) --- .../EditAndContinue/DebuggingSession.cs | 13 ++-- .../EditAndContinueWorkspaceServiceTests.cs | 73 +++++++++++++------ 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs b/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs index 50950beebc570..88a1e4ee52bd1 100644 --- a/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs +++ b/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs @@ -627,13 +627,14 @@ public void UpdateBaselines(Solution solution, ImmutableArray rebuilt { foreach (var projectId in rebuiltProjects) { - _projectBaselines.Remove(projectId); - - var (metadata, pdb) = _initialBaselineModuleReaders[projectId]; - metadata.Dispose(); - pdb.Dispose(); + if (_projectBaselines.Remove(projectId)) + { + var (metadata, pdb) = _initialBaselineModuleReaders[projectId]; + metadata.Dispose(); + pdb.Dispose(); - _initialBaselineModuleReaders.Remove(projectId); + _initialBaselineModuleReaders.Remove(projectId); + } } } diff --git a/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index cbe55ba773831..f5928559c4d59 100644 --- a/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs +++ b/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs @@ -1328,8 +1328,9 @@ public async Task RudeEdits_DelayLoadedModule() EndDebuggingSession(debuggingSession); } - [Fact] - public async Task RudeEdits_UpdateBaseline() + [Theory] + [CombinatorialData] + public async Task RudeEdits_UpdateBaseline(bool validChangeBeforeRudeEdit) { var source1 = "abstract class C { }"; var source2 = "abstract class C { void F() {} }"; @@ -1348,18 +1349,32 @@ public async Task RudeEdits_UpdateBaseline() var debuggingSession = await StartDebuggingSessionAsync(service, solution); + EmitSolutionUpdateResults result; + var readers = ImmutableArray.Empty; + // change the source (valid edit): - solution = solution.WithDocumentText(documentId, CreateText(source2)); + if (validChangeBeforeRudeEdit) + { + solution = solution.WithDocumentText(documentId, CreateText(source2)); - var result = await debuggingSession.EmitSolutionUpdateAsync(solution, runningProjects: [projectId], s_noActiveSpans, CancellationToken.None); - Assert.Equal(ModuleUpdateStatus.Ready, result.ModuleUpdates.Status); - Assert.Empty(result.ProjectsToRebuild); - Assert.Empty(result.ProjectsToRestart); + result = await debuggingSession.EmitSolutionUpdateAsync(solution, runningProjects: [projectId], s_noActiveSpans, CancellationToken.None); + Assert.Equal(ModuleUpdateStatus.Ready, result.ModuleUpdates.Status); + Assert.Empty(result.ProjectsToRebuild); + Assert.Empty(result.ProjectsToRestart); - // baseline should be present: - var readers = debuggingSession.GetTestAccessor().GetBaselineModuleReaders(); - Assert.NotEmpty(readers); - Assert.True(debuggingSession.GetTestAccessor().HasProjectEmitBaseline(projectId)); + // baseline should be present: + readers = debuggingSession.GetTestAccessor().GetBaselineModuleReaders(); + Assert.NotEmpty(readers); + Assert.True(debuggingSession.GetTestAccessor().HasProjectEmitBaseline(projectId)); + + CommitSolutionUpdate(debuggingSession); + } + else + { + // baseline is not present: + Assert.Empty(debuggingSession.GetTestAccessor().GetBaselineModuleReaders()); + Assert.False(debuggingSession.GetTestAccessor().HasProjectEmitBaseline(projectId)); + } // change the source (rude edit): solution = solution.WithDocumentText(documentId, CreateText(source3)); @@ -1376,18 +1391,19 @@ public async Task RudeEdits_UpdateBaseline() AssertEx.Equal([projectId], result.ProjectsToRebuild); AssertEx.Equal([projectId], result.ProjectsToRestart); - DiscardSolutionUpdate(debuggingSession); - // restart: _debuggerService.LoadedModules.Remove(moduleId); moduleId = EmitAndLoadLibraryToDebuggee(source3, sourceFilePath: sourceFile.Path); debuggingSession.UpdateBaselines(solution, result.ProjectsToRebuild); - // baseline should be removed: - Assert.False(debuggingSession.GetTestAccessor().HasProjectEmitBaseline(projectId)); + if (validChangeBeforeRudeEdit) + { + // baseline should be removed: + Assert.False(debuggingSession.GetTestAccessor().HasProjectEmitBaseline(projectId)); - // all readers created for the module must be disposed, so we can rebuild the module: - VerifyReadersDisposed(readers); + // all readers created for the module must be disposed, so we can rebuild the module: + VerifyReadersDisposed(readers); + } // no rude edits reported: Assert.Empty(await service.GetDocumentDiagnosticsAsync(solution.GetRequiredDocument(documentId), s_noActiveSpans, CancellationToken.None)); @@ -1405,13 +1421,22 @@ public async Task RudeEdits_UpdateBaseline() EndDebuggingSession(debuggingSession); - AssertEx.SequenceEqual( - [ - "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=0|EmptySessionCount=0|HotReloadSessionCount=2|EmptyHotReloadSessionCount=1", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=1|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines={6A6F7270-0000-4000-8000-000000000000}", - "Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=2|RudeEditKind=23|RudeEditSyntaxKind=8875|RudeEditBlocking=True|RudeEditProjectId={6A6F7270-0000-4000-8000-000000000000}", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=3|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges={6A6F7270-0000-4000-8000-000000000000}|ProjectIdsWithUpdatedBaselines=" - ], _telemetryLog); + AssertEx.SequenceEqual(validChangeBeforeRudeEdit + ? [ + "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=0|EmptySessionCount=0|HotReloadSessionCount=3|EmptyHotReloadSessionCount=1", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges={6A6F7270-0000-4000-8000-000000000000}|ProjectIdsWithUpdatedBaselines=", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=3|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=1|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines={6A6F7270-0000-4000-8000-000000000000}", + "Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=3|RudeEditKind=23|RudeEditSyntaxKind=8875|RudeEditBlocking=True|RudeEditProjectId={6A6F7270-0000-4000-8000-000000000000}", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=4|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges={6A6F7270-0000-4000-8000-000000000000}|ProjectIdsWithUpdatedBaselines=", + ] + : + [ + "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=0|EmptySessionCount=0|HotReloadSessionCount=2|EmptyHotReloadSessionCount=1", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=1|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines={6A6F7270-0000-4000-8000-000000000000}", + "Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=2|RudeEditKind=23|RudeEditSyntaxKind=8875|RudeEditBlocking=True|RudeEditProjectId={6A6F7270-0000-4000-8000-000000000000}", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=3|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges={6A6F7270-0000-4000-8000-000000000000}|ProjectIdsWithUpdatedBaselines=", + ], + _telemetryLog); } [Fact] From 220538fa8d810c4b359948e3b04022d3be30ade9 Mon Sep 17 00:00:00 2001 From: Fred Silberberg Date: Fri, 15 Nov 2024 15:16:02 -0800 Subject: [PATCH 406/508] Add runtime async design doc (#75816) * Add runtime async design doc Adding an initial design document for runtime async. I'm not making a branch for this yet so I don't have to worry about keeping it up to date until we're actually ready to start implementation work; that should begin soon, but I think getting this document into `main` and starting collaboration with the runtime and debugger folks will help that proceed. * Remove incorrect IL steps * Add more examples and updates the runtime document. * Add another open question * Add more translated C# samples * Feedback and more scenarios * Remove PR link * Add note about potential hoisted local cleanup --- docs/compilers/CSharp/Runtime Async Design.md | 543 ++++++++++++++++++ 1 file changed, 543 insertions(+) create mode 100644 docs/compilers/CSharp/Runtime Async Design.md diff --git a/docs/compilers/CSharp/Runtime Async Design.md b/docs/compilers/CSharp/Runtime Async Design.md new file mode 100644 index 0000000000000..ba218eaceb8db --- /dev/null +++ b/docs/compilers/CSharp/Runtime Async Design.md @@ -0,0 +1,543 @@ +# Runtime Async Design + +See also the ECMA-335 specification change for this feature: https://github.com/dotnet/runtime/blob/main/docs/design/specs/runtime-async.md. https://github.com/dotnet/runtime/issues/109632 tracks open issues for the feature in the runtime. + +This document goes over the general design of how Roslyn works with the Runtime Async feature to produce IL. In general, we try to avoid exposing this feature at the user level; initial binding is almost entirely +unaffected by runtime async. Exposed symbols do not give direct information about whether they were compiled with runtime async, and indeed the compiler has no idea whether a method from a referenced assembly is +compiled with runtime async or not. + +## Supporting runtime apis + +We use the following runtime flag to drive whether feature can be used. This flag must be defined in the same assembly that defines `object`, and the assembly cannot reference any other assemblies. In terms of +CoreFX, this means it must be defined in the `System.Runtime` reference assembly. + +TODO: Determine whether just the presence of this flag will cause the compiler to generate in runtime async mode. + +```cs +namespace System.Runtime.CompilerServices; + +public static class RuntimeFeature +{ + public const string Async = nameof(Async); +} +``` + +We use the following helper APIs to indicate suspension points to the runtime, in addition to the runtime async call syntax: + +```cs +namespace System.Runtime.CompilerServices; + +// These methods are used to await things that cannot use runtime async signature form +// TODO: Clarify which of these should be preferred? Should we always emit the `Unsafe` version when awaiting something that implements `ICriticalNotifyCompletion`? +namespace System.Runtime.CompilerServices; + +public static class RuntimeHelpers +{ + [RuntimeAsyncMethod] + public static Task AwaitAwaiterFromRuntimeAsync(TAwaiter awaiter) where TAwaiter : INotifyCompletion; + [RuntimeAsyncMethod] + public static Task UnsafeAwaitAwaiterFromRuntimeAsync(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion; +} +``` + +Additionally, we use the following helper attributes to indicate information to the runtime. If these attributes are not present in the reference assemblies, we will generate them; the runtime matches by full +name, not by type identity, so we do not need to care about using the "canonical" versions. + +```cs +namespace System.Runtime.CompilerServices; + +// Used to tell the runtime to generate the async state machinery for this method +[AttributeUsage(AttributeTargets.Method)] +public class RuntimeAsyncMethodAttribute() : Attribute(); + +// Used to mark locals that should be hoisted to the generated async closure. Note that the runtime does not guarantee that all locals marked with this modreq will be hoisted; if it can prove that it +// doesn't need to hoist a variable, it may avoid doing so. +public class HoistedLocal(); +``` + +For experimentation purposes, we recognize an attribute that can be used to force the compiler to generate the runtime async code, or to force the compiler to generate a full state machine. This attribute is not +defined in the BCL, and exists as an escape hatch for experimentation. It may be removed when the feature ships in stable. + +```cs +namespace System.Runtime.CompilerServices; + +[AttributeUsage(AttributeTargets.Method)] +public class RuntimeAsyncMethodGenerationAttribute(bool runtimeAsync) : Attribute(); +``` + +## Transformation strategy + +As mentioned previously, we try to expose as little of this to initial binding as possible. The one major exception to this is our handling of the `RuntimeAsyncMethodAttribute`; we do not let this be applied to +user code, and will issue an error if a user tries to do this by hand. + +Compiler generated async state machines and runtime generated async share some of the same building blocks. Both need to have `await`s with in `catch` and `finally` blocks rewritten to pend the exceptions, +perform the `await` outside of the `catch`/`finally` region, and then have the exceptions restored as necessary. + +TODO: Go over `IAsyncEnumerable` and confirm that the initial rewrite to a `Task`-based method produces code that can then be implemented with runtime async, rather than a full compiler state machine. + +TODO: Clarify with the debugger team where NOPs need to be inserted for debugging/ENC scenarios. + We will likely need to insert AwaitYieldPoint and AwaitResumePoints for the scenarios where we emit calls to `RuntimeHelpers` async helpers, but can we avoid them for calls in runtime async form? + +TODO: Do we need to implement clearing of locals marked with `Hoisted`, or will the runtime handle that? + +### Example transformations + +Below are some examples of what IL is generated for specific examples. + +TODO: Include debug versions + +#### General signature transformation + +In general, an async method declared in C# will be transformed as follows: + +```cs +async Task M() +{ + // ... +} +``` + +```cs +[RuntimeAsyncMethod, Experimental] +Task M() +{ + // ... see lowering strategy for each kind of await below ... +} +``` + +The same holds for methods that return `Task`, `ValueTask`, and `ValueTask`. Any method returning a different `Task`-like type is not transformed to runtime async form and uses a C#-generated state machine. + +`await`s within the body will either be transformed to Runtime-Async call format (as detailed in the runtime specification), or we will use one of the `RuntimeHelpers` methods to do the `await`. Specifics +for given scenarios are elaborated in more detail below. + +`Experimental` will be removed when the full feature is ready to ship, likely not before .NET 11. + +TODO: Async iterators (returning `IAsyncEnumerable`) + +#### Await `Task`-returning method + +```cs +class C +{ + static Task M(); +} + +await C.M(); +``` + +```il +call modreq(class [System.Runtime]System.Threading.Tasks.Task) void C::M() +``` + +--------------------------- + +```cs +var c = new C(); +await c.M(); + +class C +{ + Task M(); +} +``` + +```il +newobj instance void C::.ctor() +callvirt instance modreq(class [System.Runtime]System.Threading.Tasks.Task) void C::M() +``` + +#### Await a concrete `T` `Task`-returning method + +```cs +int i = await C.M(); + +class C +{ + static Task M(); +} +``` + +```il +call modreq(class [System.Runtime]System.Threading.Tasks.Task`1) int32 C::M() +stloc.0 +``` + +--------------------------- + +```cs +var c = new C(); +int i = await c.M(); + +class C +{ + Task M(); +} +``` + +```il +newobj instance void C::.ctor() +callvirt instance modreq(class [System.Runtime]System.Threading.Tasks.Task`1) int32 C::M() +stloc.0 +``` + +#### Await local of type `Task` + +```cs +var local = M(); +await local; + +class C +{ + static Task M(); +} +``` + +Translated C#: + +```cs +var local = C.M(); +{ + var awaiter = local.GetAwaiter(); + if (!awaiter.IsComplete) + { + /* Runtime-Async Call */ System.Runtime.CompilerServices.RuntimeHelpers.AwaitAwaiterFromRuntimeAsync(awaiter); + } + awaiter.GetResult() +} +``` + +```il +{ + .locals init ( + [0] valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter awaiter + ) + + IL_0000: call class [System.Runtime]System.Threading.Tasks.Task C::M() + IL_0005: callvirt instance valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter [System.Runtime]System.Threading.Tasks.Task::GetAwaiter() + IL_000a: stloc.0 + IL_000b: ldloca.s 0 + IL_000d: call instance bool [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter::get_IsCompleted() + IL_0012: brtrue.s IL_001b + + IL_0014: ldloc.0 + IL_0015: call class [System.Runtime]System.Threading.Tasks.Task System.Runtime.CompilerServices.RuntimeHelpers::AwaitAwaiterFromRuntimeAsync(!!0) + IL_001a: pop + + IL_001b: ldloca.s 0 + IL_001d: call instance void [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter::GetResult() + IL_0022: ret +} +``` + +#### Await local of concrete type `Task` + +This strategy will also be used for `Task`-like types that are not `Task`, `ValueTask`, `Task`, or `ValueTask`, both in value form, and in direct method call form. We will use either `AwaitAwaiterFromRuntimeAsync` or +`UnsafeAwaitAwaiterFromRuntimeAsync`, depending on the interface implemented by the custom awaitable. + +```cs +var local = M(); +var i = await local; + +class C +{ + static Task M(); +} +``` + +Translated C#: + +```cs +var local = C.M(); +var i = +{ + var awaiter = local.GetAwaiter(); + if (!awaiter.IsComplete) + { + /* Runtime-Async Call */ System.Runtime.CompilerServices.RuntimeHelpers.AwaitAwaiterFromRuntimeAsync(awaiter); + } + awaiter.GetResult() +}; +``` + +```il +{ + .locals init ( + [0] valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1 awaiter + ) + + IL_0000: call class [System.Runtime]System.Threading.Tasks.Task`1 C::M() + IL_0005: callvirt instance valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1 class [System.Runtime]System.Threading.Tasks.Task`1::GetAwaiter() + IL_000a: stloc.0 + IL_000b: ldloca.s 0 + IL_000d: call instance bool valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1::get_IsCompleted() + IL_0012: brtrue.s IL_001b + + IL_0014: ldloc.0 + IL_0015: call class [System.Runtime]System.Threading.Tasks.Task System.Runtime.CompilerServices.RuntimeHelpers::AwaitAwaiterFromRuntimeAsync>(!!0) + IL_001a: pop + + IL_001b: ldloca.s 0 + IL_001d: call instance !0 valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1::GetResult() + IL_0022: pop + IL_0023: ret +} +``` + +#### Await a `T`-returning method + +```cs +await C.M(); + +class C +{ + static T M(); +} +``` + +```il +TODO: https://github.com/dotnet/runtime/issues/109632 +``` + +#### Await a generic `T` `Task`-returning method + +```cs +await C.M(); + +class C +{ + static Task M(); +} +``` + +```il +TODO: https://github.com/dotnet/runtime/issues/109632 +``` + +#### Await a `Task`-returning delegate + +```cs +AsyncDelegate d = C.M; +await d(); + +delegate Task AsyncDelegate(); + +class C +{ + static Task M(); +} +``` + +```il +TODO: https://github.com/dotnet/runtime/issues/109632 +``` + +#### Await a `T`-returning delegate + +```cs +Func d = C.M; +await d(); + +class C +{ + static Task M(); +} +``` + +```il +TODO: https://github.com/dotnet/runtime/issues/109632 +``` + +#### Awaiting in a `catch` block + +```cs +try +{ + throw new Exception(); +} +catch (Exception ex) +{ + await C.M(); + throw; +} + +class C +{ + static Task M(); +} +``` + +Translated C#: + +```cs +int pendingCatch = 0; +Exception pendingException; +try +{ + throw new Exception(); +} +catch (Exception e) +{ + pendingCatch = 1; + pendingException = e; +} + +if (pendingCatch == 1) +{ + /* Runtime-Async Call */ C.M(); + throw pendingException; +} +``` + +```il +{ + .locals init ( + [0] int32 pendingCatch, + [1] class [System.Runtime]System.Exception pendingException + ) + + .try + { + IL_0000: newobj instance void [System.Runtime]System.Exception::.ctor() + IL_0005: throw + } + catch [System.Runtime]System.Exception + { + IL_0006: stloc.1 + IL_0007: ldc.i4.1 + IL_0008: stloc.0 + IL_0009: leave.s IL_000b + } + + IL_000b: ldloc.0 + IL_000c: ldc.i4.1 + IL_000d: bne.un.s IL_0017 + + IL_000f: ldloc.1 + IL_0010: call modreq(class [System.Runtime]System.Threading.Tasks.Task) void C::M() + IL_0015: pop + IL_0016: throw + + IL_0017: ret +} +``` + +#### Awaiting in a `finally` block + +```cs +try +{ + throw new Exception(); +} +finally +{ + await C.M(); +} + +class C +{ + static Task M(); +} +``` + +Translated C#: + +```cs +Exception pendingException; +try +{ + throw new Exception(); +} +catch (Exception e) +{ + pendingException = e; +} + +/* Runtime-Async Call */ C.M(); + +if (pendingException != null) +{ + throw pendingException; +} +``` + +```il +{ + .locals init ( + [0] class [System.Runtime]System.Exception pendingException + ) + + .try + { + IL_0000: newobj instance void [System.Runtime]System.Exception::.ctor() + IL_0005: throw + } + catch [System.Runtime]System.Exception + { + IL_0006: stloc.0 + IL_0007: leave.s IL_0009 + } + + IL_0009: call modreq(class [System.Runtime]System.Threading.Tasks.Task) void C::M() + IL_000e: pop + IL_000f: ldloc.0 + IL_0010: brfalse.s IL_0014 + + IL_0012: ldloc.0 + IL_0013: throw + + IL_0014: ret +} +``` + +#### Preserving compound assignments + +```cs +int[] a = new int[] { }; +a[C.M2()] += await C.M1(); + +class C +{ + public static Task M1(); + public static int M2(); +} +``` + +Translated C#: + +```cs +int[] a = new int[] { }; +int _tmp1 = C.M2(); +int _tmp2 = a[_tmp1]; +int _tmp3 = /* Runtime-Async Call */ C.M1(); +a[_tmp1] = _tmp2 + _tmp3; +``` + +```il +{ + .locals init ( + [0] int32[] a, + [1] int32 _tmp1, + [2] int32 _tmp2, + [3] int32 _tmp3 + ) + + IL_0000: ldc.i4.0 + IL_0001: newarr [System.Runtime]System.Int32 + IL_0006: stloc.0 + IL_0007: call int32 C::M2() + IL_000c: stloc.1 + IL_000d: ldloc.0 + IL_000e: ldloc.1 + IL_000f: ldelem.i4 + IL_0010: stloc.2 + IL_0011: call modreq(class [System.Runtime]System.Threading.Tasks.Task) int32 C::M1() + IL_0016: stloc.3 + IL_0017: ldloc.0 + IL_0018: ldloc.1 + IL_0019: ldloc.2 + IL_001a: ldloc.3 + IL_001b: add + IL_001c: stelem.i4 + IL_001d: ret +} +``` From 75e576969108845367d43f5972ff547b9bf04a25 Mon Sep 17 00:00:00 2001 From: Fred Silberberg Date: Fri, 15 Nov 2024 15:34:50 -0800 Subject: [PATCH 407/508] Add params vs normal form test (#75937) Inspired by a test in https://github.com/dotnet/roslyn/pull/75862. --- .../Emit3/OverloadResolutionPriorityTests.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/Compilers/CSharp/Test/Emit3/OverloadResolutionPriorityTests.cs b/src/Compilers/CSharp/Test/Emit3/OverloadResolutionPriorityTests.cs index ea94309830cf6..903c60708e9c9 100644 --- a/src/Compilers/CSharp/Test/Emit3/OverloadResolutionPriorityTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/OverloadResolutionPriorityTests.cs @@ -2780,4 +2780,44 @@ public static class E2 CompileAndVerify([source, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "E1.R(int)").VerifyDiagnostics(); } + + [Fact] + public void ParamsVsNormal_01() + { + var code = """ + using System; + using System.Runtime.CompilerServices; + + M1(1); + + partial class Program + { + static void M1(int i) => throw null; + [OverloadResolutionPriority(1)] + static void M1(params int[] i) => Console.Write("params"); + } + """; + + CompileAndVerify([code, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "params").VerifyDiagnostics(); + } + + [Fact] + public void ParamsVsNormal_02() + { + var code = """ + using System; + using System.Runtime.CompilerServices; + + M1(1); + + partial class Program + { + [OverloadResolutionPriority(-1)] + static void M1(int i) => throw null; + static void M1(params int[] i) => Console.Write("params"); + } + """; + + CompileAndVerify([code, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "params").VerifyDiagnostics(); + } } From 5810b4f5cf6f31a04675b256ce037c7e738a7373 Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Fri, 15 Nov 2024 17:31:53 -0800 Subject: [PATCH 408/508] Reduce IVsFreeThreadedFileChangeEvents2.DirectoryChangedEx2 notifications from VS shell (#75815) * Reduce IVsFreeThreadedFileChangeEvents2.DirectoryChangedEx2 notifications from VS shell While looking into another FileWatcher issue, I noticed that we were getting more notifications from shell on directory changes than I would have expected. It turns out this is because we don't currently combine WatchDirectory operations as we do WatchFile/UnwatchFile/UnwatchDirectories. This is generally the desired behavior as vs shell doesn't support multiple directories being supported by a single notification. However, they do support multiple filters on the same directory, and this is exactly the case that we usualy experience when processing a batch of WatcherOperations. This PR simply allows combining of directory watch notifications for the same directory (and sink), keeping track of the combined set of filters that all requests had. Note that this PR also changed the mergability of WatchFiles by only allowing merging if the sinks are the same. I don't have context to be completely confident the prior behavior is a bug, but it sure seems wrong. --- .../LspFileChangeWatcherTests.cs | 2 +- .../FileWatching/LspFileChangeWatcher.cs | 20 +++++-- .../FileWatching/SimpleFileChangeWatcher.cs | 4 +- .../HostWorkspace/LoadedProject.cs | 6 +- .../Def/ProjectSystem/FileChangeWatcher.cs | 49 ++++++++-------- .../FileChangeWatcherTests.vb | 57 +++++++++++++++++++ ...tchedPortableExecutableReferenceFactory.cs | 2 +- .../ProjectSystem/IFileChangeWatcher.cs | 17 +++--- .../ProjectSystem/ProjectSystemProject.cs | 4 +- 9 files changed, 113 insertions(+), 48 deletions(-) create mode 100644 src/VisualStudio/Core/Test/ProjectSystemShim/FileChangeWatcherTests.vb diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs index 41fe21c197552..9e3a07e88d383 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs @@ -61,7 +61,7 @@ public async Task CreatingDirectoryWatchRequestsDirectoryWatch() var tempDirectory = tempRoot.CreateDirectory(); // Try creating a context and ensure we created the registration - var context = lspFileChangeWatcher.CreateContext([new ProjectSystem.WatchedDirectory(tempDirectory.Path, extensionFilter: null)]); + var context = lspFileChangeWatcher.CreateContext([new ProjectSystem.WatchedDirectory(tempDirectory.Path, extensionFilters: [])]); await WaitForFileWatcherAsync(testLspServer); var watcher = GetSingleFileWatcher(dynamicCapabilitiesRpcTarget); diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspFileChangeWatcher.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspFileChangeWatcher.cs index 06451af2acaa3..c21b47a23e803 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspFileChangeWatcher.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspFileChangeWatcher.cs @@ -75,13 +75,23 @@ public FileChangeContext(ImmutableArray watchedDirectories, Ls // If we have any watched directories, then watch those directories directly if (watchedDirectories.Any()) { - var directoryWatches = watchedDirectories.Select(d => new FileSystemWatcher + var directoryWatches = watchedDirectories.Select(d => { - GlobPattern = new RelativePattern + var pattern = "**/*" + d.ExtensionFilters.Length switch { - BaseUri = ProtocolConversions.CreateRelativePatternBaseUri(d.Path), - Pattern = d.ExtensionFilter is not null ? "**/*" + d.ExtensionFilter : "**/*" - } + 0 => string.Empty, + 1 => d.ExtensionFilters[0], + _ => "{" + string.Join(',', d.ExtensionFilters) + "}" + }; + + return new FileSystemWatcher + { + GlobPattern = new RelativePattern + { + BaseUri = ProtocolConversions.CreateRelativePatternBaseUri(d.Path), + Pattern = pattern + } + }; }).ToArray(); _directoryWatchRegistration = new LspFileWatchRegistration(lspFileChangeWatcher, directoryWatches); diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/SimpleFileChangeWatcher.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/SimpleFileChangeWatcher.cs index 9cc6048699756..5fefb873a7367 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/SimpleFileChangeWatcher.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/SimpleFileChangeWatcher.cs @@ -46,8 +46,8 @@ public FileChangeContext(ImmutableArray watchedDirectories) var watcher = new FileSystemWatcher(watchedDirectory.Path); watcher.IncludeSubdirectories = true; - if (watchedDirectory.ExtensionFilter != null) - watcher.Filter = '*' + watchedDirectory.ExtensionFilter; + foreach (var filter in watchedDirectory.ExtensionFilters) + watcher.Filters.Add('*' + filter); watcher.Changed += RaiseEvent; watcher.Created += RaiseEvent; diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LoadedProject.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LoadedProject.cs index c41713fb1ac6a..0cdec8f2bd4f0 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LoadedProject.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LoadedProject.cs @@ -53,11 +53,7 @@ public LoadedProject(ProjectSystemProject projectSystemProject, SolutionServices // TODO: we only should listen for add/removals here, but we can't specify such a filter now _projectDirectory = Path.GetDirectoryName(_projectFilePath)!; - _fileChangeContext = fileWatcher.CreateContext([ - new(_projectDirectory, ".cs"), - new(_projectDirectory, ".cshtml"), - new(_projectDirectory, ".razor") - ]); + _fileChangeContext = fileWatcher.CreateContext([new(_projectDirectory, [".cs", ".cshtml", ".razor"])]); _fileChangeContext.FileChanged += FileChangedContext_FileChanged; // Start watching for file changes for the project file as well diff --git a/src/VisualStudio/Core/Def/ProjectSystem/FileChangeWatcher.cs b/src/VisualStudio/Core/Def/ProjectSystem/FileChangeWatcher.cs index 41701d2faf735..8d3e56b3365fb 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/FileChangeWatcher.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/FileChangeWatcher.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.ProjectSystem; @@ -44,10 +45,10 @@ public FileChangeWatcher( { _fileChangeService = fileChangeService; - // 📝 Empirical testing during high activity (e.g. solution close) showed strong batching performance even - // though the batching delay is 0. + // 📝 Empirical testing during high activity (e.g. solution open/close) showed strong batching performance + // with delay of 500 ms, batching over 95% of the calls. _taskQueue = new AsyncBatchingWorkQueue( - TimeSpan.Zero, + TimeSpan.FromMilliseconds(500), ProcessBatchAsync, listenerProvider.GetListener(FeatureAttribute.Workspace), CancellationToken.None); @@ -98,10 +99,10 @@ private readonly struct WatcherOperation private readonly Kind _kind; /// - /// The extension filter to apply for . This value may be - /// to disable the extension filter. + /// The extension filters to apply for . This value may be + /// empty to disable the extension filter. /// - private readonly string? _filter; + private readonly ImmutableArray _filters; /// /// The file change flags to apply for . @@ -139,13 +140,13 @@ private readonly struct WatcherOperation /// private readonly OneOrMany _paths; - private WatcherOperation(Kind kind, string directory, string? filter, IVsFreeThreadedFileChangeEvents2 sink, List cookies) + private WatcherOperation(Kind kind, string directory, ImmutableArray filters, IVsFreeThreadedFileChangeEvents2 sink, List cookies) { Contract.ThrowIfFalse(kind is Kind.WatchDirectory); _kind = kind; _paths = new OneOrMany(directory); - _filter = filter; + _filters = filters.NullToEmpty(); _sink = sink; _cookies = cookies; @@ -165,7 +166,7 @@ private WatcherOperation(Kind kind, OneOrMany files, _VSFILECHANGEFLAGS _tokens = tokens; // Other watching fields are not used for this kind - _filter = null; + _filters = ImmutableArray.Empty; _cookies = null!; } @@ -177,7 +178,7 @@ private WatcherOperation(Kind kind, List cookies) _cookies = cookies; // Other watching fields are not used for this kind - _filter = null; + _filters = ImmutableArray.Empty; _fileChangeFlags = 0; _sink = null!; _tokens = OneOrMany.Empty; @@ -192,7 +193,7 @@ private WatcherOperation(Kind kind, OneOrMany tokens _tokens = tokens; // Other watching fields are not used for this kind - _filter = null; + _filters = ImmutableArray.Empty; _fileChangeFlags = 0; _sink = null!; _cookies = null!; @@ -207,8 +208,8 @@ private enum Kind UnwatchFiles, } - public static WatcherOperation WatchDirectory(string directory, string? filter, IVsFreeThreadedFileChangeEvents2 sink, List cookies) - => new(Kind.WatchDirectory, directory, filter, sink, cookies); + public static WatcherOperation WatchDirectory(string directory, ImmutableArray filters, IVsFreeThreadedFileChangeEvents2 sink, List cookies) + => new(Kind.WatchDirectory, directory, filters, sink, cookies); public static WatcherOperation WatchFile(string path, _VSFILECHANGEFLAGS fileChangeFlags, IVsFreeThreadedFileChangeEvents2 sink, Context.RegularWatchedFile token) => new(Kind.WatchFiles, OneOrMany.Create(path), fileChangeFlags, sink, OneOrMany.Create(token)); @@ -252,21 +253,15 @@ public static WatcherOperation CombineRange(ImmutableSegmentedList 0) + await service.FilterDirectoryChangesAsync(cookie, _filters.ToArray(), cancellationToken).ConfigureAwait(false); return; @@ -367,8 +363,13 @@ public Context(FileChangeWatcher fileChangeWatcher, ImmutableArray WatcherOperation.WatchDirectory(watchedDirectory.Path, watchedDirectory.ExtensionFilter, this, _directoryWatchCookies))); + var item = WatcherOperation.WatchDirectory( + watchedDirectory.Path, + watchedDirectory.ExtensionFilters, + this, + _directoryWatchCookies); + + _fileChangeWatcher._taskQueue.AddWork(item); } } diff --git a/src/VisualStudio/Core/Test/ProjectSystemShim/FileChangeWatcherTests.vb b/src/VisualStudio/Core/Test/ProjectSystemShim/FileChangeWatcherTests.vb new file mode 100644 index 0000000000000..ce58e9645d014 --- /dev/null +++ b/src/VisualStudio/Core/Test/ProjectSystemShim/FileChangeWatcherTests.vb @@ -0,0 +1,57 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports System.Collections.Immutable +Imports System.IO +Imports Microsoft.CodeAnalysis.ProjectSystem +Imports Microsoft.CodeAnalysis.Shared.TestHooks +Imports Microsoft.CodeAnalysis.Test.Utilities +Imports Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem +Imports Roslyn.Test.Utilities +Imports IVsAsyncFileChangeEx2 = Microsoft.VisualStudio.Shell.IVsAsyncFileChangeEx2 + +Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim + + Public NotInheritable Class FileChangeWatcherTests + Implements IDisposable + + Private ReadOnly _tempPath As String + + Public Sub New() + _tempPath = Path.Combine(TempRoot.Root, Path.GetRandomFileName()) + Directory.CreateDirectory(_tempPath) + End Sub + + Private Sub Dispose() Implements IDisposable.Dispose + Directory.Delete(_tempPath, recursive:=True) + End Sub + + + Public Async Function WatchingMultipleContexts() As Task + Using workspace = New EditorTestWorkspace() + Dim fileChangeService = New MockVsFileChangeEx + Dim fileChangeWatcher = New FileChangeWatcher(workspace.GetService(Of IAsynchronousOperationListenerProvider)(), Task.FromResult(Of IVsAsyncFileChangeEx2)(fileChangeService)) + + Dim context1 = fileChangeWatcher.CreateContext(ImmutableArray.Create(New WatchedDirectory(_tempPath, ImmutableArray(Of String).Empty))) + Dim context2 = fileChangeWatcher.CreateContext(ImmutableArray.Create(New WatchedDirectory(_tempPath, ImmutableArray(Of String).Empty))) + + Dim handler1Called As Boolean = False + Dim handler2Called As Boolean = False + + AddHandler context1.FileChanged, Sub(sender, args) handler1Called = True + AddHandler context2.FileChanged, Sub(sender, args) handler2Called = True + + Dim watchedFile1 = context1.EnqueueWatchingFile("file1.txt") + Dim watchedFile2 = context2.EnqueueWatchingFile("file2.txt") + + Await workspace.GetService(Of AsynchronousOperationListenerProvider)().GetWaiter(FeatureAttribute.Workspace).ExpeditedWaitAsync() + + fileChangeService.FireUpdate("file2.txt") + + Assert.False(handler1Called) + Assert.True(handler2Called) + End Using + End Function + End Class +End Namespace diff --git a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/FileWatchedPortableExecutableReferenceFactory.cs b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/FileWatchedPortableExecutableReferenceFactory.cs index 3cc09158ef1e1..66880110e594b 100644 --- a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/FileWatchedPortableExecutableReferenceFactory.cs +++ b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/FileWatchedPortableExecutableReferenceFactory.cs @@ -109,7 +109,7 @@ static ImmutableArray GetAdditionalWatchedDirectories() referenceDirectories.Add(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages")); } - return referenceDirectories.SelectAsArray(static d => new WatchedDirectory(d, ".dll")); + return referenceDirectories.SelectAsArray(static d => new WatchedDirectory(d, [".dll"])); } } diff --git a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/IFileChangeWatcher.cs b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/IFileChangeWatcher.cs index 0e7a9612cef12..67458f3e7cdeb 100644 --- a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/IFileChangeWatcher.cs +++ b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/IFileChangeWatcher.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Immutable; +using System.Linq; namespace Microsoft.CodeAnalysis.ProjectSystem; @@ -26,7 +27,7 @@ internal interface IFileChangeWatcher /// internal sealed class WatchedDirectory { - public WatchedDirectory(string path, string? extensionFilter) + public WatchedDirectory(string path, ImmutableArray extensionFilters) { // We are doing string comparisons with this path, so ensure it has a trailing directory separator so we don't get confused with sibling // paths that won't actually be covered. For example, if we're watching C:\Directory we wouldn't see changes to C:\DirectorySibling\Foo.txt. @@ -35,13 +36,13 @@ public WatchedDirectory(string path, string? extensionFilter) path += System.IO.Path.DirectorySeparatorChar; } - if (extensionFilter != null && !extensionFilter.StartsWith(".")) + if (extensionFilters.Any(static filter => !filter.StartsWith("."))) { - throw new ArgumentException($"{nameof(extensionFilter)} should start with a period.", nameof(extensionFilter)); + throw new ArgumentException($"{nameof(extensionFilters)} should only contain entries starting with a period.", nameof(extensionFilters)); } Path = path; - ExtensionFilter = extensionFilter; + ExtensionFilters = extensionFilters; } public string Path { get; } @@ -49,7 +50,7 @@ public WatchedDirectory(string path, string? extensionFilter) /// /// If non-null, only watch the directory for changes to a specific extension. String always starts with a period. /// - public string? ExtensionFilter { get; } + public ImmutableArray ExtensionFilters { get; } public static bool FilePathCoveredByWatchedDirectories(ImmutableArray watchedDirectories, string filePath, StringComparison stringComparison) { @@ -57,10 +58,10 @@ public static bool FilePathCoveredByWatchedDirectories(ImmutableArray filePath.EndsWith(filter, stringComparison))) { return true; } diff --git a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProject.cs b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProject.cs index 3eb2c57e75cd6..5fcbed2ccaeb0 100644 --- a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProject.cs +++ b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProject.cs @@ -222,8 +222,8 @@ static ImmutableArray GetWatchedDirectories(string? language, return language switch { - LanguageNames.VisualBasic => [new(rootPath, ".vb")], - LanguageNames.CSharp => [new(rootPath, ".cs"), new(rootPath, ".razor"), new(rootPath, ".cshtml")], + LanguageNames.VisualBasic => [new(rootPath, [".vb"])], + LanguageNames.CSharp => [new(rootPath, [".cs", ".razor", ".cshtml"])], _ => [] }; } From f6f0035c96393e68bb2e54b7118c38c20fdfb3b3 Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Sat, 16 Nov 2024 07:51:32 -0800 Subject: [PATCH 409/508] Send over the new sourcetext's content checksum when sending notification of textchanges (#75928) * Send over the new sourcetext's content checksum when sending notification of textchanges Calculation of this on the server was taking quite a bit of CPU (about 11.6% in the scrolling speedometer during the typing scenario). Instead, pass this data over as part of the text change notification, similar to what SerializableSourceText's serialization does. --- .../Core/Remote/SolutionChecksumUpdater.cs | 5 +++-- .../Test.Next/Services/ServiceHubServicesTests.cs | 10 +++++----- .../Serialization/SerializableSourceText.cs | 15 ++++++++++----- .../Core/IRemoteAssetSynchronizationService.cs | 2 +- .../RemoteAssetSynchronizationService.cs | 6 +++--- 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs index fea47f1ac5bee..6f1b14d5dc446 100644 --- a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs +++ b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs @@ -221,7 +221,7 @@ private async ValueTask SynchronizeTextChangesAsync( // // otherwise, it will do the normal behavior of getting full text from VS side. this optimization saves // times we need to do full text synchronization for typing scenario. - using var _ = ArrayBuilder<(DocumentId id, Checksum textChecksum, ImmutableArray changes)>.GetInstance(out var builder); + using var _ = ArrayBuilder<(DocumentId id, Checksum textChecksum, ImmutableArray changes, Checksum newTextChecksum)>.GetInstance(out var builder); foreach (var (oldDocument, newDocument) in values) { @@ -242,9 +242,10 @@ private async ValueTask SynchronizeTextChangesAsync( continue; var state = await oldDocument.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); + var newState = await newDocument.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); var textChanges = newText.GetTextChanges(oldText).AsImmutable(); - builder.Add((oldDocument.Id, state.Text, textChanges)); + builder.Add((oldDocument.Id, state.Text, textChanges, newState.Text)); } if (builder.Count == 0) diff --git a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs index fb57ce4999708..fd9622f9ae376 100644 --- a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs @@ -89,15 +89,15 @@ public async Task TestRemoteHostTextSynchronize() // update text var newText = oldText.WithChanges(new TextChange(TextSpan.FromBounds(0, 0), "/* test */")); - // sync - await client.TryInvokeAsync( - (service, cancellationToken) => service.SynchronizeTextChangesAsync([(oldDocument.Id, oldState.Text, newText.GetTextChanges(oldText).AsImmutable())], cancellationToken), - CancellationToken.None); - // apply change to solution var newDocument = oldDocument.WithText(newText); var newState = await newDocument.State.GetStateChecksumsAsync(CancellationToken.None); + // sync + await client.TryInvokeAsync( + (service, cancellationToken) => service.SynchronizeTextChangesAsync([(oldDocument.Id, oldState.Text, newText.GetTextChanges(oldText).AsImmutable(), newState.Text)], cancellationToken), + CancellationToken.None); + // check that text already exist in remote side Assert.True(client.TestData.WorkspaceManager.SolutionAssetCache.TryGetAsset(newState.Text, out var serializableRemoteText)); Assert.Equal(newText.ToString(), (await serializableRemoteText.GetTextAsync(CancellationToken.None)).ToString()); diff --git a/src/Workspaces/Core/Portable/Serialization/SerializableSourceText.cs b/src/Workspaces/Core/Portable/Serialization/SerializableSourceText.cs index 06e2b900e3035..b3f854ae1a892 100644 --- a/src/Workspaces/Core/Portable/Serialization/SerializableSourceText.cs +++ b/src/Workspaces/Core/Portable/Serialization/SerializableSourceText.cs @@ -57,26 +57,31 @@ internal sealed class SerializableSourceText public readonly Checksum ContentChecksum; public SerializableSourceText(TemporaryStorageTextHandle storageHandle) - : this(storageHandle, text: null, storageHandle.ContentHash) + : this(storageHandle, text: null, Checksum.Create(storageHandle.ContentHash)) { } public SerializableSourceText(SourceText text, ImmutableArray contentHash) - : this(storageHandle: null, text, contentHash) + : this(storageHandle: null, text, Checksum.Create(contentHash)) { } - private SerializableSourceText(TemporaryStorageTextHandle? storageHandle, SourceText? text, ImmutableArray contentHash) + public SerializableSourceText(SourceText text, Checksum contentChecksum) + : this(storageHandle: null, text, contentChecksum) + { + } + + private SerializableSourceText(TemporaryStorageTextHandle? storageHandle, SourceText? text, Checksum contentChecksum) { Debug.Assert(storageHandle is null != text is null); _storageHandle = storageHandle; _text = text; - ContentChecksum = Checksum.Create(contentHash); + ContentChecksum = contentChecksum; #if DEBUG var computedContentHash = TryGetText()?.GetContentHash() ?? _storageHandle!.ContentHash; - Debug.Assert(contentHash.SequenceEqual(computedContentHash)); + Debug.Assert(contentChecksum == Checksum.Create(computedContentHash)); #endif } diff --git a/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs b/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs index 03e5d8e76a289..079bc05f93d52 100644 --- a/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs +++ b/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs @@ -26,7 +26,7 @@ internal interface IRemoteAssetSynchronizationService /// entire contents of the file over. /// ValueTask SynchronizeTextChangesAsync( - ImmutableArray<(DocumentId documentId, Checksum baseTextChecksum, ImmutableArray textChanges)> changes, + ImmutableArray<(DocumentId documentId, Checksum baseTextChecksum, ImmutableArray textChanges, Checksum newTextChecksum)> changes, CancellationToken cancellationToken); /// diff --git a/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs b/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs index 8bec38df958f1..f6cc7c147aab3 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs @@ -49,7 +49,7 @@ public ValueTask SynchronizeActiveDocumentAsync(DocumentId? documentId, Cancella } public ValueTask SynchronizeTextChangesAsync( - ImmutableArray<(DocumentId documentId, Checksum baseTextChecksum, ImmutableArray textChanges)> changes, + ImmutableArray<(DocumentId documentId, Checksum baseTextChecksum, ImmutableArray textChanges, Checksum newTextChecksum)> changes, CancellationToken cancellationToken) { return RunServiceAsync(async cancellationToken => @@ -58,7 +58,7 @@ public ValueTask SynchronizeTextChangesAsync( using (RoslynLogger.LogBlock(FunctionId.RemoteHostService_SynchronizeTextAsync, cancellationToken)) { - foreach (var (documentId, baseTextChecksum, textChanges) in changes) + foreach (var (documentId, baseTextChecksum, textChanges, newTextChecksum) in changes) { // Try to get the text associated with baseTextChecksum var text = await TryGetSourceTextAsync(WorkspaceManager, workspace, documentId, baseTextChecksum, cancellationToken).ConfigureAwait(false); @@ -73,7 +73,7 @@ public ValueTask SynchronizeTextChangesAsync( // the asset cache so that future calls to retrieve it can do so quickly, without synchronizing over // the entire document. var newText = text.WithChanges(textChanges); - var newSerializableText = new SerializableSourceText(newText, newText.GetContentHash()); + var newSerializableText = new SerializableSourceText(newText, newTextChecksum); WorkspaceManager.SolutionAssetCache.GetOrAdd(newSerializableText.ContentChecksum, newSerializableText); } From 0a1a7c67471a1c6c521bd5258a2b6601a19722dd Mon Sep 17 00:00:00 2001 From: Omar Bonnet Date: Sun, 17 Nov 2024 22:35:35 -0500 Subject: [PATCH 410/508] Handle `using var` in redundant assignment removal (#75952) Co-authored-by: Omar Bonnet --- .../RemoveUnusedValueAssignmentTests.cs | 29 +++++++++++++++++++ ...scardDeclarationsWithAssignmentsService.cs | 6 ++++ 2 files changed, 35 insertions(+) diff --git a/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedValueAssignmentTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedValueAssignmentTests.cs index c80e2492244f8..7000b894c5edb 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedValueAssignmentTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedValueAssignmentTests.cs @@ -10072,4 +10072,33 @@ public void Reset() { ReferenceAssemblies = ReferenceAssemblies.Net.Net80, }.RunAsync(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72829")] + public async Task RemoveRedundantAssignment_PreservesUsingVar() + { + await TestInRegularAndScriptAsync( + """ + class C + { + void M() + { + {|FixAllInDocument:int items = 0;|} + items = 42; + System.Console.WriteLine(items); + using var _ = System.IO.File.OpenRead("test.txt"); + } + } + """, + """ + class C + { + void M() + { + int items = 42; + System.Console.WriteLine(items); + using var _ = System.IO.File.OpenRead("test.txt"); + } + } + """); + } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpReplaceDiscardDeclarationsWithAssignmentsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpReplaceDiscardDeclarationsWithAssignmentsService.cs index 6de1859a1714d..5b4c5b3e3ebde 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpReplaceDiscardDeclarationsWithAssignmentsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpReplaceDiscardDeclarationsWithAssignmentsService.cs @@ -49,6 +49,12 @@ public async Task ReplaceAsync( case LocalDeclarationStatementSyntax localDeclarationStatement: if (localDeclarationStatement.Declaration.Variables.Any(IsDiscardDeclaration)) { + // Skip replacing discard declarations in "using var" + if (localDeclarationStatement.UsingKeyword != default) + { + continue; + } + RemoveDiscardHelper.ProcessDeclarationStatement(localDeclarationStatement, editor); } From 0d72d14601b0a06075e261323cd826378b616a46 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 18 Nov 2024 15:22:27 -0800 Subject: [PATCH 411/508] Tweaks to inline variable and nullable --- .../Test/Semantic/Semantics/NullableTests.cs | 45 ++++++++++++++++++- .../InlineTemporaryCodeRefactoringProvider.cs | 22 ++++----- .../InlineTemporary/InlineTemporaryTests.cs | 42 +++++++++++++++++ .../Simplifiers/CastSimplifier.cs | 25 +++++++++-- 4 files changed, 117 insertions(+), 17 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableTests.cs index 8334bc3120387..5c29c4382cbe6 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableTests.cs @@ -6,7 +6,9 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; @@ -14,7 +16,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests { - public partial class NullableSemanticTests : SemanticModelTestBase + public sealed partial class NullableSemanticTests : SemanticModelTestBase { [Fact, WorkItem(651624, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/651624")] public void NestedNullableWithAttemptedConversion() @@ -2266,5 +2268,46 @@ class X { } // X M2() => new(); Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "new()").WithArguments("System.Nullable`1").WithLocation(5, 19)); } + + [Fact, WorkItem(651624, "https://github.com/dotnet/roslyn/issues/60552")] + public void TestSemanticModelConversionFromNullableToNonNullable() + { + var src = + """ + #nullable enable + + using System; + + class C + { + public string Main(string? x) + { + return x; + } + + public (int a, int b) Main((int, int) x) + { + return x; + } + + public dynamic Main(object x) + { + return x; + } + } + """; + + var comp = CreateCompilation(src); + + var syntaxTree = comp.SyntaxTrees.Single(); + var semanticModel = comp.GetSemanticModel(syntaxTree); + + var root = syntaxTree.GetRoot(); + var returnStatements = root.DescendantNodesAndSelf().OfType().ToArray(); + + var typeInfo1 = semanticModel.GetTypeInfo(returnStatements[0].Expression); + var typeInfo2 = semanticModel.GetTypeInfo(returnStatements[1].Expression); + var typeInfo3 = semanticModel.GetTypeInfo(returnStatements[2].Expression); + } } } diff --git a/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs index ab294eb81f9c0..af667cac512ef 100644 --- a/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs @@ -25,19 +25,15 @@ namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings.InlineTemporary; [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.InlineTemporary), Shared] -internal sealed partial class CSharpInlineTemporaryCodeRefactoringProvider +[method: ImportingConstructor] +[method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] +internal sealed partial class CSharpInlineTemporaryCodeRefactoringProvider() : AbstractInlineTemporaryCodeRefactoringProvider { private static readonly SyntaxAnnotation DefinitionAnnotation = new(); private static readonly SyntaxAnnotation ReferenceAnnotation = new(); private static readonly SyntaxAnnotation ExpressionAnnotation = new(); - [ImportingConstructor] - [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] - public CSharpInlineTemporaryCodeRefactoringProvider() - { - } - public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var (document, _, cancellationToken) = context; @@ -93,7 +89,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte context.RegisterRefactoring( CodeAction.Create( FeaturesResources.Inline_temporary_variable, - c => InlineTemporaryAsync(document, variableDeclarator, c), + cancellationToken => InlineTemporaryAsync(document, variableDeclarator, cancellationToken), nameof(FeaturesResources.Inline_temporary_variable)), variableDeclarator.Span); } @@ -191,12 +187,12 @@ private static async Task InlineTemporaryAsync(Document document, Vari var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); // Make each topmost parenting statement or Equals Clause Expressions semantically explicit. - document = await document.ReplaceNodesAsync(topmostParentingExpressions, (o, n) => + document = await document.ReplaceNodesAsync(topmostParentingExpressions, (original, current) => { // warn when inlining into a conditional expression, as the inlined expression will not be executed. - if (semanticModel.GetSymbolInfo(o, cancellationToken).Symbol is IMethodSymbol { IsConditional: true }) + if (semanticModel.GetSymbolInfo(original, cancellationToken).Symbol is IMethodSymbol { IsConditional: true }) { - n = n.WithAdditionalAnnotations( + current = current.WithAdditionalAnnotations( WarningAnnotation.Create(CSharpFeaturesResources.Warning_Inlining_temporary_into_conditional_method_call)); } @@ -204,12 +200,12 @@ private static async Task InlineTemporaryAsync(Document document, Vari // on the first inlined location. if (mayContainSideEffects) { - n = n.WithAdditionalAnnotations( + current = current.WithAdditionalAnnotations( WarningAnnotation.Create(CSharpFeaturesResources.Warning_Inlining_temporary_variable_may_change_code_meaning)); mayContainSideEffects = false; } - return n; + return current; }, cancellationToken).ConfigureAwait(false); return document; diff --git a/src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs b/src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs index 93a70d5c9f7a8..c73157fd375d2 100644 --- a/src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs +++ b/src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs @@ -5842,4 +5842,46 @@ void SomeMethod(string _) { } await TestInRegularAndScriptAsync(code, expected); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/60552")] + public async Task TestNullable1() + { + var code = """ + #nullable enable + + public class C + { + private struct S + { + } + + public string M() + { + S s; + var [||]a = "" + s; // "Inline temporary variable" for a + return a; + } + } + """; + + var expected = """ + #nullable enable + + public class C + { + private struct S + { + } + + public string M() + { + S s; + // "Inline temporary variable" for a + return "" + s; + } + } + """; + + await TestInRegularAndScriptAsync(code, expected); + } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/Simplifiers/CastSimplifier.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/Simplifiers/CastSimplifier.cs index 251d02fcc361a..87c331a210ff7 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/Simplifiers/CastSimplifier.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/Simplifiers/CastSimplifier.cs @@ -506,9 +506,28 @@ private static bool IsConversionCastSafeToRemove( return false; } - // If the types of the expressions are the same, then we can remove safely. - if (originalConvertedType.Equals(rewrittenConvertedType, SymbolEqualityComparer.IncludeNullability)) - return true; + if (originalConvertedType.Equals(rewrittenConvertedType)) + { + // If the types of the expressions are exactly the same, then we can remove safely. + if (originalConvertedType.Equals(rewrittenConvertedType, SymbolEqualityComparer.IncludeNullability)) + return true; + + // The types differ on nullability. If it's just the outer nullability that differs, there are cases where + // we still might want to remove. + // + // For example: + // + // string Method() => (string?)notNullString; + // + // Here we have a non-null type converted to its nullable form, which is target typed back to the non-null + // type. Removing this nullable cast is safe and desirable. + if (originalConvertedType.NullableAnnotation == NullableAnnotation.Annotated && + originalConvertedType.WithNullableAnnotation(NullableAnnotation.NotAnnotated).Equals(rewrittenConvertedType, SymbolEqualityComparer.IncludeNullability) && + rewrittenConvertedType.Equals(GetTargetType(castNode), SymbolEqualityComparer.IncludeNullability)) + { + return true; + } + } // We can safely remove convertion to object in interpolated strings regardless of nullability if (castNode.IsParentKind(SyntaxKind.Interpolation) && originalConversionOperation.Type?.SpecialType is SpecialType.System_Object) From a4bf9d4068a632f95c6489d0c980e83983ea39d2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 18 Nov 2024 17:11:19 -0800 Subject: [PATCH 412/508] IN progress --- .../UseCollectionExpressionHelpers.cs | 130 ++---------------- .../Extensions/ArgumentSyntaxExtensions.cs | 1 - .../Extensions/ExpressionSyntaxExtensions.cs | 125 +++++++++++++++++ .../Simplifiers/CastSimplifier.cs | 12 +- 4 files changed, 142 insertions(+), 126 deletions(-) diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs index e3f25377f485f..a79625daad08b 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs @@ -96,7 +96,11 @@ public static bool CanReplaceWithCollectionExpression( var parent = topMostExpression.GetRequiredParent(); - if (!IsInTargetTypedLocation(semanticModel, topMostExpression, cancellationToken)) + if (!topMostExpression.IsInTargetTypedLocation(semanticModel, cancellationToken)) + return false; + + // (X[])[1, 2, 3] is target typed. `(X)[1, 2, 3]` is currently not (because it looks like indexing into an expr). + if (topMostExpression.Parent is CastExpressionSyntax castExpression && castExpression.Type is IdentifierNameSyntax) return false; // X[] = new Y[] { 1, 2, 3 } @@ -613,118 +617,6 @@ bool IsPrimitiveConstant(ExpressionSyntax expression) semanticModel.GetTypeInfo(expression, cancellationToken).Type?.IsValueType == true; } - private static bool IsInTargetTypedLocation(SemanticModel semanticModel, ExpressionSyntax expression, CancellationToken cancellationToken) - { - var topExpression = expression.WalkUpParentheses(); - var parent = topExpression.Parent; - return parent switch - { - EqualsValueClauseSyntax equalsValue => IsInTargetTypedEqualsValueClause(equalsValue), - CastExpressionSyntax castExpression => IsInTargetTypedCastExpression(castExpression), - // a ? [1, 2, 3] : ... is target typed if either the other side is *not* a collection, - // or the entire ternary is target typed itself. - ConditionalExpressionSyntax conditionalExpression => IsInTargetTypedConditionalExpression(conditionalExpression, topExpression), - // Similar rules for switches. - SwitchExpressionArmSyntax switchExpressionArm => IsInTargetTypedSwitchExpressionArm(switchExpressionArm), - InitializerExpressionSyntax initializerExpression => IsInTargetTypedInitializerExpression(initializerExpression, topExpression), - CollectionElementSyntax collectionElement => IsInTargetTypedCollectionElement(collectionElement), - AssignmentExpressionSyntax assignmentExpression => IsInTargetTypedAssignmentExpression(assignmentExpression, topExpression), - BinaryExpressionSyntax binaryExpression => IsInTargetTypedBinaryExpression(binaryExpression, topExpression), - LambdaExpressionSyntax lambda => IsInTargetTypedLambdaExpression(lambda, topExpression), - ArgumentSyntax or AttributeArgumentSyntax => true, - ReturnStatementSyntax => true, - ArrowExpressionClauseSyntax => true, - _ => false, - }; - - bool HasType(ExpressionSyntax expression) - => semanticModel.GetTypeInfo(expression, cancellationToken).Type is not null and not IErrorTypeSymbol; - - static bool IsInTargetTypedEqualsValueClause(EqualsValueClauseSyntax equalsValue) - // If we're after an `x = ...` and it's not `var x`, this is target typed. - => equalsValue.Parent is not VariableDeclaratorSyntax { Parent: VariableDeclarationSyntax { Type.IsVar: true } }; - - static bool IsInTargetTypedCastExpression(CastExpressionSyntax castExpression) - // (X[])[1, 2, 3] is target typed. `(X)[1, 2, 3]` is currently not (because it looks like indexing into an expr). - => castExpression.Type is not IdentifierNameSyntax; - - bool IsInTargetTypedConditionalExpression(ConditionalExpressionSyntax conditionalExpression, ExpressionSyntax expression) - { - if (conditionalExpression.WhenTrue == expression) - return HasType(conditionalExpression.WhenFalse) || IsInTargetTypedLocation(semanticModel, conditionalExpression, cancellationToken); - else if (conditionalExpression.WhenFalse == expression) - return HasType(conditionalExpression.WhenTrue) || IsInTargetTypedLocation(semanticModel, conditionalExpression, cancellationToken); - else - return false; - } - - bool IsInTargetTypedLambdaExpression(LambdaExpressionSyntax lambda, ExpressionSyntax expression) - => lambda.ExpressionBody == expression && IsInTargetTypedLocation(semanticModel, lambda, cancellationToken); - - bool IsInTargetTypedSwitchExpressionArm(SwitchExpressionArmSyntax switchExpressionArm) - { - var switchExpression = (SwitchExpressionSyntax)switchExpressionArm.GetRequiredParent(); - - // check if any other arm has a type that this would be target typed against. - foreach (var arm in switchExpression.Arms) - { - if (arm != switchExpressionArm && HasType(arm.Expression)) - return true; - } - - // All arms do not have a type, this is target typed if the switch itself is target typed. - return IsInTargetTypedLocation(semanticModel, switchExpression, cancellationToken); - } - - bool IsInTargetTypedCollectionElement(CollectionElementSyntax collectionElement) - { - // We do not currently target type spread expressions in a collection expression. - if (collectionElement is not ExpressionElementSyntax) - return false; - - // The element it target typed if the parent collection is itself target typed. - var collectionExpression = (CollectionExpressionSyntax)collectionElement.GetRequiredParent(); - return IsInTargetTypedLocation(semanticModel, collectionExpression, cancellationToken); - } - - bool IsInTargetTypedInitializerExpression(InitializerExpressionSyntax initializerExpression, ExpressionSyntax expression) - { - // new X[] { [1, 2, 3] }. Elements are target typed by array type. - if (initializerExpression.Parent is ArrayCreationExpressionSyntax) - return true; - - // new [] { [1, 2, 3], ... }. Elements are target typed if there's another element with real type. - if (initializerExpression.Parent is ImplicitArrayCreationExpressionSyntax) - { - foreach (var sibling in initializerExpression.Expressions) - { - if (sibling != expression && HasType(sibling)) - return true; - } - } - - // TODO: Handle these. - if (initializerExpression.Parent is StackAllocArrayCreationExpressionSyntax or ImplicitStackAllocArrayCreationExpressionSyntax) - return false; - - // T[] x = [1, 2, 3]; - if (initializerExpression.Parent is EqualsValueClauseSyntax) - return true; - - return false; - } - - bool IsInTargetTypedAssignmentExpression(AssignmentExpressionSyntax assignmentExpression, ExpressionSyntax expression) - { - return expression == assignmentExpression.Right && HasType(assignmentExpression.Left); - } - - bool IsInTargetTypedBinaryExpression(BinaryExpressionSyntax binaryExpression, ExpressionSyntax expression) - { - return binaryExpression.Kind() == SyntaxKind.CoalesceExpression && binaryExpression.Right == expression && HasType(binaryExpression.Left); - } - } - public static CollectionExpressionSyntax ConvertInitializerToCollectionExpression( InitializerExpressionSyntax initializer, bool wasOnSingleLine) { @@ -1076,13 +968,13 @@ bool IsCompatibleSignatureAndArguments( if (arguments.Count == 1 && compilation.SupportsRuntimeCapability(RuntimeCapability.InlineArrayTypes) && originalCreateMethod.Parameters is [ + { + Type: INamedTypeSymbol { - Type: INamedTypeSymbol - { - Name: nameof(Span) or nameof(ReadOnlySpan), - TypeArguments: [ITypeParameterSymbol { TypeParameterKind: TypeParameterKind.Method }] - } spanType - }]) + Name: nameof(Span) or nameof(ReadOnlySpan), + TypeArguments: [ITypeParameterSymbol { TypeParameterKind: TypeParameterKind.Method }] + } spanType + }]) { if (spanType.OriginalDefinition.Equals(compilation.SpanOfTType()) || spanType.OriginalDefinition.Equals(compilation.ReadOnlySpanOfTType())) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ArgumentSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ArgumentSyntaxExtensions.cs index 4b3f3039139ef..d606b894ff809 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ArgumentSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ArgumentSyntaxExtensions.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Linq; -using System.Text.RegularExpressions; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs index 5f5a71587dcfa..8e8dc10c54b96 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs @@ -902,4 +902,129 @@ public static bool IsDirectChildOfMemberAccessExpression(this ExpressionSyntax e public static bool InsideCrefReference(this ExpressionSyntax expression) => expression.FirstAncestorOrSelf() != null; + + public static bool IsInTargetTypedLocation( + this ExpressionSyntax expression, + SemanticModel semanticModel, + CancellationToken cancellationToken) + => IsInTargetTypedLocation(expression, semanticModel, out _, cancellationToken); + + private static bool IsInTargetTypedLocation( + this ExpressionSyntax expression, + SemanticModel semanticModel, + [NotNullWhen(true)] out ITypeSymbol? targetType, + CancellationToken cancellationToken) + { + var topExpression = expression.WalkUpParentheses(); + var parent = topExpression.Parent; + return parent switch + { + EqualsValueClauseSyntax equalsValue => IsInTargetTypedEqualsValueClause(equalsValue, out targetType), + CastExpressionSyntax castExpression => IsInTargetTypedCastExpression(castExpression, out targetType), + // a ? [1, 2, 3] : ... is target typed if either the other side is *not* a collection, + // or the entire ternary is target typed itself. + ConditionalExpressionSyntax conditionalExpression => IsInTargetTypedConditionalExpression(conditionalExpression, topExpression, out targetType), + // Similar rules for switches. + SwitchExpressionArmSyntax switchExpressionArm => IsInTargetTypedSwitchExpressionArm(switchExpressionArm, out targetType), + InitializerExpressionSyntax initializerExpression => IsInTargetTypedInitializerExpression(initializerExpression, topExpression, out targetType), + CollectionElementSyntax collectionElement => IsInTargetTypedCollectionElement(collectionElement, out targetType), + AssignmentExpressionSyntax assignmentExpression => IsInTargetTypedAssignmentExpression(assignmentExpression, topExpression, out targetType), + BinaryExpressionSyntax binaryExpression => IsInTargetTypedBinaryExpression(binaryExpression, topExpression, out targetType), + LambdaExpressionSyntax lambda => IsInTargetTypedLambdaExpression(lambda, topExpression, out targetType), + ArgumentSyntax or AttributeArgumentSyntax => true, + ReturnStatementSyntax => true, + ArrowExpressionClauseSyntax => true, + _ => false, + }; + + bool HasType(ExpressionSyntax expression) + => semanticModel.GetTypeInfo(expression, cancellationToken).Type is not null and not IErrorTypeSymbol; + + static bool IsInTargetTypedEqualsValueClause(EqualsValueClauseSyntax equalsValue) + // If we're after an `x = ...` and it's not `var x`, this is target typed. + => equalsValue.Parent is not VariableDeclaratorSyntax { Parent: VariableDeclarationSyntax { Type.IsVar: true } }; + + static bool IsInTargetTypedCastExpression( + CastExpressionSyntax castExpression, + [NotNullWhen(true)] out ITypeSymbol? targetType) + // (X[])[1, 2, 3] is target typed. `(X)[1, 2, 3]` is currently not (because it looks like indexing into an expr). + => castExpression.Type is not IdentifierNameSyntax; + + bool IsInTargetTypedConditionalExpression(ConditionalExpressionSyntax conditionalExpression, ExpressionSyntax expression) + { + if (conditionalExpression.WhenTrue == expression) + return HasType(conditionalExpression.WhenFalse) || IsInTargetTypedLocation(semanticModel, conditionalExpression, cancellationToken); + else if (conditionalExpression.WhenFalse == expression) + return HasType(conditionalExpression.WhenTrue) || IsInTargetTypedLocation(semanticModel, conditionalExpression, cancellationToken); + else + return false; + } + + bool IsInTargetTypedLambdaExpression(LambdaExpressionSyntax lambda, ExpressionSyntax expression) + => lambda.ExpressionBody == expression && IsInTargetTypedLocation(semanticModel, lambda, cancellationToken); + + bool IsInTargetTypedSwitchExpressionArm(SwitchExpressionArmSyntax switchExpressionArm) + { + var switchExpression = (SwitchExpressionSyntax)switchExpressionArm.GetRequiredParent(); + + // check if any other arm has a type that this would be target typed against. + foreach (var arm in switchExpression.Arms) + { + if (arm != switchExpressionArm && HasType(arm.Expression)) + return true; + } + + // All arms do not have a type, this is target typed if the switch itself is target typed. + return IsInTargetTypedLocation(semanticModel, switchExpression, cancellationToken); + } + + bool IsInTargetTypedCollectionElement(CollectionElementSyntax collectionElement) + { + // We do not currently target type spread expressions in a collection expression. + if (collectionElement is not ExpressionElementSyntax) + return false; + + // The element it target typed if the parent collection is itself target typed. + var collectionExpression = (CollectionExpressionSyntax)collectionElement.GetRequiredParent(); + return IsInTargetTypedLocation(semanticModel, collectionExpression, cancellationToken); + } + + bool IsInTargetTypedInitializerExpression(InitializerExpressionSyntax initializerExpression, ExpressionSyntax expression) + { + // new X[] { [1, 2, 3] }. Elements are target typed by array type. + if (initializerExpression.Parent is ArrayCreationExpressionSyntax) + return true; + + // new [] { [1, 2, 3], ... }. Elements are target typed if there's another element with real type. + if (initializerExpression.Parent is ImplicitArrayCreationExpressionSyntax) + { + foreach (var sibling in initializerExpression.Expressions) + { + if (sibling != expression && HasType(sibling)) + return true; + } + } + + // TODO: Handle these. + if (initializerExpression.Parent is StackAllocArrayCreationExpressionSyntax or ImplicitStackAllocArrayCreationExpressionSyntax) + return false; + + // T[] x = [1, 2, 3]; + if (initializerExpression.Parent is EqualsValueClauseSyntax) + return true; + + return false; + } + + bool IsInTargetTypedAssignmentExpression(AssignmentExpressionSyntax assignmentExpression, ExpressionSyntax expression) + { + return expression == assignmentExpression.Right && HasType(assignmentExpression.Left); + } + + bool IsInTargetTypedBinaryExpression(BinaryExpressionSyntax binaryExpression, ExpressionSyntax expression) + { + return binaryExpression.Kind() == SyntaxKind.CoalesceExpression && binaryExpression.Right == expression && HasType(binaryExpression.Left); + } + } + } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/Simplifiers/CastSimplifier.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/Simplifiers/CastSimplifier.cs index 87c331a210ff7..9170a17ebefdb 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/Simplifiers/CastSimplifier.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/Simplifiers/CastSimplifier.cs @@ -521,12 +521,12 @@ private static bool IsConversionCastSafeToRemove( // // Here we have a non-null type converted to its nullable form, which is target typed back to the non-null // type. Removing this nullable cast is safe and desirable. - if (originalConvertedType.NullableAnnotation == NullableAnnotation.Annotated && - originalConvertedType.WithNullableAnnotation(NullableAnnotation.NotAnnotated).Equals(rewrittenConvertedType, SymbolEqualityComparer.IncludeNullability) && - rewrittenConvertedType.Equals(GetTargetType(castNode), SymbolEqualityComparer.IncludeNullability)) - { - return true; - } + //if (originalConvertedType.NullableAnnotation == NullableAnnotation.Annotated && + // originalConvertedType.WithNullableAnnotation(NullableAnnotation.NotAnnotated).Equals(rewrittenConvertedType, SymbolEqualityComparer.IncludeNullability) && + // rewrittenConvertedType.Equals(GetTargetType(castNode), SymbolEqualityComparer.IncludeNullability)) + //{ + // return true; + //} } // We can safely remove convertion to object in interpolated strings regardless of nullability From 869c99d830ae5c91186585e791394da5d7d6957f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 18 Nov 2024 17:53:07 -0800 Subject: [PATCH 413/508] Working for inline temp --- .../UseCollectionExpressionHelpers.cs | 3 +- .../CSharpSimplificationService.cs | 2 +- .../Extensions/ExpressionSyntaxExtensions.cs | 167 ++++++++++++------ .../Simplifiers/CastSimplifier.cs | 15 +- 4 files changed, 121 insertions(+), 66 deletions(-) diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs index a79625daad08b..ae7567fdb9e89 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs @@ -96,7 +96,8 @@ public static bool CanReplaceWithCollectionExpression( var parent = topMostExpression.GetRequiredParent(); - if (!topMostExpression.IsInTargetTypedLocation(semanticModel, cancellationToken)) + var targetType = topMostExpression.GetTargetType(semanticModel, cancellationToken); + if (targetType is null or IErrorTypeSymbol) return false; // (X[])[1, 2, 3] is target typed. `(X)[1, 2, 3]` is currently not (because it looks like indexing into an expr). diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.cs b/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.cs index fb48ff3897859..188d9122e8376 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.cs @@ -30,10 +30,10 @@ internal partial class CSharpSimplificationService() // 2. Extension method reducer may insert parentheses. So run it before the parentheses remover. private static readonly ImmutableArray s_reducers = [ + new CSharpCastReducer(), new CSharpVarReducer(), new CSharpNameReducer(), new CSharpNullableAnnotationReducer(), - new CSharpCastReducer(), new CSharpExtensionMethodReducer(), new CSharpParenthesizedExpressionReducer(), new CSharpParenthesizedPatternReducer(), diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs index 8e8dc10c54b96..66690ff0f3021 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; @@ -903,128 +904,182 @@ public static bool IsDirectChildOfMemberAccessExpression(this ExpressionSyntax e public static bool InsideCrefReference(this ExpressionSyntax expression) => expression.FirstAncestorOrSelf() != null; - public static bool IsInTargetTypedLocation( + public static ITypeSymbol? GetTargetType( this ExpressionSyntax expression, SemanticModel semanticModel, CancellationToken cancellationToken) - => IsInTargetTypedLocation(expression, semanticModel, out _, cancellationToken); - - private static bool IsInTargetTypedLocation( - this ExpressionSyntax expression, - SemanticModel semanticModel, - [NotNullWhen(true)] out ITypeSymbol? targetType, - CancellationToken cancellationToken) { var topExpression = expression.WalkUpParentheses(); var parent = topExpression.Parent; return parent switch { - EqualsValueClauseSyntax equalsValue => IsInTargetTypedEqualsValueClause(equalsValue, out targetType), - CastExpressionSyntax castExpression => IsInTargetTypedCastExpression(castExpression, out targetType), + EqualsValueClauseSyntax equalsValue => GetTargetTypeForEqualsValueClause(equalsValue), + CastExpressionSyntax castExpression => GetTargetTypedForCastExpression(castExpression), // a ? [1, 2, 3] : ... is target typed if either the other side is *not* a collection, // or the entire ternary is target typed itself. - ConditionalExpressionSyntax conditionalExpression => IsInTargetTypedConditionalExpression(conditionalExpression, topExpression, out targetType), + ConditionalExpressionSyntax conditionalExpression => GetTargetTypeForConditionalExpression(conditionalExpression, topExpression), // Similar rules for switches. - SwitchExpressionArmSyntax switchExpressionArm => IsInTargetTypedSwitchExpressionArm(switchExpressionArm, out targetType), - InitializerExpressionSyntax initializerExpression => IsInTargetTypedInitializerExpression(initializerExpression, topExpression, out targetType), - CollectionElementSyntax collectionElement => IsInTargetTypedCollectionElement(collectionElement, out targetType), - AssignmentExpressionSyntax assignmentExpression => IsInTargetTypedAssignmentExpression(assignmentExpression, topExpression, out targetType), - BinaryExpressionSyntax binaryExpression => IsInTargetTypedBinaryExpression(binaryExpression, topExpression, out targetType), - LambdaExpressionSyntax lambda => IsInTargetTypedLambdaExpression(lambda, topExpression, out targetType), - ArgumentSyntax or AttributeArgumentSyntax => true, - ReturnStatementSyntax => true, - ArrowExpressionClauseSyntax => true, - _ => false, + SwitchExpressionArmSyntax switchExpressionArm => GetTargetTypeForSwitchExpressionArm(switchExpressionArm), + InitializerExpressionSyntax initializerExpression => GetTargetTypeForInitializerExpression(initializerExpression, topExpression), + CollectionElementSyntax collectionElement => GetTargetTypeForCollectionElement(collectionElement), + AssignmentExpressionSyntax assignmentExpression => GetTargetTypeForAssignmentExpression(assignmentExpression, topExpression), + BinaryExpressionSyntax binaryExpression => GetTargetTypeForBinaryExpression(binaryExpression, topExpression), + LambdaExpressionSyntax lambda => GetTargetTypeForLambdaExpression(lambda, topExpression), + ArgumentSyntax argument => GetTargetTypeForArgument(argument), + AttributeArgumentSyntax attributeArgument => GetTargetTypeForAttributeArgument(attributeArgument), + ReturnStatementSyntax returnStatement => GetTargetTypeForReturnStatement(returnStatement), + ArrowExpressionClauseSyntax arrowExpression => GetTargetTypeForArrowExpression(arrowExpression), + _ => null, }; - bool HasType(ExpressionSyntax expression) - => semanticModel.GetTypeInfo(expression, cancellationToken).Type is not null and not IErrorTypeSymbol; + // return result is IErrorTypeSymbol ? null : result; + + bool HasType(ExpressionSyntax expression, [NotNullWhen(true)] out ITypeSymbol? type) + { + type = semanticModel.GetTypeInfo(expression, cancellationToken).Type; + return type is not null; // and not IErrorTypeSymbol; + } + + ITypeSymbol? GetTargetTypeForArgument(ArgumentSyntax argument) + { + var operation = semanticModel.GetOperation(argument, cancellationToken) as IArgumentOperation; + return operation?.Parameter?.Type; + } + + ITypeSymbol? GetTargetTypeForAttributeArgument(AttributeArgumentSyntax argument) + { + var operation = semanticModel.GetOperation(argument, cancellationToken) as IArgumentOperation; + return operation?.Parameter?.Type; + } + + ITypeSymbol? GetTargetTypeForArrowExpression(ArrowExpressionClauseSyntax arrowExpression) + { + var parent = arrowExpression.GetRequiredParent(); + var symbol = semanticModel.GetSymbolInfo(parent, cancellationToken).Symbol ?? semanticModel.GetDeclaredSymbol(parent, cancellationToken); + return symbol.GetMemberType(); + } + + ITypeSymbol? GetTargetTypeForReturnStatement(ReturnStatementSyntax returnStatement) + { + for (SyntaxNode? current = returnStatement; current != null; current = current.Parent) + { + if (current.IsReturnableConstruct()) + { + var symbol = semanticModel.GetSymbolInfo(current, cancellationToken).Symbol ?? semanticModel.GetDeclaredSymbol(current, cancellationToken); + return symbol.GetMemberType(); + } + } - static bool IsInTargetTypedEqualsValueClause(EqualsValueClauseSyntax equalsValue) + return null; + } + + ITypeSymbol? GetTargetTypeForEqualsValueClause(EqualsValueClauseSyntax equalsValue) + { // If we're after an `x = ...` and it's not `var x`, this is target typed. - => equalsValue.Parent is not VariableDeclaratorSyntax { Parent: VariableDeclarationSyntax { Type.IsVar: true } }; + if (equalsValue.Parent is VariableDeclaratorSyntax { Parent: VariableDeclarationSyntax { Type.IsVar: true } }) + return null; - static bool IsInTargetTypedCastExpression( - CastExpressionSyntax castExpression, - [NotNullWhen(true)] out ITypeSymbol? targetType) - // (X[])[1, 2, 3] is target typed. `(X)[1, 2, 3]` is currently not (because it looks like indexing into an expr). - => castExpression.Type is not IdentifierNameSyntax; + var symbol = semanticModel.GetDeclaredSymbol(equalsValue.GetRequiredParent(), cancellationToken); + return symbol.GetMemberType(); + } + + ITypeSymbol? GetTargetTypedForCastExpression(CastExpressionSyntax castExpression) + => semanticModel.GetTypeInfo(castExpression.Type, cancellationToken).Type; - bool IsInTargetTypedConditionalExpression(ConditionalExpressionSyntax conditionalExpression, ExpressionSyntax expression) + ITypeSymbol? GetTargetTypeForConditionalExpression(ConditionalExpressionSyntax conditionalExpression, ExpressionSyntax expression) { if (conditionalExpression.WhenTrue == expression) - return HasType(conditionalExpression.WhenFalse) || IsInTargetTypedLocation(semanticModel, conditionalExpression, cancellationToken); + return HasType(conditionalExpression.WhenFalse, out var falseType) ? falseType : conditionalExpression.GetTargetType(semanticModel, cancellationToken); else if (conditionalExpression.WhenFalse == expression) - return HasType(conditionalExpression.WhenTrue) || IsInTargetTypedLocation(semanticModel, conditionalExpression, cancellationToken); + return HasType(conditionalExpression.WhenTrue, out var trueType) ? trueType : conditionalExpression.GetTargetType(semanticModel, cancellationToken); else - return false; + return null; } - bool IsInTargetTypedLambdaExpression(LambdaExpressionSyntax lambda, ExpressionSyntax expression) - => lambda.ExpressionBody == expression && IsInTargetTypedLocation(semanticModel, lambda, cancellationToken); + ITypeSymbol? GetTargetTypeForLambdaExpression(LambdaExpressionSyntax lambda, ExpressionSyntax expression) + => lambda.ExpressionBody == expression && + lambda.GetTargetType(semanticModel, cancellationToken) is INamedTypeSymbol { DelegateInvokeMethod.ReturnType: var returnType } ? returnType : null; - bool IsInTargetTypedSwitchExpressionArm(SwitchExpressionArmSyntax switchExpressionArm) + ITypeSymbol? GetTargetTypeForSwitchExpressionArm(SwitchExpressionArmSyntax switchExpressionArm) { var switchExpression = (SwitchExpressionSyntax)switchExpressionArm.GetRequiredParent(); // check if any other arm has a type that this would be target typed against. foreach (var arm in switchExpression.Arms) { - if (arm != switchExpressionArm && HasType(arm.Expression)) - return true; + if (arm != switchExpressionArm && HasType(arm.Expression, out var armType)) + return armType; } // All arms do not have a type, this is target typed if the switch itself is target typed. - return IsInTargetTypedLocation(semanticModel, switchExpression, cancellationToken); + return switchExpression.GetTargetType(semanticModel, cancellationToken); } - bool IsInTargetTypedCollectionElement(CollectionElementSyntax collectionElement) + ITypeSymbol? GetTargetTypeForCollectionElement(CollectionElementSyntax collectionElement) { // We do not currently target type spread expressions in a collection expression. if (collectionElement is not ExpressionElementSyntax) - return false; + return null; // The element it target typed if the parent collection is itself target typed. var collectionExpression = (CollectionExpressionSyntax)collectionElement.GetRequiredParent(); - return IsInTargetTypedLocation(semanticModel, collectionExpression, cancellationToken); + var collectionTargetType = collectionExpression.GetTargetType(semanticModel, cancellationToken); + if (collectionTargetType is null) + return null; + + if (collectionTargetType.IsSpanOrReadOnlySpan()) + return collectionTargetType.GetTypeArguments().Single(); + + var ienumerableType = semanticModel.Compilation.IEnumerableOfTType(); + if (collectionTargetType.OriginalDefinition.Equals(ienumerableType)) + return collectionTargetType.GetTypeArguments().Single(); + + foreach (var iface in collectionTargetType.AllInterfaces) + { + if (iface.OriginalDefinition.Equals(ienumerableType)) + return iface.TypeArguments.Single(); + } + + return null; } - bool IsInTargetTypedInitializerExpression(InitializerExpressionSyntax initializerExpression, ExpressionSyntax expression) + ITypeSymbol? GetTargetTypeForInitializerExpression(InitializerExpressionSyntax initializerExpression, ExpressionSyntax expression) { // new X[] { [1, 2, 3] }. Elements are target typed by array type. - if (initializerExpression.Parent is ArrayCreationExpressionSyntax) - return true; + if (initializerExpression.Parent is ArrayCreationExpressionSyntax arrayCreation) + return HasType(arrayCreation.Type, out var elementType) ? elementType : null; // new [] { [1, 2, 3], ... }. Elements are target typed if there's another element with real type. if (initializerExpression.Parent is ImplicitArrayCreationExpressionSyntax) { foreach (var sibling in initializerExpression.Expressions) { - if (sibling != expression && HasType(sibling)) - return true; + if (sibling != expression && HasType(sibling, out var siblingType)) + return siblingType; } + + return null; } // TODO: Handle these. if (initializerExpression.Parent is StackAllocArrayCreationExpressionSyntax or ImplicitStackAllocArrayCreationExpressionSyntax) - return false; + return null; // T[] x = [1, 2, 3]; if (initializerExpression.Parent is EqualsValueClauseSyntax) - return true; + return null; - return false; + return null; } - bool IsInTargetTypedAssignmentExpression(AssignmentExpressionSyntax assignmentExpression, ExpressionSyntax expression) + ITypeSymbol? GetTargetTypeForAssignmentExpression(AssignmentExpressionSyntax assignmentExpression, ExpressionSyntax expression) { - return expression == assignmentExpression.Right && HasType(assignmentExpression.Left); + return expression == assignmentExpression.Right && HasType(assignmentExpression.Left, out var leftType) ? leftType : null; } - bool IsInTargetTypedBinaryExpression(BinaryExpressionSyntax binaryExpression, ExpressionSyntax expression) + ITypeSymbol? GetTargetTypeForBinaryExpression(BinaryExpressionSyntax binaryExpression, ExpressionSyntax expression) { - return binaryExpression.Kind() == SyntaxKind.CoalesceExpression && binaryExpression.Right == expression && HasType(binaryExpression.Left); + return binaryExpression.Kind() == SyntaxKind.CoalesceExpression && binaryExpression.Right == expression && HasType(binaryExpression.Left, out var leftType) ? leftType : null; } } - } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/Simplifiers/CastSimplifier.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/Simplifiers/CastSimplifier.cs index 9170a17ebefdb..b8933465c52de 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/Simplifiers/CastSimplifier.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/Simplifiers/CastSimplifier.cs @@ -512,8 +512,7 @@ private static bool IsConversionCastSafeToRemove( if (originalConvertedType.Equals(rewrittenConvertedType, SymbolEqualityComparer.IncludeNullability)) return true; - // The types differ on nullability. If it's just the outer nullability that differs, there are cases where - // we still might want to remove. + // The types differ on nullability. But we may still want to remove this. // // For example: // @@ -521,12 +520,12 @@ private static bool IsConversionCastSafeToRemove( // // Here we have a non-null type converted to its nullable form, which is target typed back to the non-null // type. Removing this nullable cast is safe and desirable. - //if (originalConvertedType.NullableAnnotation == NullableAnnotation.Annotated && - // originalConvertedType.WithNullableAnnotation(NullableAnnotation.NotAnnotated).Equals(rewrittenConvertedType, SymbolEqualityComparer.IncludeNullability) && - // rewrittenConvertedType.Equals(GetTargetType(castNode), SymbolEqualityComparer.IncludeNullability)) - //{ - // return true; - //} + var targetType = castNode.GetTargetType(originalSemanticModel, cancellationToken); + if (targetType is not null and not IErrorTypeSymbol && + rewrittenConvertedType.Equals(targetType, SymbolEqualityComparer.IncludeNullability)) + { + return true; + } } // We can safely remove convertion to object in interpolated strings regardless of nullability From ed139d13eba8038bc7f02ec59ce0a1f226816de3 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 18 Nov 2024 18:05:04 -0800 Subject: [PATCH 414/508] mostly collection expr --- .../CSharp/Extensions/ExpressionSyntaxExtensions.cs | 10 ++-------- .../Compiler/Core/Extensions/ISymbolExtensions.cs | 1 + 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs index 66690ff0f3021..d4213ffb25098 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs @@ -941,16 +941,10 @@ bool HasType(ExpressionSyntax expression, [NotNullWhen(true)] out ITypeSymbol? t } ITypeSymbol? GetTargetTypeForArgument(ArgumentSyntax argument) - { - var operation = semanticModel.GetOperation(argument, cancellationToken) as IArgumentOperation; - return operation?.Parameter?.Type; - } + => argument.DetermineParameter(semanticModel, allowUncertainCandidates: false, allowParams: true, cancellationToken)?.Type; ITypeSymbol? GetTargetTypeForAttributeArgument(AttributeArgumentSyntax argument) - { - var operation = semanticModel.GetOperation(argument, cancellationToken) as IArgumentOperation; - return operation?.Parameter?.Type; - } + => argument.DetermineParameter(semanticModel, allowUncertainCandidates: false, allowParams: true, cancellationToken)?.Type; ITypeSymbol? GetTargetTypeForArrowExpression(ArrowExpressionClauseSyntax arrowExpression) { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs index 6ab3df0c46d46..64152ff9a6ad2 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs @@ -330,6 +330,7 @@ public static bool IsRequired([NotNullWhen(true)] this ISymbol? symbol) IMethodSymbol methodSymbol => methodSymbol.ReturnType, IEventSymbol eventSymbol => eventSymbol.Type, IParameterSymbol parameterSymbol => parameterSymbol.Type, + ILocalSymbol localSymbol => localSymbol.Type, _ => null, }; From 1d9b96f34165a17785dec76be6bdcab788de2566 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 18 Nov 2024 18:09:28 -0800 Subject: [PATCH 415/508] Fix tuple --- .../Extensions/ExpressionSyntaxExtensions.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs index d4213ffb25098..3872284a1b2f7 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs @@ -941,7 +941,23 @@ bool HasType(ExpressionSyntax expression, [NotNullWhen(true)] out ITypeSymbol? t } ITypeSymbol? GetTargetTypeForArgument(ArgumentSyntax argument) - => argument.DetermineParameter(semanticModel, allowUncertainCandidates: false, allowParams: true, cancellationToken)?.Type; + { + if (argument.Parent is TupleExpressionSyntax tupleExpression) + { + var tupleType = tupleExpression.GetTargetType(semanticModel, cancellationToken); + if (tupleType is null) + return null; + + var typeArguments = tupleType.GetTypeArguments(); + var index = tupleExpression.Arguments.IndexOf(argument); + + return index < typeArguments.Length ? typeArguments[index] : null; + } + else + { + return argument.DetermineParameter(semanticModel, allowUncertainCandidates: false, allowParams: true, cancellationToken)?.Type; + } + } ITypeSymbol? GetTargetTypeForAttributeArgument(AttributeArgumentSyntax argument) => argument.DetermineParameter(semanticModel, allowUncertainCandidates: false, allowParams: true, cancellationToken)?.Type; From ef60ad49474b02be5277312d356b98babca30f6b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 18 Nov 2024 18:18:56 -0800 Subject: [PATCH 416/508] Fix equals value --- .../CSharp/Extensions/ExpressionSyntaxExtensions.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs index 3872284a1b2f7..45a5af43bbbde 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs @@ -1071,14 +1071,14 @@ bool HasType(ExpressionSyntax expression, [NotNullWhen(true)] out ITypeSymbol? t return null; } + // T[] x = [1, 2, 3]; + if (initializerExpression.Parent is EqualsValueClauseSyntax equalsValue) + return GetTargetTypeForEqualsValueClause(equalsValue); + // TODO: Handle these. if (initializerExpression.Parent is StackAllocArrayCreationExpressionSyntax or ImplicitStackAllocArrayCreationExpressionSyntax) return null; - // T[] x = [1, 2, 3]; - if (initializerExpression.Parent is EqualsValueClauseSyntax) - return null; - return null; } From cb8dfefca7a8953b65546a68d004a420b34b7398 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 18 Nov 2024 18:23:00 -0800 Subject: [PATCH 417/508] Revert file --- .../Test/Semantic/Semantics/NullableTests.cs | 45 +------------------ 1 file changed, 1 insertion(+), 44 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableTests.cs index 5c29c4382cbe6..8334bc3120387 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableTests.cs @@ -6,9 +6,7 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Text; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; @@ -16,7 +14,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests { - public sealed partial class NullableSemanticTests : SemanticModelTestBase + public partial class NullableSemanticTests : SemanticModelTestBase { [Fact, WorkItem(651624, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/651624")] public void NestedNullableWithAttemptedConversion() @@ -2268,46 +2266,5 @@ class X { } // X M2() => new(); Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "new()").WithArguments("System.Nullable`1").WithLocation(5, 19)); } - - [Fact, WorkItem(651624, "https://github.com/dotnet/roslyn/issues/60552")] - public void TestSemanticModelConversionFromNullableToNonNullable() - { - var src = - """ - #nullable enable - - using System; - - class C - { - public string Main(string? x) - { - return x; - } - - public (int a, int b) Main((int, int) x) - { - return x; - } - - public dynamic Main(object x) - { - return x; - } - } - """; - - var comp = CreateCompilation(src); - - var syntaxTree = comp.SyntaxTrees.Single(); - var semanticModel = comp.GetSemanticModel(syntaxTree); - - var root = syntaxTree.GetRoot(); - var returnStatements = root.DescendantNodesAndSelf().OfType().ToArray(); - - var typeInfo1 = semanticModel.GetTypeInfo(returnStatements[0].Expression); - var typeInfo2 = semanticModel.GetTypeInfo(returnStatements[1].Expression); - var typeInfo3 = semanticModel.GetTypeInfo(returnStatements[2].Expression); - } } } From 324fd25331c969cd742ba68eee09ffd4b6fd29e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Mon, 18 Nov 2024 19:04:37 -0800 Subject: [PATCH 418/508] Include list of processes that lock file in `can't write file` error message (#75946) --- .../CSharp/Portable/CSharpResources.resx | 2 +- .../Portable/xlf/CSharpResources.cs.xlf | 4 +- .../Portable/xlf/CSharpResources.de.xlf | 4 +- .../Portable/xlf/CSharpResources.es.xlf | 4 +- .../Portable/xlf/CSharpResources.fr.xlf | 4 +- .../Portable/xlf/CSharpResources.it.xlf | 4 +- .../Portable/xlf/CSharpResources.ja.xlf | 4 +- .../Portable/xlf/CSharpResources.ko.xlf | 4 +- .../Portable/xlf/CSharpResources.pl.xlf | 4 +- .../Portable/xlf/CSharpResources.pt-BR.xlf | 4 +- .../Portable/xlf/CSharpResources.ru.xlf | 4 +- .../Portable/xlf/CSharpResources.tr.xlf | 4 +- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 4 +- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 4 +- .../Test/CommandLine/CommandLineTests.cs | 34 ++- .../Core/Portable/CodeAnalysisResources.resx | 3 + ...mmonCompiler.CompilerEmitStreamProvider.cs | 14 +- .../InternalUtilities/FileLockCheck.cs | 227 ++++++++++++++++++ .../Portable/xlf/CodeAnalysisResources.cs.xlf | 5 + .../Portable/xlf/CodeAnalysisResources.de.xlf | 5 + .../Portable/xlf/CodeAnalysisResources.es.xlf | 5 + .../Portable/xlf/CodeAnalysisResources.fr.xlf | 5 + .../Portable/xlf/CodeAnalysisResources.it.xlf | 5 + .../Portable/xlf/CodeAnalysisResources.ja.xlf | 5 + .../Portable/xlf/CodeAnalysisResources.ko.xlf | 5 + .../Portable/xlf/CodeAnalysisResources.pl.xlf | 5 + .../xlf/CodeAnalysisResources.pt-BR.xlf | 5 + .../Portable/xlf/CodeAnalysisResources.ru.xlf | 5 + .../Portable/xlf/CodeAnalysisResources.tr.xlf | 5 + .../xlf/CodeAnalysisResources.zh-Hans.xlf | 5 + .../xlf/CodeAnalysisResources.zh-Hant.xlf | 5 + .../Test/CommandLine/CommandLineTests.vb | 30 ++- 32 files changed, 396 insertions(+), 31 deletions(-) create mode 100644 src/Compilers/Core/Portable/InternalUtilities/FileLockCheck.cs diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 60b12161bbe16..2a355f98b4b39 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -3546,7 +3546,7 @@ Give the compiler some way to differentiate the methods. For example, you can gi Error opening response file '{0}' - Cannot open '{0}' for writing -- '{1}' + Cannot open '{0}' for writing -- {1} Invalid image base number '{0}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 3063434c31f32..9280853e7d4e2 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -10305,8 +10305,8 @@ Poskytněte kompilátoru nějaký způsob, jak metody rozlišit. Můžete např - Cannot open '{0}' for writing -- '{1}' - {0} se nedá otevřít pro zápis -- {1}. + Cannot open '{0}' for writing -- {1} + {0} se nedá otevřít pro zápis -- {1}. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 90f477f32f713..f1ab55a44fbfe 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -10305,8 +10305,8 @@ Unterstützen Sie den Compiler bei der Unterscheidung zwischen den Methoden. Daz - Cannot open '{0}' for writing -- '{1}' - "{0}" kann nicht zum Schreiben geöffnet werden: "{1}" + Cannot open '{0}' for writing -- {1} + "{0}" kann nicht zum Schreiben geöffnet werden: "{1}" diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index b02f65a303a4b..8fcc5196b9532 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -10305,8 +10305,8 @@ Indique al compilador alguna forma de diferenciar los métodos. Por ejemplo, pue - Cannot open '{0}' for writing -- '{1}' - No se puede abrir '{0}' para escribir: '{1}' + Cannot open '{0}' for writing -- {1} + No se puede abrir '{0}' para escribir: '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index e68743f716884..e0a6124ed6b93 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -10305,8 +10305,8 @@ Permettez au compilateur de différencier les méthodes. Par exemple, vous pouve - Cannot open '{0}' for writing -- '{1}' - Impossible d'ouvrir '{0}' en écriture -- '{1}' + Cannot open '{0}' for writing -- {1} + Impossible d'ouvrir '{0}' en écriture -- '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index d740d7581631a..6e9edfb0ce2a4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -10305,8 +10305,8 @@ Impostare il compilatore in modo tale da distinguere i metodi, ad esempio assegn - Cannot open '{0}' for writing -- '{1}' - Non è possibile aprire '{0}' per la scrittura - '{1}' + Cannot open '{0}' for writing -- {1} + Non è possibile aprire '{0}' per la scrittura - '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 99aac9bb501f4..c631942fc593f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -10305,8 +10305,8 @@ C# では out と ref を区別しますが、CLR では同じと認識します - Cannot open '{0}' for writing -- '{1}' - ファイル '{0}' を開いて書き込むことができません -- '{1}' + Cannot open '{0}' for writing -- {1} + ファイル '{0}' を開いて書き込むことができません -- '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index d892a425f8a3a..4e72992be7e73 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -10305,8 +10305,8 @@ C#에서는 out과 ref를 구분하지만 CLR에서는 동일한 것으로 간 - Cannot open '{0}' for writing -- '{1}' - '{0}'을(를) 쓰기용으로 열 수 없습니다. '{1}' + Cannot open '{0}' for writing -- {1} + '{0}'을(를) 쓰기용으로 열 수 없습니다. '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index eb751460f7219..2377df8f9e139 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -10305,8 +10305,8 @@ Musisz umożliwić kompilatorowi rozróżnienie metod. Możesz na przykład nada - Cannot open '{0}' for writing -- '{1}' - Nie można otworzyć „{0}” do zapisu — „{1}” + Cannot open '{0}' for writing -- {1} + Nie można otworzyć „{0}” do zapisu — „{1}” diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 1d7ee3712fe2c..5a9e8a14a525c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -10305,8 +10305,8 @@ Forneça ao compilador alguma forma de diferenciar os métodos. Por exemplo, voc - Cannot open '{0}' for writing -- '{1}' - Não é possível abrir "{0}" para escrever -- "{1}" + Cannot open '{0}' for writing -- {1} + Não é possível abrir "{0}" para escrever -- "{1}" diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index bced0025a1c6a..afd7cc97f6b04 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -10306,8 +10306,8 @@ Give the compiler some way to differentiate the methods. For example, you can gi - Cannot open '{0}' for writing -- '{1}' - Не удается открыть "{0}" для записи — "{1}". + Cannot open '{0}' for writing -- {1} + Не удается открыть "{0}" для записи — "{1}". diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 4ed19a83eb011..5feae747be33d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -10305,8 +10305,8 @@ Derleyiciye yöntemleri ayrıştırma yolu verin. Örneğin, bunlara farklı adl - Cannot open '{0}' for writing -- '{1}' - '{0}' yazma için açılamıyor -- '{1}' + Cannot open '{0}' for writing -- {1} + '{0}' yazma için açılamıyor -- '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 1fc9dc681199e..bfdbc17e43f04 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -10305,8 +10305,8 @@ Give the compiler some way to differentiate the methods. For example, you can gi - Cannot open '{0}' for writing -- '{1}' - 无法打开“{0}”进行写入 --“{1}” + Cannot open '{0}' for writing -- {1} + 无法打开“{0}”进行写入 --“{1}” diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index d3fee078125d8..c510281698946 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -10305,8 +10305,8 @@ Give the compiler some way to differentiate the methods. For example, you can gi - Cannot open '{0}' for writing -- '{1}' - 無法開啟 '{0}' 進行寫入 -- '{1}' + Cannot open '{0}' for writing -- {1} + 無法開啟 '{0}' 進行寫入 -- '{1}' diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index b6cf51975aba5..35200629caa39 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -38,6 +38,7 @@ using Basic.Reference.Assemblies; using static Microsoft.CodeAnalysis.CommonDiagnosticAnalyzers; using static Roslyn.Test.Utilities.SharedResourceHelpers; +using System.Diagnostics; namespace Microsoft.CodeAnalysis.CSharp.CommandLine.UnitTests { @@ -4760,7 +4761,7 @@ public void SdkPathAndLibEnvVariable_Relative_csc() } [Fact] - public void UnableWriteOutput() + public void UnableWriteOutput_OutputFileIsDirectory() { var tempFolder = Temp.CreateDirectory(); var baseDirectory = tempFolder.ToString(); @@ -4772,7 +4773,36 @@ public void UnableWriteOutput() var outWriter = new StringWriter(CultureInfo.InvariantCulture); int exitCode = CreateCSharpCompiler(null, baseDirectory, new[] { "/nologo", "/preferreduilang:en", "/t:library", "/out:" + subFolder.ToString(), src.ToString() }).Run(outWriter); Assert.Equal(1, exitCode); - Assert.True(outWriter.ToString().Trim().StartsWith("error CS2012: Cannot open '" + subFolder.ToString() + "' for writing -- '", StringComparison.Ordinal)); // Cannot create a file when that file already exists. + var output = outWriter.ToString().Trim(); + Assert.StartsWith($"error CS2012: Cannot open '{subFolder}' for writing -- ", output); // Cannot create a file when that file already exists. + + CleanupAllGeneratedFiles(src.Path); + } + + [ConditionalFact(typeof(WindowsOnly))] + public void UnableWriteOutput_OutputFileLocked() + { + var tempFolder = Temp.CreateDirectory(); + var baseDirectory = tempFolder.ToString(); + var filePath = tempFolder.CreateFile("temp").Path; + + using var _ = new FileStream(filePath, FileMode.Open, FileAccess.Write, FileShare.None); + var currentProcess = Process.GetCurrentProcess(); + + var src = Temp.CreateFile("a.cs"); + src.WriteAllText("public class C{}"); + + var outWriter = new StringWriter(CultureInfo.InvariantCulture); + int exitCode = CreateCSharpCompiler(responseFile: null, baseDirectory, ["/nologo", "/preferreduilang:en", "/t:library", "/out:" + filePath, src.ToString()]).Run(outWriter); + Assert.Equal(1, exitCode); + var output = outWriter.ToString().Trim(); + + var pattern = @"error CS2012: Cannot open '(?.*)' for writing -- (?.*); file may be locked by '(?.*)' \((?.*)\)"; + var match = Regex.Match(output, pattern); + Assert.True(match.Success, $"Expected pattern:{Environment.NewLine}{pattern}{Environment.NewLine}Actual:{Environment.NewLine}{output}"); + Assert.Equal(filePath, match.Groups["path"].Value); + Assert.Contains("testhost", match.Groups["app"].Value); + Assert.Equal(currentProcess.Id, int.Parse(match.Groups["pid"].Value)); CleanupAllGeneratedFiles(src.Path); } diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.resx b/src/Compilers/Core/Portable/CodeAnalysisResources.resx index c013f246a5a59..6dd5fc6a12d31 100644 --- a/src/Compilers/Core/Portable/CodeAnalysisResources.resx +++ b/src/Compilers/Core/Portable/CodeAnalysisResources.resx @@ -794,4 +794,7 @@ '{0}' type does not have the expected constructor + + {0}; file may be locked by {1} + \ No newline at end of file diff --git a/src/Compilers/Core/Portable/CommandLine/CommonCompiler.CompilerEmitStreamProvider.cs b/src/Compilers/Core/Portable/CommandLine/CommonCompiler.CompilerEmitStreamProvider.cs index f0cc3f26907d5..882906e7540c9 100644 --- a/src/Compilers/Core/Portable/CommandLine/CommonCompiler.CompilerEmitStreamProvider.cs +++ b/src/Compilers/Core/Portable/CommandLine/CommonCompiler.CompilerEmitStreamProvider.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.IO; +using System.Linq; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis @@ -114,7 +115,18 @@ private Stream OpenFileStream() private void ReportOpenFileDiagnostic(DiagnosticBag diagnostics, Exception e) { var messageProvider = _compiler.MessageProvider; - diagnostics.Add(messageProvider.CreateDiagnostic(messageProvider.ERR_CantOpenFileWrite, Location.None, _filePath, e.Message)); + var lockedBy = FileLockCheck.TryGetLockingProcessInfos(_filePath); + + var message = lockedBy.IsEmpty + ? (object)e.Message + : new LocalizableResourceString( + nameof(CodeAnalysisResources.ExceptionMessage_FileMayBeLockedBy), + CodeAnalysisResources.ResourceManager, + typeof(CodeAnalysisResources), + e.Message, + string.Join(", ", lockedBy.Select(info => $"'{info.applicationName}' ({info.processId})"))); + + diagnostics.Add(messageProvider.CreateDiagnostic(messageProvider.ERR_CantOpenFileWrite, Location.None, _filePath, message)); } } } diff --git a/src/Compilers/Core/Portable/InternalUtilities/FileLockCheck.cs b/src/Compilers/Core/Portable/InternalUtilities/FileLockCheck.cs new file mode 100644 index 0000000000000..3238c827b0694 --- /dev/null +++ b/src/Compilers/Core/Portable/InternalUtilities/FileLockCheck.cs @@ -0,0 +1,227 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// Based on https://github.com/dotnet/msbuild/blob/6cd445d84e59a36c7fbb6f50b7a5a62767a6da51/src/Utilities/LockCheck.cs + +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Runtime.InteropServices; +using Microsoft.CodeAnalysis.PooledObjects; + +namespace Roslyn.Utilities; + +/// +/// This class implements checking what processes are locking a file on Windows. +/// It uses the Restart Manager API to do this. Other platforms are skipped. +/// +internal static class FileLockCheck +{ + [StructLayout(LayoutKind.Sequential)] + private struct FILETIME + { + public uint dwLowDateTime; + public uint dwHighDateTime; + } + + [StructLayout(LayoutKind.Sequential)] + private struct RM_UNIQUE_PROCESS + { + public uint dwProcessId; + public FILETIME ProcessStartTime; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + private struct RM_PROCESS_INFO + { + private const int CCH_RM_MAX_APP_NAME = 255; + private const int CCH_RM_MAX_SVC_NAME = 63; + + internal RM_UNIQUE_PROCESS Process; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)] + public string strAppName; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)] + public string strServiceShortName; + internal int ApplicationType; + public uint AppStatus; + public uint TSSessionId; + [MarshalAs(UnmanagedType.Bool)] + public bool bRestartable; + } + + private const string RestartManagerDll = "rstrtmgr.dll"; + + [DllImport(RestartManagerDll, CharSet = CharSet.Unicode)] + private static extern int RmRegisterResources(uint pSessionHandle, + uint nFiles, + string[] rgsFilenames, + uint nApplications, + [In] RM_UNIQUE_PROCESS[]? rgApplications, + uint nServices, + string[]? rgsServiceNames); + + /// + /// Starts a new Restart Manager session. + /// A maximum of 64 Restart Manager sessions per user session + /// can be open on the system at the same time. When this + /// function starts a session, it returns a session handle + /// and session key that can be used in subsequent calls to + /// the Restart Manager API. + /// + /// + /// A pointer to the handle of a Restart Manager session. + /// The session handle can be passed in subsequent calls + /// to the Restart Manager API. + /// + /// + /// Reserved. This parameter should be 0. + /// + /// + /// A null-terminated string that contains the session key + /// to the new session. The string must be allocated before + /// calling the RmStartSession function. + /// + /// System error codes that are defined in Winerror.h. + /// + /// The Rm­­StartSession function doesn’t properly null-terminate + /// the session key, even though the function is documented as + /// returning a null-terminated string. To work around this bug, + /// we pre-fill the buffer with null characters so that whatever + /// ends gets written will have a null terminator (namely, one of + /// the null characters we placed ahead of time). + /// + /// see . + /// + /// + [DllImport(RestartManagerDll, CharSet = CharSet.Unicode)] + private static extern unsafe int RmStartSession( + out uint pSessionHandle, + int dwSessionFlags, + char* strSessionKey); + + /// + /// Ends the Restart Manager session. + /// This function should be called by the primary installer that + /// has previously started the session by calling the + /// function. The RmEndSession function can be called by a secondary installer + /// that is joined to the session once no more resources need to be registered + /// by the secondary installer. + /// + /// A handle to an existing Restart Manager session. + /// + /// The function can return one of the system error codes that are defined in Winerror.h. + /// + [DllImport(RestartManagerDll)] + private static extern int RmEndSession(uint pSessionHandle); + + [DllImport(RestartManagerDll, CharSet = CharSet.Unicode)] + private static extern int RmGetList(uint dwSessionHandle, + out uint pnProcInfoNeeded, + ref uint pnProcInfo, + [In, Out] RM_PROCESS_INFO[]? rgAffectedApps, + ref uint lpdwRebootReasons); + + public static ImmutableArray<(int processId, string applicationName)> TryGetLockingProcessInfos(string path) + { + if (!PlatformInformation.IsWindows) + { + return []; + } + + try + { + return GetLockingProcessInfosImpl([path]); + } + catch + { + return []; + } + } + + private static ImmutableArray<(int processId, string applicationName)> GetLockingProcessInfosImpl(string[] paths) + { + const int MaxRetries = 6; + const int ERROR_MORE_DATA = 234; + const uint RM_REBOOT_REASON_NONE = 0; + + uint handle; + int res; + + unsafe + { + char* key = stackalloc char[sizeof(Guid) * 2 + 1]; + res = RmStartSession(out handle, 0, key); + } + + if (res != 0) + { + return []; + } + + try + { + res = RmRegisterResources(handle, (uint)paths.Length, paths, 0, null, 0, null); + if (res != 0) + { + return []; + } + + // + // Obtain the list of affected applications/services. + // + // NOTE: Restart Manager returns the results into the buffer allocated by the caller. The first call to + // RmGetList() will return the size of the buffer (i.e. nProcInfoNeeded) the caller needs to allocate. + // The caller then needs to allocate the buffer (i.e. rgAffectedApps) and make another RmGetList() + // call to ask Restart Manager to write the results into the buffer. However, since Restart Manager + // refreshes the list every time RmGetList()is called, it is possible that the size returned by the first + // RmGetList()call is not sufficient to hold the results discovered by the second RmGetList() call. Therefore, + // it is recommended that the caller follows the following practice to handle this race condition: + // + // Use a loop to call RmGetList() in case the buffer allocated according to the size returned in previous + // call is not enough. + // + uint pnProcInfo = 0; + RM_PROCESS_INFO[]? rgAffectedApps = null; + int retry = 0; + do + { + uint lpdwRebootReasons = RM_REBOOT_REASON_NONE; + res = RmGetList(handle, out uint pnProcInfoNeeded, ref pnProcInfo, rgAffectedApps, ref lpdwRebootReasons); + if (res == 0) + { + // If pnProcInfo == 0, then there is simply no locking process (found), in this case rgAffectedApps is "null". + if (pnProcInfo == 0) + { + return []; + } + + Debug.Assert(rgAffectedApps != null); + + var lockInfos = ArrayBuilder<(int, string)>.GetInstance((int)pnProcInfo); + for (int i = 0; i < pnProcInfo; i++) + { + lockInfos.Add(((int)rgAffectedApps[i].Process.dwProcessId, rgAffectedApps[i].strAppName)); + } + + return lockInfos.ToImmutableAndFree(); + } + + if (res != ERROR_MORE_DATA) + { + return []; + } + + pnProcInfo = pnProcInfoNeeded; + rgAffectedApps = new RM_PROCESS_INFO[pnProcInfo]; + } + while (res == ERROR_MORE_DATA && retry++ < MaxRetries); + } + finally + { + _ = RmEndSession(handle); + } + + return []; + } +} diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.cs.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.cs.xlf index 2df3ee048a081..8c78a670fca3d 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.cs.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.cs.xlf @@ -87,6 +87,11 @@ Hodnota end nesmí být menší než hodnota start. start={0} end={1}. + + {0}; file may be locked by {1} + {0}; file may be locked by {1} + + Generator Generátor diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.de.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.de.xlf index 94bb96efb0917..09105c3c8acdc 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.de.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.de.xlf @@ -87,6 +87,11 @@ "Ende" darf nicht kleiner sein als "Start". start='{0}' end='{1}'. + + {0}; file may be locked by {1} + {0}; file may be locked by {1} + + Generator Generator diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.es.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.es.xlf index 3c49dabe1c642..464d9582d2581 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.es.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.es.xlf @@ -87,6 +87,11 @@ 'fin' no debe ser menor que 'inicio'. inicio='{0}' fin='{1}'. + + {0}; file may be locked by {1} + {0}; file may be locked by {1} + + Generator Generador diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.fr.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.fr.xlf index 8364a6e0d57d7..cd0e0e2f40022 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.fr.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.fr.xlf @@ -87,6 +87,11 @@ 'end' ne doit pas être inférieur à 'start'. start='{0}'end='{1}'. + + {0}; file may be locked by {1} + {0}; file may be locked by {1} + + Generator Générateur diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.it.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.it.xlf index 4e1ea7458a093..9bff0aac1c488 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.it.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.it.xlf @@ -87,6 +87,11 @@ il valore di 'end' non deve essere minore di quello di 'start'. start='{0}' end='{1}'. + + {0}; file may be locked by {1} + {0}; file may be locked by {1} + + Generator Generatore diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ja.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ja.xlf index bbe59f873bdcb..7e6eaf727cd1d 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ja.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ja.xlf @@ -87,6 +87,11 @@ 'end' は 'start' より小さくすることはできません。start='{0}' end='{1}'。 + + {0}; file may be locked by {1} + {0}; file may be locked by {1} + + Generator ジェネレーター diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ko.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ko.xlf index db8f19aed58d0..b974f0d815418 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ko.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ko.xlf @@ -87,6 +87,11 @@ 'end'는 'start'보다 작을 수 없습니다. start='{0}' end='{1}' + + {0}; file may be locked by {1} + {0}; file may be locked by {1} + + Generator 생성기 diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pl.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pl.xlf index cc98075bccb34..8afb64f762d90 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pl.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pl.xlf @@ -87,6 +87,11 @@ Wartość „end” nie może być mniejsza niż wartość „start”. start=„{0}” end=„{1}”. + + {0}; file may be locked by {1} + {0}; file may be locked by {1} + + Generator Generator diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pt-BR.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pt-BR.xlf index 52a2d40046885..667da8fa8a4ff 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pt-BR.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pt-BR.xlf @@ -87,6 +87,11 @@ 'end' não deve ser menor que 'start'. start='{0}' end='{1}'. + + {0}; file may be locked by {1} + {0}; file may be locked by {1} + + Generator Gerador diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ru.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ru.xlf index 7137861c3cee9..d73c99504d656 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ru.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ru.xlf @@ -87,6 +87,11 @@ Значение "end" не должно быть меньше, чем "start". start="{0}", end="{1}". + + {0}; file may be locked by {1} + {0}; file may be locked by {1} + + Generator Генератор diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.tr.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.tr.xlf index c18509b928aee..7b5f10d12a00d 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.tr.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.tr.xlf @@ -87,6 +87,11 @@ 'bitiş', 'başlangıç' değerinden küçük olmamalıdır. başla='{0}' bitiş='{1}'. + + {0}; file may be locked by {1} + {0}; file may be locked by {1} + + Generator Oluşturucu diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hans.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hans.xlf index 195a98abb1b34..db00dcb951da4 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hans.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hans.xlf @@ -87,6 +87,11 @@ "end" 不得小于 "start"。start="{0}" end="{1}"。 + + {0}; file may be locked by {1} + {0}; file may be locked by {1} + + Generator 生成器 diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hant.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hant.xlf index d0daf896b2cdd..15e41de1aac64 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hant.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hant.xlf @@ -87,6 +87,11 @@ 'end' 不可小於 'start'。start='{0}' end='{1}'。 + + {0}; file may be locked by {1} + {0}; file may be locked by {1} + + Generator 產生器 diff --git a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb index 56c4f2eea52d6..bb42d5e53c829 100644 --- a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb +++ b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb @@ -5026,7 +5026,7 @@ End Class End Sub - Public Sub UnableWriteOutput() + Public Sub UnableWriteOutput_OutputFileIsDirectory() Dim tempFolder = Temp.CreateDirectory() Dim baseDirectory = tempFolder.ToString() Dim subFolder = tempFolder.CreateDirectory("temp.dll") @@ -5042,6 +5042,34 @@ End Class CleanupAllGeneratedFiles(src.Path) End Sub + + Public Sub UnableWriteOutput_OutputFileLocked() + Dim tempFolder = Temp.CreateDirectory() + Dim baseDirectory = tempFolder.ToString() + Dim filePath = tempFolder.CreateFile("temp.dll").Path + + Dim src = Temp.CreateFile("a.vb") + src.WriteAllText("Imports System") + + Using New FileStream(filePath, FileMode.Open, FileAccess.Write, FileShare.None) + Dim currentProcess = Process.GetCurrentProcess() + + Dim outWriter As New StringWriter() + Dim exitCode As Integer = New MockVisualBasicCompiler(Nothing, baseDirectory, {"/nologo", "/preferreduilang:en", "/t:library", "/out:" & filePath, src.ToString()}).Run(outWriter, Nothing) + Assert.Equal(1, exitCode) + Dim output = outWriter.ToString().Trim() + + Dim pattern = "vbc : error BC2012: can't open '(?.*)' for writing: (?.*); file may be locked by '(?.*)' \((?.*)\)" + Dim match = Regex.Match(output, pattern) + Assert.True(match.Success, $"Expected pattern:{Environment.NewLine}{pattern}{Environment.NewLine}Actual:{Environment.NewLine}{output}") + Assert.Equal(filePath, match.Groups("path").Value) + Assert.Contains("testhost", match.Groups("app").Value) + Assert.Equal(currentProcess.Id, Integer.Parse(match.Groups("pid").Value)) + End Using + + CleanupAllGeneratedFiles(src.Path) + End Sub + Public Sub SdkPathAndLibEnvVariable() Dim parsedArgs = DefaultParse({"/libpath:c:lib2", "/sdkpath:<>,d:\sdk1", "/vbruntime*", "/nostdlib", "a.vb"}, _baseDirectory) From 9497a5ec07bdcd90dcc86cc4e29f156b6c65a1b7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 09:03:00 -0800 Subject: [PATCH 419/508] rename --- .../CSharp/Portable/CSharpResources.resx | 2 +- .../Test/Semantic/Semantics/NameOfTests.cs | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 67840d39fa4f2..85dd758873c3f 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -4933,7 +4933,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ nameof operator - Unbound generic types in nameof operator + unbound generic types in nameof operator dictionary initializer diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index 74e55cf4e90fa..f43c1101589f9 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -2389,9 +2389,9 @@ public void OpenTypeInNameof_CSharp13() var v = nameof(List<>); Console.WriteLine(v); """, parseOptions: TestOptions.Regular13).VerifyDiagnostics( - // (4,16): error CS8652: The feature 'Unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // (4,16): error CS8652: The feature 'unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // var v = nameof(List<>); - Diagnostic(ErrorCode.ERR_FeatureInPreview, "List<>").WithArguments("Unbound generic types in nameof operator").WithLocation(4, 16)); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "List<>").WithArguments("unbound generic types in nameof operator").WithLocation(4, 16)); } [Fact] @@ -2417,9 +2417,9 @@ public void OpenTypeInNameof_CSharp13_Nested1() class A { public class B; } """, parseOptions: TestOptions.Regular13).VerifyDiagnostics( - // (3,16): error CS8652: The feature 'Unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // (3,16): error CS8652: The feature 'unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // var v = nameof(A<>.B); - Diagnostic(ErrorCode.ERR_FeatureInPreview, "A<>").WithArguments("Unbound generic types in nameof operator").WithLocation(3, 16)); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "A<>").WithArguments("unbound generic types in nameof operator").WithLocation(3, 16)); } [Fact] @@ -2433,9 +2433,9 @@ public void OpenTypeInNameof_CSharp13_Nested2() class A { public class B; } """, parseOptions: TestOptions.Regular13).VerifyDiagnostics( - // (3,23): error CS8652: The feature 'Unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // (3,23): error CS8652: The feature 'unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // var v = nameof(A.B<>); - Diagnostic(ErrorCode.ERR_FeatureInPreview, "B<>").WithArguments("Unbound generic types in nameof operator").WithLocation(3, 23)); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "B<>").WithArguments("unbound generic types in nameof operator").WithLocation(3, 23)); } [Fact] @@ -2449,12 +2449,12 @@ public void OpenTypeInNameof_CSharp13_Nested3() class A { public class B; } """, parseOptions: TestOptions.Regular13).VerifyDiagnostics( - // (3,16): error CS8652: The feature 'Unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // (3,16): error CS8652: The feature 'unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // var v = nameof(A<>.B<>); - Diagnostic(ErrorCode.ERR_FeatureInPreview, "A<>").WithArguments("Unbound generic types in nameof operator").WithLocation(3, 16), - // (3,20): error CS8652: The feature 'Unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + Diagnostic(ErrorCode.ERR_FeatureInPreview, "A<>").WithArguments("unbound generic types in nameof operator").WithLocation(3, 16), + // (3,20): error CS8652: The feature 'unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // var v = nameof(A<>.B<>); - Diagnostic(ErrorCode.ERR_FeatureInPreview, "B<>").WithArguments("Unbound generic types in nameof operator").WithLocation(3, 20)); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "B<>").WithArguments("unbound generic types in nameof operator").WithLocation(3, 20)); } [Fact] From 7ed0bfe6c8a86c8386e31d29210ec33fd5ef6a5c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 09:07:44 -0800 Subject: [PATCH 420/508] do not use collection expression --- src/Compilers/CSharp/Portable/Binder/OpenTypeVisitor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Binder/OpenTypeVisitor.cs b/src/Compilers/CSharp/Portable/Binder/OpenTypeVisitor.cs index 1b1b9c1326877..7654a5e40e9e1 100644 --- a/src/Compilers/CSharp/Portable/Binder/OpenTypeVisitor.cs +++ b/src/Compilers/CSharp/Portable/Binder/OpenTypeVisitor.cs @@ -42,7 +42,7 @@ public override void VisitGenericName(GenericNameSyntax node) SeparatedSyntaxList typeArguments = node.TypeArgumentList.Arguments; if (node.IsUnboundGenericName) { - _allowedMap ??= []; + _allowedMap ??= new Dictionary(); _allowedMap[node] = !_seenConstructed; } else From 30c1a45eae94a60278fc05456bcc97a0721ab1ab Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 09:09:13 -0800 Subject: [PATCH 421/508] do not use expr body or out var --- src/Compilers/CSharp/Portable/Binder/TypeofBinder.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Binder/TypeofBinder.cs b/src/Compilers/CSharp/Portable/Binder/TypeofBinder.cs index 66e1192f235fe..7f0c3d231363e 100644 --- a/src/Compilers/CSharp/Portable/Binder/TypeofBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/TypeofBinder.cs @@ -32,6 +32,9 @@ internal TypeofBinder(ExpressionSyntax typeExpression, Binder next) } protected override bool IsUnboundTypeAllowed(GenericNameSyntax syntax) - => _allowedMap != null && _allowedMap.TryGetValue(syntax, out bool allowed) && allowed; + { + bool allowed; + return _allowedMap != null && _allowedMap.TryGetValue(syntax, out allowed) && allowed; + } } } From efeea4cc85ab26a5e72b0894c5274a5f531a4777 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 09:26:44 -0800 Subject: [PATCH 422/508] Add tesets --- .../Portable/xlf/CSharpResources.cs.xlf | 4 +- .../Portable/xlf/CSharpResources.de.xlf | 4 +- .../Portable/xlf/CSharpResources.es.xlf | 4 +- .../Portable/xlf/CSharpResources.fr.xlf | 4 +- .../Portable/xlf/CSharpResources.it.xlf | 4 +- .../Portable/xlf/CSharpResources.ja.xlf | 4 +- .../Portable/xlf/CSharpResources.ko.xlf | 4 +- .../Portable/xlf/CSharpResources.pl.xlf | 4 +- .../Portable/xlf/CSharpResources.pt-BR.xlf | 4 +- .../Portable/xlf/CSharpResources.ru.xlf | 4 +- .../Portable/xlf/CSharpResources.tr.xlf | 4 +- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 4 +- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 4 +- .../Test/Semantic/Semantics/NameOfTests.cs | 107 ++++++++++++++++++ .../Test/Semantic/Semantics/TypeOfTests.cs | 62 ++++++++++ 15 files changed, 195 insertions(+), 26 deletions(-) diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 34990bbc79621..a5d4e7d459849 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -2578,8 +2578,8 @@ - Unbound generic types in nameof operator - Unbound generic types in nameof operator + unbound generic types in nameof operator + unbound generic types in nameof operator diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index b5cfe0057f869..49661004a2962 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -2578,8 +2578,8 @@ - Unbound generic types in nameof operator - Unbound generic types in nameof operator + unbound generic types in nameof operator + unbound generic types in nameof operator diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index fe0550138a8bb..581451baade5e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -2578,8 +2578,8 @@ - Unbound generic types in nameof operator - Unbound generic types in nameof operator + unbound generic types in nameof operator + unbound generic types in nameof operator diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index bb071c083d82f..246bddbdd5cc6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -2578,8 +2578,8 @@ - Unbound generic types in nameof operator - Unbound generic types in nameof operator + unbound generic types in nameof operator + unbound generic types in nameof operator diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 5b99259647824..438db326e9229 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -2578,8 +2578,8 @@ - Unbound generic types in nameof operator - Unbound generic types in nameof operator + unbound generic types in nameof operator + unbound generic types in nameof operator diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index a59c803c0f335..e6eb82d333c76 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -2578,8 +2578,8 @@ - Unbound generic types in nameof operator - Unbound generic types in nameof operator + unbound generic types in nameof operator + unbound generic types in nameof operator diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 11686860ab693..303ca7e9dc7a5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -2578,8 +2578,8 @@ - Unbound generic types in nameof operator - Unbound generic types in nameof operator + unbound generic types in nameof operator + unbound generic types in nameof operator diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 1041c8c9b9c0c..a0d2f13e94d99 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -2578,8 +2578,8 @@ - Unbound generic types in nameof operator - Unbound generic types in nameof operator + unbound generic types in nameof operator + unbound generic types in nameof operator diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 1e34a32c674ce..e6828357934e3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -2578,8 +2578,8 @@ - Unbound generic types in nameof operator - Unbound generic types in nameof operator + unbound generic types in nameof operator + unbound generic types in nameof operator diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 85ad495f3ee09..af40b0f4b46af 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -2578,8 +2578,8 @@ - Unbound generic types in nameof operator - Unbound generic types in nameof operator + unbound generic types in nameof operator + unbound generic types in nameof operator diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 7e4009a05b044..62efb622ebeaf 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -2578,8 +2578,8 @@ - Unbound generic types in nameof operator - Unbound generic types in nameof operator + unbound generic types in nameof operator + unbound generic types in nameof operator diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 1f180771820bc..6f8caf4fd86c1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -2578,8 +2578,8 @@ - Unbound generic types in nameof operator - Unbound generic types in nameof operator + unbound generic types in nameof operator + unbound generic types in nameof operator diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 7ab84a8b739d5..c8ffa8fac8617 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -2578,8 +2578,8 @@ - Unbound generic types in nameof operator - Unbound generic types in nameof operator + unbound generic types in nameof operator + unbound generic types in nameof operator diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index f43c1101589f9..a5d6212f1c1f9 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -2825,6 +2825,113 @@ interface IGoo Diagnostic(ErrorCode.ERR_NameofMethodGroupWithTypeParameters, "IGoo.M").WithLocation(3, 16)); } + [Fact] + public void NameofFunctionPointer1() + { + CreateCompilation(""" + class C + { + unsafe void M() + { + var v = nameof(delegate*); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (5,32): error CS1514: { expected + // var v = nameof(delegate*); + Diagnostic(ErrorCode.ERR_LbraceExpected, "*").WithLocation(5, 32), + // (5,32): warning CS8848: Operator '*' cannot be used here due to precedence. Use parentheses to disambiguate. + // var v = nameof(delegate*); + Diagnostic(ErrorCode.WRN_PrecedenceInversion, "*").WithArguments("*").WithLocation(5, 32), + // (5,33): error CS1525: Invalid expression term '<' + // var v = nameof(delegate*); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "<").WithArguments("<").WithLocation(5, 33), + // (5,34): error CS1525: Invalid expression term 'int' + // var v = nameof(delegate*); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(5, 34), + // (5,38): error CS1525: Invalid expression term ')' + // var v = nameof(delegate*); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(5, 38)); + } + + [Fact] + public void NameofFunctionPointer2() + { + CreateCompilation(""" + using System.Collections.Generic; + + class C + { + unsafe void M() + { + var v = nameof(delegate*>); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (7,32): error CS1514: { expected + // var v = nameof(delegate*>); + Diagnostic(ErrorCode.ERR_LbraceExpected, "*").WithLocation(7, 32), + // (7,32): warning CS8848: Operator '*' cannot be used here due to precedence. Use parentheses to disambiguate. + // var v = nameof(delegate*>); + Diagnostic(ErrorCode.WRN_PrecedenceInversion, "*").WithArguments("*").WithLocation(7, 32), + // (7,33): error CS1525: Invalid expression term '<' + // var v = nameof(delegate*>); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "<").WithArguments("<").WithLocation(7, 33), + // (7,34): error CS0119: 'List' is a type, which is not valid in the given context + // var v = nameof(delegate*>); + Diagnostic(ErrorCode.ERR_BadSKunknown, "List<>").WithArguments("System.Collections.Generic.List", "type").WithLocation(7, 34), + // (7,41): error CS1525: Invalid expression term ')' + // var v = nameof(delegate*>); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(7, 41)); + } + + [Fact] + public void NameofFunctionPointer3() + { + CreateCompilation(""" + using System.Collections.Generic; + + class C + { + unsafe void M() + { + var v = nameof(List>); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (7,13): warning CS0219: The variable 'v' is assigned but its value is never used + // var v = nameof(List>); + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "v").WithArguments("v").WithLocation(7, 13), + // (7,29): error CS0306: The type 'delegate*' may not be used as a type argument + // var v = nameof(List>); + Diagnostic(ErrorCode.ERR_BadTypeArgument, "delegate*").WithArguments("delegate*").WithLocation(7, 29)); + } + + [Fact] + public void NameofFunctionPointer4() + { + CreateCompilation(""" + using System.Collections.Generic; + + class C + { + unsafe void M() + { + var v = nameof(List>>); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (7,13): warning CS0219: The variable 'v' is assigned but its value is never used + // var v = nameof(List>>); + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "v").WithArguments("v").WithLocation(7, 13), + // (7,29): error CS0306: The type 'delegate*>' may not be used as a type argument + // var v = nameof(List>>); + Diagnostic(ErrorCode.ERR_BadTypeArgument, "delegate*>").WithArguments("delegate*>").WithLocation(7, 29), + // (7,39): error CS7003: Unexpected use of an unbound generic name + // var v = nameof(List>>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(7, 39)); + } + [Theory] [InlineData("IGoo<>")] [InlineData("IGoo<>.Count")] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/TypeOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/TypeOfTests.cs index 61ee254c26203..d402ddeaad902 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/TypeOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/TypeOfTests.cs @@ -37,7 +37,69 @@ public C(int i) Assert.Equal("C..ctor(System.Int32 i)", symbolInfo.Symbol.ToTestDisplayString()); var typeInfo = model.GetTypeInfo(node); Assert.Equal("C", typeInfo.Type.ToTestDisplayString()); + } + + [Fact] + public void TypeofPointer() + { + CreateCompilation(""" + class C + { + unsafe void M() + { + var v = typeof(int*); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void TypeofFunctionPointer1() + { + CreateCompilation(""" + class C + { + unsafe void M() + { + var v = typeof(delegate*); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void TypeofFunctionPointer2() + { + CreateCompilation(""" + using System.Collections.Generic; + + class C + { + unsafe void M() + { + var v = typeof(delegate*,int>); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void TypeofFunctionPointer3() + { + CreateCompilation(""" + using System.Collections.Generic; + class C + { + unsafe void M() + { + var v = typeof(delegate*,int>); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (7,34): error CS7003: Unexpected use of an unbound generic name + // var v = typeof(delegate*,int>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(7, 34)); } } } From f77d4d00e0dda379397e7486daf17dcd1832d747 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 09:31:32 -0800 Subject: [PATCH 423/508] Adding tests --- .../Test/Semantic/Semantics/NameOfTests.cs | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index a5d6212f1c1f9..a24109caecf42 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -2932,6 +2932,62 @@ unsafe void M() Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(7, 39)); } + [Fact] + public void Namof_NestedOpenType1() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List[]>); + Console.WriteLine(v); + """).VerifyDiagnostics(); + } + + [Fact] + public void Namof_NestedOpenType2() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List[]>); + Console.WriteLine(v); + """).VerifyDiagnostics( + // (4,21): error CS7003: Unexpected use of an unbound generic name + // var v = nameof(List[]>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(4, 21)); + } + + [Fact] + public void Namof_NestedOpenType3() + { + CreateCompilation(""" + #nullable enable + using System; + using System.Collections.Generic; + + var v = nameof(List?>); + Console.WriteLine(v); + """).VerifyDiagnostics(); + } + + [Fact] + public void Namof_NestedOpenType4() + { + CreateCompilation(""" + #nullable enable + using System; + using System.Collections.Generic; + + var v = nameof(List?>); + Console.WriteLine(v); + """).VerifyDiagnostics( + // (5,21): error CS7003: Unexpected use of an unbound generic name + // var v = nameof(List?>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(5, 21)); + } + [Theory] [InlineData("IGoo<>")] [InlineData("IGoo<>.Count")] From 0a3e50a2a2bf6dae5ac2e84fb84b99b63c244636 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 09:48:54 -0800 Subject: [PATCH 424/508] use explicit else clause --- .../CSharp/Portable/Binder/Binder_Symbols.cs | 10 ++++++---- .../Test/Semantic/Semantics/NameOfTests.cs | 19 +++++++++++++++---- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index 5b1564b9e09d7..b3c42874d46a0 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -1401,10 +1401,12 @@ private NamedTypeSymbol ConstructNamedTypeUnlessTypeArgumentOmitted(SyntaxNode t return type; } - - // we pass an empty basesBeingResolved here because this invocation is not on any possible path of - // infinite recursion in binding base clauses. - return ConstructNamedType(type, typeSyntax, typeArgumentsSyntax, typeArguments, basesBeingResolved: null, diagnostics: diagnostics); + else + { + // we pass an empty basesBeingResolved here because this invocation is not on any possible path of + // infinite recursion in binding base clauses. + return ConstructNamedType(type, typeSyntax, typeArgumentsSyntax, typeArguments, basesBeingResolved: null, diagnostics: diagnostics); + } } /// diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index a24109caecf42..a22a10cb8453a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -2933,7 +2933,7 @@ unsafe void M() } [Fact] - public void Namof_NestedOpenType1() + public void Nameof_NestedOpenType1() { CreateCompilation(""" using System; @@ -2945,7 +2945,7 @@ public void Namof_NestedOpenType1() } [Fact] - public void Namof_NestedOpenType2() + public void Nameof_NestedOpenType2() { CreateCompilation(""" using System; @@ -2960,7 +2960,7 @@ public void Namof_NestedOpenType2() } [Fact] - public void Namof_NestedOpenType3() + public void Nameof_NestedOpenType3() { CreateCompilation(""" #nullable enable @@ -2973,7 +2973,7 @@ public void Namof_NestedOpenType3() } [Fact] - public void Namof_NestedOpenType4() + public void Nameof_NestedOpenType4() { CreateCompilation(""" #nullable enable @@ -2988,6 +2988,17 @@ public void Namof_NestedOpenType4() Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(5, 21)); } + [Fact] + public void Nameof_AliasQualifiedName() + { + CreateCompilation(""" + using System; + + var v = nameof(global::System.Collections.Generic.List<>); + Console.WriteLine(v); + """).VerifyDiagnostics(); + } + [Theory] [InlineData("IGoo<>")] [InlineData("IGoo<>.Count")] From 21d4a4e19601b8e39d8479b76700797b679f37f8 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 10:18:58 -0800 Subject: [PATCH 425/508] validate runtime behavior --- .../Test/Semantic/Semantics/NameOfTests.cs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index a22a10cb8453a..cf4ff3cdac972 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -2370,13 +2370,13 @@ class Attr : System.Attribute { public Attr(string s) {} }"; [Fact] public void OpenTypeInNameof_Preview() { - CreateCompilation(""" + CompileAndVerify(""" using System; using System.Collections.Generic; var v = nameof(List<>); Console.WriteLine(v); - """).VerifyDiagnostics(); + """, expectedOutput: "List").VerifyDiagnostics(); } [Fact] @@ -2397,13 +2397,13 @@ public void OpenTypeInNameof_CSharp13() [Fact] public void OpenTypeInNameof_Next() { - CreateCompilation(""" + CompileAndVerify(""" using System; using System.Collections.Generic; var v = nameof(List<>); Console.WriteLine(v); - """, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(); + """, parseOptions: TestOptions.RegularNext, expectedOutput: "List").VerifyDiagnostics(); } [Fact] @@ -2623,25 +2623,25 @@ public class Outer { public class Inner { } } [Fact] public void Nameof_NestedClosedType1() { - CreateCompilation(""" + CompileAndVerify(""" using System; using System.Collections.Generic; var v = nameof(List>); Console.WriteLine(v); - """).VerifyDiagnostics(); + """, expectedOutput: "List").VerifyDiagnostics(); } [Fact] public void Nameof_NestedClosedType2() { - CreateCompilation(""" + CompileAndVerify(""" using System; using System.Collections.Generic; var v = nameof(List[]>); Console.WriteLine(v); - """).VerifyDiagnostics(); + """, expectedOutput: "List").VerifyDiagnostics(); } [Fact] @@ -2935,13 +2935,13 @@ unsafe void M() [Fact] public void Nameof_NestedOpenType1() { - CreateCompilation(""" + CompileAndVerify(""" using System; using System.Collections.Generic; var v = nameof(List[]>); Console.WriteLine(v); - """).VerifyDiagnostics(); + """, expectedOutput: "List").VerifyDiagnostics(); } [Fact] @@ -2962,14 +2962,14 @@ public void Nameof_NestedOpenType2() [Fact] public void Nameof_NestedOpenType3() { - CreateCompilation(""" + CompileAndVerify(""" #nullable enable using System; using System.Collections.Generic; var v = nameof(List?>); Console.WriteLine(v); - """).VerifyDiagnostics(); + """, expectedOutput: "List").VerifyDiagnostics(); } [Fact] @@ -2991,12 +2991,12 @@ public void Nameof_NestedOpenType4() [Fact] public void Nameof_AliasQualifiedName() { - CreateCompilation(""" + CompileAndVerify(""" using System; var v = nameof(global::System.Collections.Generic.List<>); Console.WriteLine(v); - """).VerifyDiagnostics(); + """, expectedOutput: "List").VerifyDiagnostics(); } [Theory] From 0be4c20136337d9555e58d86d089954a2c443cad Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 10:19:48 -0800 Subject: [PATCH 426/508] Do not use expression bodies or patterns --- src/Compilers/Test/Utilities/CSharp/Extensions.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Compilers/Test/Utilities/CSharp/Extensions.cs b/src/Compilers/Test/Utilities/CSharp/Extensions.cs index 13cd405a7b597..6b4e838a76726 100644 --- a/src/Compilers/Test/Utilities/CSharp/Extensions.cs +++ b/src/Compilers/Test/Utilities/CSharp/Extensions.cs @@ -764,7 +764,9 @@ public static ImmutableArray GetParameters(this ISymbol member } public static bool IsUnboundGenericType(this ITypeSymbol type) - => type is INamedTypeSymbol { IsUnboundGenericType: true }; + { + return type is INamedTypeSymbol namedType && namedType.IsUnboundGenericType; + } public static bool GivesAccessTo(this AssemblySymbol first, AssemblySymbol second) { From 530876b64ab9f2534b265812be609687a8f46c40 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 10:41:30 -0800 Subject: [PATCH 427/508] Update extract method tests --- .../CSharpTest/ExtractMethod/ExtractLocalFunctionTests.cs | 4 ++-- src/Features/CSharpTest/ExtractMethod/ExtractMethodTests2.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Features/CSharpTest/ExtractMethod/ExtractLocalFunctionTests.cs b/src/Features/CSharpTest/ExtractMethod/ExtractLocalFunctionTests.cs index bf1cefc748d8e..cc38ca4485195 100644 --- a/src/Features/CSharpTest/ExtractMethod/ExtractLocalFunctionTests.cs +++ b/src/Features/CSharpTest/ExtractMethod/ExtractLocalFunctionTests.cs @@ -590,7 +590,7 @@ class Program static void Main() { byte z = 0; - Goo({|Rename:NewMethod|}(), y => (byte)0, z, z); + Goo({|Rename:NewMethod|}(), y => 0, z, z); static Func NewMethod() { @@ -632,7 +632,7 @@ class Program static void Main() { byte z = 0; - Goo({|Rename:NewMethod|}(), y => { return (byte)0; }, z, z); + Goo({|Rename:NewMethod|}(), y => { return 0; }, z, z); static Func NewMethod() { diff --git a/src/Features/CSharpTest/ExtractMethod/ExtractMethodTests2.cs b/src/Features/CSharpTest/ExtractMethod/ExtractMethodTests2.cs index da95d9798d456..7492fb1be3169 100644 --- a/src/Features/CSharpTest/ExtractMethod/ExtractMethodTests2.cs +++ b/src/Features/CSharpTest/ExtractMethod/ExtractMethodTests2.cs @@ -840,7 +840,7 @@ class Program static void Main() { byte z = 0; - Goo({|Rename:NewMethod|}(), y => (byte)0, z, z); + Goo({|Rename:NewMethod|}(), y => 0, z, z); } private static Func NewMethod() @@ -882,7 +882,7 @@ class Program static void Main() { byte z = 0; - Goo({|Rename:NewMethod|}(), y => { return (byte)0; }, z, z); + Goo({|Rename:NewMethod|}(), y => { return 0; }, z, z); } private static Func NewMethod() From 3eb510284ee6c8dda5f0c960226c7f0dfd574ca4 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 10:54:55 -0800 Subject: [PATCH 428/508] Fixup test --- .../Test2/Simplification/CastSimplificationTests.vb | 2 +- .../Simplification/CSharpSimplificationService.cs | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/EditorFeatures/Test2/Simplification/CastSimplificationTests.vb b/src/EditorFeatures/Test2/Simplification/CastSimplificationTests.vb index 9791137b56170..375418d345861 100644 --- a/src/EditorFeatures/Test2/Simplification/CastSimplificationTests.vb +++ b/src/EditorFeatures/Test2/Simplification/CastSimplificationTests.vb @@ -704,7 +704,7 @@ class C void M() { System.Action<string> g = null; - var h = (Goo<string>) + g; + var h = (Goo) + g; } static void Goo<T>(T y) { } diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.cs b/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.cs index 188d9122e8376..f2ed9d5989834 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.cs @@ -23,15 +23,16 @@ namespace Microsoft.CodeAnalysis.CSharp.Simplification; [ExportLanguageService(typeof(ISimplificationService), LanguageNames.CSharp), Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal partial class CSharpSimplificationService() +internal sealed partial class CSharpSimplificationService() : AbstractSimplificationService(s_reducers) { - // 1. the cast simplifier should run earlier then everything else to minimize the type expressions - // 2. Extension method reducer may insert parentheses. So run it before the parentheses remover. + // 1. Prefer 'var' simplification first. In other words we like `var v = (int)x` vs `int v = x` + // 2. the cast simplifier should run earlier then everything else to minimize the type expressions + // 3. Extension method reducer may insert parentheses. So run it before the parentheses remover. private static readonly ImmutableArray s_reducers = [ - new CSharpCastReducer(), new CSharpVarReducer(), + new CSharpCastReducer(), new CSharpNameReducer(), new CSharpNullableAnnotationReducer(), new CSharpExtensionMethodReducer(), From a0e8e13ea257931c9ba260c07c2bac5050c27b75 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 11:00:22 -0800 Subject: [PATCH 429/508] Add typeof teest --- .../Test/Semantic/Semantics/TypeOfTests.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/TypeOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/TypeOfTests.cs index d402ddeaad902..fbb75ecdb1d65 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/TypeOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/TypeOfTests.cs @@ -101,5 +101,27 @@ unsafe void M() // var v = typeof(delegate*,int>); Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(7, 34)); } + + [Fact] + public void TypeofFunctionPointer4() + { + CreateCompilation(""" + using System.Collections.Generic; + + class D + { + unsafe void M() + { + var v = typeof(D<, delegate*, List<>>); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (7,26): error CS1031: Type expected + // var v = typeof(D<, delegate*, List<>>); + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(7, 26), + // (7,44): error CS7003: Unexpected use of an unbound generic name + // var v = typeof(D<, delegate*, List<>>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(7, 44)); + } } } From 4d19b869eb3b3625eb142649d7a53b5ee65cf2b4 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 11:02:06 -0800 Subject: [PATCH 430/508] Add nameof teest --- .../Test/Semantic/Semantics/NameOfTests.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index cf4ff3cdac972..216a495096a2a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -2932,6 +2932,28 @@ unsafe void M() Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(7, 39)); } + [Fact] + public void NameofFunctionPointer5() + { + CreateCompilation(""" + using System.Collections.Generic; + + class D + { + unsafe void M() + { + var v = nameof(D<, delegate*, List<>>); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (7,26): error CS1031: Type expected + // var v = nameof(D<, delegate*, List<>>); + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(7, 26), + // (7,44): error CS7003: Unexpected use of an unbound generic name + // var v = nameof(D<, delegate*, List<>>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(7, 44)); + } + [Fact] public void Nameof_NestedOpenType1() { From dd327909f30b2196b3a2366d45e04283d4630e27 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 11:30:44 -0800 Subject: [PATCH 431/508] Add analyzer --- .../Analyzers/CSharpAnalyzers.projitems | 1 + .../Analyzers/CSharpAnalyzersResources.resx | 3 + .../CSharpAnalyzerOptionsProvider.cs | 1 + ...seUnboundTypeInNameOfDiagnosticAnalyzer.cs | 91 +++++++++++++++++++ .../xlf/CSharpAnalyzersResources.cs.xlf | 5 + .../xlf/CSharpAnalyzersResources.de.xlf | 5 + .../xlf/CSharpAnalyzersResources.es.xlf | 5 + .../xlf/CSharpAnalyzersResources.fr.xlf | 5 + .../xlf/CSharpAnalyzersResources.it.xlf | 5 + .../xlf/CSharpAnalyzersResources.ja.xlf | 5 + .../xlf/CSharpAnalyzersResources.ko.xlf | 5 + .../xlf/CSharpAnalyzersResources.pl.xlf | 5 + .../xlf/CSharpAnalyzersResources.pt-BR.xlf | 5 + .../xlf/CSharpAnalyzersResources.ru.xlf | 5 + .../xlf/CSharpAnalyzersResources.tr.xlf | 5 + .../xlf/CSharpAnalyzersResources.zh-Hans.xlf | 5 + .../xlf/CSharpAnalyzersResources.zh-Hant.xlf | 5 + .../Core/Analyzers/EnforceOnBuildValues.cs | 1 + .../Core/Analyzers/IDEDiagnosticIds.cs | 2 + .../CodeStyle/CSharpCodeStyleOptions.cs | 4 + .../Extensions/LanguageVersionExtensions.cs | 3 + 21 files changed, 171 insertions(+) create mode 100644 src/Analyzers/CSharp/Analyzers/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfDiagnosticAnalyzer.cs diff --git a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems index ba405268c06a2..32d0471897a72 100644 --- a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems +++ b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems @@ -155,6 +155,7 @@ + diff --git a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx index 5fe66d4bd5183..80bef57ff4fc5 100644 --- a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx +++ b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx @@ -422,4 +422,7 @@ Use 'System.Threading.Lock' {Locked="System.Threading.Lock"} "System.Threading.Lock" is the name of a .Net type and should not be localized. + + Use unbound type + \ No newline at end of file diff --git a/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs b/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs index 0bd7b8598310b..83667b65925c2 100644 --- a/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs +++ b/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs @@ -69,6 +69,7 @@ internal CSharpSimplifierOptions GetSimplifierOptions() public CodeStyleOption2 PreferMethodGroupConversion => GetOption(CSharpCodeStyleOptions.PreferMethodGroupConversion); public CodeStyleOption2 PreferPrimaryConstructors => GetOption(CSharpCodeStyleOptions.PreferPrimaryConstructors); public CodeStyleOption2 PreferSystemThreadingLock => GetOption(CSharpCodeStyleOptions.PreferSystemThreadingLock); + public CodeStyleOption2 PreferUnboundTypeInNameOf => GetOption(CSharpCodeStyleOptions.PreferUnboundTypeInNameOf); // CodeGenerationOptions diff --git a/src/Analyzers/CSharp/Analyzers/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfDiagnosticAnalyzer.cs new file mode 100644 index 0000000000000..7b4dbacdd0cce --- /dev/null +++ b/src/Analyzers/CSharp/Analyzers/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfDiagnosticAnalyzer.cs @@ -0,0 +1,91 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Extensions; +using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.CSharp.UseUnboundTypeInNameOf; + +/// +/// Looks for code of the form: +/// +/// +/// nameof(List<...>) +/// +/// +/// and converts it to: +/// +/// +/// nameof(List<>) +/// +/// +/// +[DiagnosticAnalyzer(LanguageNames.CSharp)] +internal sealed class CSharpUseUnboundTypeInNameOfDiagnosticAnalyzer() + : AbstractBuiltInCodeStyleDiagnosticAnalyzer( + IDEDiagnosticIds.UseUnboundTypeInNameOfDiagnosticId, + EnforceOnBuildValues.UseUnboundTypeInNameOf, + CSharpCodeStyleOptions.PreferUnboundTypeInNameOf, + new LocalizableResourceString( + nameof(CSharpAnalyzersResources.Use_unbound_type), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources))) +{ + public override DiagnosticAnalyzerCategory GetAnalyzerCategory() + => DiagnosticAnalyzerCategory.SemanticSpanAnalysis; + + protected override void InitializeWorker(AnalysisContext context) + { + context.RegisterCompilationStartAction(context => + { + // Tuples are only available in C# 14 and above. + var compilation = context.Compilation; + if (compilation.LanguageVersion().IsCSharp14OrAbove()) + return; + + context.RegisterSyntaxNodeAction( + AnalyzeInvocationExpression, + SyntaxKind.InvocationExpression); + }); + } + + private void AnalyzeInvocationExpression(SyntaxNodeAnalysisContext syntaxContext) + { + var cancellationToken = syntaxContext.CancellationToken; + var styleOption = syntaxContext.GetCSharpAnalyzerOptions().PreferUnboundTypeInNameOf; + if (!styleOption.Value || ShouldSkipAnalysis(syntaxContext, styleOption.Notification)) + return; + + var invocation = (InvocationExpressionSyntax)syntaxContext.Node; + if (!invocation.IsNameOfInvocation()) + return; + + foreach (var typeArgumentList in invocation.DescendantNodesAndSelf().OfType()) + { + foreach (var argument in typeArgumentList.Arguments) + { + if (argument.Kind() != SyntaxKind.OmittedTypeArgument) + { + syntaxContext.ReportDiagnostic(DiagnosticHelper.CreateWithLocationTags( + Descriptor, + invocation.GetFirstToken().GetLocation(), + styleOption.Notification, + syntaxContext.Options, + [invocation.GetLocation()], + additionalUnnecessaryLocations: [invocation.SyntaxTree.GetLocation( + TextSpan.FromBounds(typeArgumentList.LessThanToken.Span.End, typeArgumentList.GreaterThanToken.Span.Start))])); + + return; + } + } + } + } +} diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf index 62f46796ecfab..4120b434e04c8 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf @@ -422,6 +422,11 @@ Prohození hodnot pomocí řazené kolekce členů + + Use unbound type + Use unbound type + + Using directives must be placed inside of a namespace declaration Direktivy using se musí umístit do deklarace namespace. diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf index accb3909571de..1ecca56c733c9 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf @@ -422,6 +422,11 @@ Tupel zum Tauschen von Werten verwenden + + Use unbound type + Use unbound type + + Using directives must be placed inside of a namespace declaration Using-Anweisungen müssen in einer namespace-Deklaration platziert werden. diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf index d7855c6a310b3..7a3316a683e24 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf @@ -422,6 +422,11 @@ Utilizar tupla para intercambiar valores + + Use unbound type + Use unbound type + + Using directives must be placed inside of a namespace declaration Las directivas using deben colocarse dentro de una declaración de namespace diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf index e8f1a51941216..554357ff6103a 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf @@ -422,6 +422,11 @@ Utilisez le tuple pour échanger des valeurs + + Use unbound type + Use unbound type + + Using directives must be placed inside of a namespace declaration Les directives using doivent être placées dans une déclaration namespace diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf index 4568f9346ec71..9f5e02b744ead 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf @@ -422,6 +422,11 @@ Usa la tupla per scambiare i valori + + Use unbound type + Use unbound type + + Using directives must be placed inside of a namespace declaration Le direttive using devono essere inserite all'interno di una dichiarazione di namespace diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf index 40b6033cee3da..f8e4896ed13ef 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf @@ -422,6 +422,11 @@ タプルを使用して値をスワップする + + Use unbound type + Use unbound type + + Using directives must be placed inside of a namespace declaration using ディレクティブを namespace 宣言の中に配置する必要があります diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf index a450fc7a71c47..18a8a2428729d 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf @@ -422,6 +422,11 @@ 튜플을 사용하여 값 바꾸기 + + Use unbound type + Use unbound type + + Using directives must be placed inside of a namespace declaration Using 지시문은 namespace 선언 내부에 배치되어야 합니다. diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf index f4e2d9f1ce44e..2ba611f598b83 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf @@ -422,6 +422,11 @@ Użyj krotki do zamiany wartości + + Use unbound type + Use unbound type + + Using directives must be placed inside of a namespace declaration Dyrektywy using muszą znajdować się wewnątrz deklaracji namespace diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf index d23edd5e0edf4..b900d31459b5d 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf @@ -422,6 +422,11 @@ Usar a tupla para trocar valores + + Use unbound type + Use unbound type + + Using directives must be placed inside of a namespace declaration As diretivas using precisam ser colocadas dentro de uma declaração de namespace diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf index ecb6219478e28..fb664006d0c91 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf @@ -422,6 +422,11 @@ Использовать кортеж для переключения значений + + Use unbound type + Use unbound type + + Using directives must be placed inside of a namespace declaration Директивы using должны находиться внутри объявления namespace diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf index 1b93fd9b6ccad..00a5b3754369e 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf @@ -422,6 +422,11 @@ Değerleri değiştirmek için demeti kullanın + + Use unbound type + Use unbound type + + Using directives must be placed inside of a namespace declaration Using yönergelerinin bir namespace bildiriminin içine yerleştirilmesi gerekir diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf index ea18e9dbd42f5..aae6920af745f 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf @@ -422,6 +422,11 @@ 使用元组交换值 + + Use unbound type + Use unbound type + + Using directives must be placed inside of a namespace declaration using 指令必须放在 namespace 声明的内部 diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf index ae7a62524cb9c..12ee577953fcf 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf @@ -422,6 +422,11 @@ 使用元組交換值 + + Use unbound type + Use unbound type + + Using directives must be placed inside of a namespace declaration using 指示詞必須放在 namespace 宣告內 diff --git a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs index 16bc901f9533a..0da7ce88a6b53 100644 --- a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs +++ b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs @@ -99,6 +99,7 @@ internal static class EnforceOnBuildValues public const EnforceOnBuild UseCollectionExpressionForNew = /*IDE0306*/ EnforceOnBuild.Recommended; public const EnforceOnBuild MakeAnonymousFunctionStatic = /*IDE0320*/ EnforceOnBuild.Recommended; public const EnforceOnBuild UseSystemThreadingLock = /*IDE0330*/ EnforceOnBuild.Recommended; + public const EnforceOnBuild UseUnboundTypeInNameOf = /*IDE0340*/ EnforceOnBuild.Recommended; /* EnforceOnBuild.WhenExplicitlyEnabled */ public const EnforceOnBuild RemoveUnnecessaryCast = /*IDE0004*/ EnforceOnBuild.WhenExplicitlyEnabled; // TODO: Move to 'Recommended' OR 'HighlyRecommended' bucket once performance problems are addressed: https://github.com/dotnet/roslyn/issues/43304 diff --git a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs index abb14572b56d6..3a9749fbe2faf 100644 --- a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs +++ b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs @@ -204,6 +204,8 @@ internal static class IDEDiagnosticIds public const string UseSystemThreadingLockDiagnosticId = "IDE0330"; + public const string UseUnboundTypeInNameOfDiagnosticId = "IDE0340"; + // Analyzer error Ids public const string AnalyzerChangedId = "IDE1001"; public const string AnalyzerDependencyConflictId = "IDE1002"; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs index 358c3dd605363..ff49ffeafa15e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs @@ -88,6 +88,10 @@ private static Option2> CreateOption( CodeStyleOptionGroups.ExpressionLevelPreferences, "csharp_style_prefer_utf8_string_literals", defaultValue: CodeStyleOption2.TrueWithSuggestionEnforcement); + public static readonly Option2> PreferUnboundTypeInNameOf = CreateOption( + CodeStyleOptionGroups.ExpressionLevelPreferences, "csharp_style_prefer_unbound_type_in_nameof", + defaultValue: CodeStyleOption2.TrueWithSuggestionEnforcement); + public static readonly CodeStyleOption2 NeverWithSilentEnforcement = new(ExpressionBodyPreference.Never, NotificationOption2.Silent); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/LanguageVersionExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/LanguageVersionExtensions.cs index 7f451630e9f61..d4aa3c168bb89 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/LanguageVersionExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/LanguageVersionExtensions.cs @@ -6,6 +6,9 @@ namespace Microsoft.CodeAnalysis.CSharp.Shared.Extensions; internal static class LanguageVersionExtensions { + public static bool IsCSharp14OrAbove(this LanguageVersion languageVersion) + => languageVersion >= LanguageVersion.Preview; + public static bool IsCSharp13OrAbove(this LanguageVersion languageVersion) => languageVersion >= LanguageVersion.CSharp13; From b2e545d1c30af6a48a282d254128f0f2717bd768 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 11:45:18 -0800 Subject: [PATCH 432/508] Add tests --- ...seUnboundTypeInNameOfDiagnosticAnalyzer.cs | 5 +- .../CodeFixes/CSharpCodeFixes.projitems | 1 + ...rpUseUnboundTypeInNameOfCodeFixProvider.cs | 66 ++++++++++++ .../Tests/CSharpAnalyzers.UnitTests.projitems | 1 + .../Tests/UseTupleSwap/UseTupleSwapTests.cs | 2 +- .../UseUnboundTypeInNameOfTests.cs | 100 ++++++++++++++++++ .../PredefinedCodeFixProviderNames.cs | 1 + src/Compilers/Test/Core/Traits/Traits.cs | 2 + 8 files changed, 173 insertions(+), 5 deletions(-) create mode 100644 src/Analyzers/CSharp/CodeFixes/UseUnboundTypeInNameOf/CSharpUseUnboundTypeInNameOfCodeFixProvider.cs create mode 100644 src/Analyzers/CSharp/Tests/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfTests.cs diff --git a/src/Analyzers/CSharp/Analyzers/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfDiagnosticAnalyzer.cs index 7b4dbacdd0cce..a49846a7d4429 100644 --- a/src/Analyzers/CSharp/Analyzers/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfDiagnosticAnalyzer.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; @@ -11,7 +9,6 @@ using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.UseUnboundTypeInNameOf; @@ -48,7 +45,7 @@ protected override void InitializeWorker(AnalysisContext context) { // Tuples are only available in C# 14 and above. var compilation = context.Compilation; - if (compilation.LanguageVersion().IsCSharp14OrAbove()) + if (!compilation.LanguageVersion().IsCSharp14OrAbove()) return; context.RegisterSyntaxNodeAction( diff --git a/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems b/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems index 74ad3aea9f2a2..0993c5600de6a 100644 --- a/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems +++ b/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems @@ -176,6 +176,7 @@ + diff --git a/src/Analyzers/CSharp/CodeFixes/UseUnboundTypeInNameOf/CSharpUseUnboundTypeInNameOfCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseUnboundTypeInNameOf/CSharpUseUnboundTypeInNameOfCodeFixProvider.cs new file mode 100644 index 0000000000000..955fe33c458a2 --- /dev/null +++ b/src/Analyzers/CSharp/CodeFixes/UseUnboundTypeInNameOf/CSharpUseUnboundTypeInNameOfCodeFixProvider.cs @@ -0,0 +1,66 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Extensions; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Shared.Extensions; + +namespace Microsoft.CodeAnalysis.CSharp.UseUnboundTypeInNameOf; + +using static SyntaxFactory; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.UseUnboundTypeInNameOf), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed partial class CSharpUseUnboundTypeInNameOfCodeFixProvider() : SyntaxEditorBasedCodeFixProvider +{ + private static readonly SyntaxNodeOrToken s_omittedArgument = (SyntaxNodeOrToken)OmittedTypeArgument(); + + public override ImmutableArray FixableDiagnosticIds { get; } + = [IDEDiagnosticIds.UseUnboundTypeInNameOfDiagnosticId]; + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + RegisterCodeFix(context, CSharpAnalyzersResources.Use_unbound_type, nameof(CSharpAnalyzersResources.Use_unbound_type)); + return Task.CompletedTask; + } + + protected override Task FixAllAsync( + Document document, ImmutableArray diagnostics, + SyntaxEditor editor, CancellationToken cancellationToken) + { + foreach (var diagnostic in diagnostics) + FixOne(editor, diagnostic, cancellationToken); + + return Task.CompletedTask; + } + + private static void FixOne( + SyntaxEditor editor, Diagnostic diagnostic, CancellationToken cancellationToken) + { + var nameofInvocation = (InvocationExpressionSyntax)diagnostic.AdditionalLocations[0].FindNode(getInnermostNodeForTie: true, cancellationToken); + if (!nameofInvocation.IsNameOfInvocation()) + return; + + foreach (var typeArgumentList in nameofInvocation.DescendantNodes().OfType().OrderByDescending(t => t.SpanStart)) + { + if (typeArgumentList.Arguments.Any(a => a.Kind() != SyntaxKind.OmittedTypeArgument)) + { + var list = NodeOrTokenList(typeArgumentList.Arguments.GetWithSeparators().Select( + t => t.IsToken ? t.AsToken().WithoutTrivia() : s_omittedArgument)); + editor.ReplaceNode(typeArgumentList, typeArgumentList.WithArguments(SeparatedList(list))); + } + } + } +} diff --git a/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems b/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems index 4930c8dd698b1..e2fbf6cc404a6 100644 --- a/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems +++ b/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems @@ -185,6 +185,7 @@ + diff --git a/src/Analyzers/CSharp/Tests/UseTupleSwap/UseTupleSwapTests.cs b/src/Analyzers/CSharp/Tests/UseTupleSwap/UseTupleSwapTests.cs index dd1ac5b1f3dc4..c3befc7094af4 100644 --- a/src/Analyzers/CSharp/Tests/UseTupleSwap/UseTupleSwapTests.cs +++ b/src/Analyzers/CSharp/Tests/UseTupleSwap/UseTupleSwapTests.cs @@ -18,7 +18,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseTupleSwap; CSharpUseTupleSwapDiagnosticAnalyzer, CSharpUseTupleSwapCodeFixProvider>; -[Trait(Traits.Feature, Traits.Features.CodeActionsUseLocalFunction)] +[Trait(Traits.Feature, Traits.Features.CodeActionsUseTupleSwap)] public partial class UseTupleSwapTests { [Fact] diff --git a/src/Analyzers/CSharp/Tests/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfTests.cs b/src/Analyzers/CSharp/Tests/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfTests.cs new file mode 100644 index 0000000000000..4b3468df9d08e --- /dev/null +++ b/src/Analyzers/CSharp/Tests/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfTests.cs @@ -0,0 +1,100 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; +using Microsoft.CodeAnalysis.CSharp.UseTupleSwap; +using Microsoft.CodeAnalysis.CSharp.UseUnboundTypeInNameOf; +using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Testing; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseUnboundTypeInNameOf; + +using VerifyCS = CSharpCodeFixVerifier< + CSharpUseUnboundTypeInNameOfDiagnosticAnalyzer, + CSharpUseUnboundTypeInNameOfCodeFixProvider>; + +[Trait(Traits.Feature, Traits.Features.CodeActionsUseUnboundTypeInNameOf)] +public partial class UseUnboundTypeInNameOfTests +{ + [Fact] + public async Task TestBaseCase() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = [|nameof|](List); + } + } + """, + FixedCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = nameof(List<>); + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestMissingBeforeCSharp14() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = nameof(List); + } + } + """, + LanguageVersion = LanguageVersion.CSharp13, + }.RunAsync(); + } + + [Fact] + public async Task TestMissingWithFeatureOff() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = nameof(List); + } + } + """, + Options = + { + { CSharpCodeStyleOptions.PreferUnboundTypeInNameOf, false, CodeStyle.NotificationOption2.Silent } + }, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } +} diff --git a/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs b/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs index 9331fb48b1947..af2c3512a5167 100644 --- a/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs +++ b/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs @@ -178,5 +178,6 @@ internal static class PredefinedCodeFixProviderNames public const string UseSystemThreadingLock = nameof(UseSystemThreadingLock); public const string UseThrowExpression = nameof(UseThrowExpression); public const string UseTupleSwap = nameof(UseTupleSwap); + public const string UseUnboundTypeInNameOf = nameof(UseUnboundTypeInNameOf); public const string UseUtf8StringLiteral = nameof(UseUtf8StringLiteral); } diff --git a/src/Compilers/Test/Core/Traits/Traits.cs b/src/Compilers/Test/Core/Traits/Traits.cs index 0aeb366503abe..88fee8711d2bf 100644 --- a/src/Compilers/Test/Core/Traits/Traits.cs +++ b/src/Compilers/Test/Core/Traits/Traits.cs @@ -218,6 +218,8 @@ public static class Features public const string CodeActionsUseSystemHashCode = "CodeActions.UseSystemHashCode"; public const string CodeActionsUseSystemThreadingLock = "CodeActions.UseSystemThreadingLock"; public const string CodeActionsUseThrowExpression = "CodeActions.UseThrowExpression"; + public const string CodeActionsUseTupleSwap = "CodeActions.UseTupleSwap"; + public const string CodeActionsUseUnboundTypeInNameOf = "CodeActions.UseUnboundTypeInNameOf"; public const string CodeActionsUseUtf8StringLiteral = "CodeActions.CodeActionsUseUtf8StringLiteral"; public const string CodeActionsWrapping = "CodeActions.Wrapping"; public const string CodeCleanup = nameof(CodeCleanup); From aaa10cb351e272559164053b2e94736bddad4092 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 11:50:40 -0800 Subject: [PATCH 433/508] Add tests --- .../UseUnboundTypeInNameOfTests.cs | 220 ++++++++++++++++++ 1 file changed, 220 insertions(+) diff --git a/src/Analyzers/CSharp/Tests/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfTests.cs b/src/Analyzers/CSharp/Tests/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfTests.cs index 4b3468df9d08e..a0fcbcf479e7e 100644 --- a/src/Analyzers/CSharp/Tests/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfTests.cs +++ b/src/Analyzers/CSharp/Tests/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfTests.cs @@ -97,4 +97,224 @@ void M(string[] args) LanguageVersion = LanguageVersionExtensions.CSharpNext, }.RunAsync(); } + + [Fact] + public async Task TestMultipleTypeArguments() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = [|nameof|](Dictionary); + } + } + """, + FixedCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = nameof(Dictionary<,>); + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestGlobal() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = [|nameof|](global::System.Collections.Generic.Dictionary); + } + } + """, + FixedCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = nameof(global::System.Collections.Generic.Dictionary<,>); + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestNestedArgs() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = [|nameof|](Dictionary, string>); + } + } + """, + FixedCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = nameof(Dictionary<,>); + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestNestedType1() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = [|nameof|](Outer.Inner); + } + } + + class Outer { public class Inner { } } + """, + FixedCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = nameof(Outer<>.Inner<>); + } + } + + class Outer { public class Inner { } } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestNestedType2() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = [|nameof|](Outer.Inner<>); + } + } + + class Outer { public class Inner { } } + """, + FixedCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = nameof(Outer<>.Inner<>); + } + } + + class Outer { public class Inner { } } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestNestedType3() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = [|nameof|](Outer<>.Inner); + } + } + + class Outer { public class Inner { } } + """, + FixedCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = nameof(Outer<>.Inner<>); + } + } + + class Outer { public class Inner { } } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestNestedType4() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = nameof(Outer<>.Inner<>); + } + } + + class Outer { public class Inner { } } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } } From 959bb32ed9273447fd2e9389e7dbb120fd3380f0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 11:51:40 -0800 Subject: [PATCH 434/508] Add tests --- .../UseUnboundTypeInNameOfTests.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/Analyzers/CSharp/Tests/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfTests.cs b/src/Analyzers/CSharp/Tests/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfTests.cs index a0fcbcf479e7e..ca14189f92e65 100644 --- a/src/Analyzers/CSharp/Tests/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfTests.cs +++ b/src/Analyzers/CSharp/Tests/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfTests.cs @@ -54,6 +54,26 @@ void M(string[] args) }.RunAsync(); } + [Fact] + public async Task TestNotIfAlreadyOmitted() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = nameof(List<>); + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + [Fact] public async Task TestMissingBeforeCSharp14() { From d57368960a4107df03133a2f507df86718727f64 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 11:56:45 -0800 Subject: [PATCH 435/508] make sealed --- src/Analyzers/CSharp/Tests/UseTupleSwap/UseTupleSwapTests.cs | 2 +- .../Tests/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Analyzers/CSharp/Tests/UseTupleSwap/UseTupleSwapTests.cs b/src/Analyzers/CSharp/Tests/UseTupleSwap/UseTupleSwapTests.cs index c3befc7094af4..59fa35886effa 100644 --- a/src/Analyzers/CSharp/Tests/UseTupleSwap/UseTupleSwapTests.cs +++ b/src/Analyzers/CSharp/Tests/UseTupleSwap/UseTupleSwapTests.cs @@ -19,7 +19,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseTupleSwap; CSharpUseTupleSwapCodeFixProvider>; [Trait(Traits.Feature, Traits.Features.CodeActionsUseTupleSwap)] -public partial class UseTupleSwapTests +public sealed class UseTupleSwapTests { [Fact] public async Task TestMissingBeforeCSharp7() diff --git a/src/Analyzers/CSharp/Tests/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfTests.cs b/src/Analyzers/CSharp/Tests/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfTests.cs index ca14189f92e65..fdc1df5db9dee 100644 --- a/src/Analyzers/CSharp/Tests/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfTests.cs +++ b/src/Analyzers/CSharp/Tests/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfTests.cs @@ -21,7 +21,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseUnboundTypeInNameOf; CSharpUseUnboundTypeInNameOfCodeFixProvider>; [Trait(Traits.Feature, Traits.Features.CodeActionsUseUnboundTypeInNameOf)] -public partial class UseUnboundTypeInNameOfTests +public sealed class UseUnboundTypeInNameOfTests { [Fact] public async Task TestBaseCase() From 282b3d08e2ab787c0c11d9f36ba44c8d8d04de0f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 12:07:22 -0800 Subject: [PATCH 436/508] Simplify tests --- .../AddAccessibilityModifiersTests.cs | 3 - .../ConflictMarkerResolutionTests.cs | 4 -- ...vertToBlockScopedNamespaceAnalyzerTests.cs | 1 - ...nvertToFileScopedNamespaceAnalyzerTests.cs | 6 -- ...ConvertSwitchStatementToExpressionTests.cs | 1 - .../Tests/FileHeaders/FileHeaderTests.cs | 5 -- .../Tests/FixReturnType/FixReturnTypeTests.cs | 1 - .../GenerateDefaultConstructorsTests.cs | 2 - .../ImplementInterfaceTests.cs | 1 - .../MakeAnonymousFunctionStaticTests.cs | 2 - .../MakeMemberRequiredTests.cs | 5 -- .../MakeStructMemberReadOnlyTests.cs | 28 --------- .../MakeTypePartial/MakeTypePartialTests.cs | 1 - .../ArrowExpressionClausePlacementTests.cs | 11 ---- .../ConditionalExpressionPlacementTests.cs | 12 ---- .../ConsecutiveBracePlacementTests.cs | 19 ------- .../ConsecutiveStatementPlacementTests.cs | 15 ----- .../ConstructorInitializerPlacementTests.cs | 5 -- .../EmbeddedStatementPlacementTests.cs | 3 - .../MultipleBlankLinesTests.cs | 16 ------ .../RemoveUnnecessaryCastTests.cs | 57 ------------------- .../RemoveUnnecessaryLambdaExpressionTests.cs | 1 - .../RemoveUnusedMembersTests.cs | 1 - .../RemoveUnusedValueAssignmentTests.cs | 9 --- ...eExpressionForIfNullStatementCheckTests.cs | 11 ---- .../UseCollectionInitializerTests.cs | 1 - .../UseCompoundAssignmentTests.cs | 3 - .../UseCompoundCoalesceAssignmentTests.cs | 1 - ...ConditionalExpressionForAssignmentTests.cs | 1 - .../UseDefaultLiteralTests.cs | 14 ----- .../UseExplicitTupleNameTests.cs | 3 - ...ExpressionBodyForAccessorsAnalyzerTests.cs | 1 - .../UseIndexOperatorTests.cs | 11 ---- .../UseRangeOperatorTests.cs | 5 -- .../UseNullPropagationTests.cs | 1 - .../UseObjectInitializerTests.cs | 1 - .../CSharpAsAndMemberAccessTests.cs | 17 ------ .../Tests/UseTupleSwap/UseTupleSwapTests.cs | 13 ----- ...ateEqualsAndGetHashCodeFromMembersTests.cs | 1 - .../AddParameterCheckTests.cs | 22 ++----- .../CSharpMoveStaticMembersTests.cs | 9 --- .../ConvertDirectCastToTryCastTests.cs | 3 - .../ConvertTryCastToDirectCastTests.cs | 2 - .../ConvertIfToSwitchTests.cs | 7 --- .../ConvertNamespaceRefactoringTests.cs | 13 ----- .../ConvertToProgramMainAnalyzerTests.cs | 4 -- .../ConvertToProgramMainRefactoringTests.cs | 5 -- ...onvertToTopLevelStatementsAnalyzerTests.cs | 26 --------- ...ertToTopLevelStatementsRefactoringTests.cs | 7 --- ...tConcatenationToInterpolatedStringTests.cs | 2 - .../JsonStringDetectorTests.cs | 2 - .../EnableNullable/EnableNullableTests.cs | 1 - .../ExtractClass/ExtractClassTests.cs | 22 ++----- ...ddConstructorParametersFromMembersTests.cs | 1 - .../SimplifyPropertyPatternTests.cs | 9 --- .../UseNameofInAttributeTests.cs | 4 -- 56 files changed, 10 insertions(+), 422 deletions(-) diff --git a/src/Analyzers/CSharp/Tests/AddAccessibilityModifiers/AddAccessibilityModifiersTests.cs b/src/Analyzers/CSharp/Tests/AddAccessibilityModifiers/AddAccessibilityModifiersTests.cs index 28a7bdb9bd464..8f432ae3a5330 100644 --- a/src/Analyzers/CSharp/Tests/AddAccessibilityModifiers/AddAccessibilityModifiersTests.cs +++ b/src/Analyzers/CSharp/Tests/AddAccessibilityModifiers/AddAccessibilityModifiersTests.cs @@ -622,7 +622,6 @@ internal class C : I var test = new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp12, ReferenceAssemblies = Testing.ReferenceAssemblies.Net.Net60 }; @@ -645,7 +644,6 @@ public async Task TestFileDeclaration(string declarationKind) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp12, }.RunAsync(); } @@ -658,7 +656,6 @@ public async Task TestFileDelegate() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp12, }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/ConflictMarkerResolution/ConflictMarkerResolutionTests.cs b/src/Analyzers/CSharp/Tests/ConflictMarkerResolution/ConflictMarkerResolutionTests.cs index 59ff1fc342f65..c843828b45629 100644 --- a/src/Analyzers/CSharp/Tests/ConflictMarkerResolution/ConflictMarkerResolutionTests.cs +++ b/src/Analyzers/CSharp/Tests/ConflictMarkerResolution/ConflictMarkerResolutionTests.cs @@ -717,7 +717,6 @@ class X { await new VerifyCS.Test { TestCode = source, - FixedCode = source, }.RunAsync(); } @@ -734,7 +733,6 @@ class X { await new VerifyCS.Test { TestCode = source, - FixedCode = source, }.RunAsync(); } @@ -751,7 +749,6 @@ class X { await new VerifyCS.Test { TestCode = source, - FixedCode = source, }.RunAsync(); } @@ -1457,7 +1454,6 @@ class X { await new VerifyCS.Test { TestCode = source, - FixedCode = source, }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/ConvertNamespace/ConvertToBlockScopedNamespaceAnalyzerTests.cs b/src/Analyzers/CSharp/Tests/ConvertNamespace/ConvertToBlockScopedNamespaceAnalyzerTests.cs index aa486c15513a7..123ccc7a55a15 100644 --- a/src/Analyzers/CSharp/Tests/ConvertNamespace/ConvertToBlockScopedNamespaceAnalyzerTests.cs +++ b/src/Analyzers/CSharp/Tests/ConvertNamespace/ConvertToBlockScopedNamespaceAnalyzerTests.cs @@ -81,7 +81,6 @@ namespace N {} await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { diff --git a/src/Analyzers/CSharp/Tests/ConvertNamespace/ConvertToFileScopedNamespaceAnalyzerTests.cs b/src/Analyzers/CSharp/Tests/ConvertNamespace/ConvertToFileScopedNamespaceAnalyzerTests.cs index dcb3bff374a04..78e14b88c340c 100644 --- a/src/Analyzers/CSharp/Tests/ConvertNamespace/ConvertToFileScopedNamespaceAnalyzerTests.cs +++ b/src/Analyzers/CSharp/Tests/ConvertNamespace/ConvertToFileScopedNamespaceAnalyzerTests.cs @@ -29,7 +29,6 @@ namespace N await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, Options = { @@ -49,7 +48,6 @@ namespace N await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -115,7 +113,6 @@ namespace N2 await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -138,7 +135,6 @@ namespace N2 await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -160,7 +156,6 @@ namespace N await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -182,7 +177,6 @@ namespace N await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, ExpectedDiagnostics = { diff --git a/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs b/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs index 298041054b5aa..753c56b4a8cc8 100644 --- a/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs +++ b/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs @@ -630,7 +630,6 @@ void M(int i) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = CSharp9, }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/FileHeaders/FileHeaderTests.cs b/src/Analyzers/CSharp/Tests/FileHeaders/FileHeaderTests.cs index 70a4e835c508c..fc47c9a461a8a 100644 --- a/src/Analyzers/CSharp/Tests/FileHeaders/FileHeaderTests.cs +++ b/src/Analyzers/CSharp/Tests/FileHeaders/FileHeaderTests.cs @@ -44,7 +44,6 @@ namespace N await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, EditorConfig = $@" [*] {fileHeaderTemplate} @@ -242,7 +241,6 @@ namespace Bar await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, EditorConfig = TestSettings, }.RunAsync(); } @@ -267,7 +265,6 @@ namespace Bar await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, EditorConfig = TestSettings, }.RunAsync(); } @@ -291,7 +288,6 @@ namespace Bar await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, EditorConfig = TestSettings, }.RunAsync(); } @@ -442,7 +438,6 @@ namespace Bar await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, EditorConfig = TestSettings, }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/FixReturnType/FixReturnTypeTests.cs b/src/Analyzers/CSharp/Tests/FixReturnType/FixReturnTypeTests.cs index cddfb81f7d86c..dd6daad80c85e 100644 --- a/src/Analyzers/CSharp/Tests/FixReturnType/FixReturnTypeTests.cs +++ b/src/Analyzers/CSharp/Tests/FixReturnType/FixReturnTypeTests.cs @@ -500,7 +500,6 @@ async C M() await new VerifyCS.Test { TestCode = markup, - FixedCode = markup, ReferenceAssemblies = ReferenceAssemblies.Net.Net60 }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/GenerateDefaultConstructors/GenerateDefaultConstructorsTests.cs b/src/Analyzers/CSharp/Tests/GenerateDefaultConstructors/GenerateDefaultConstructorsTests.cs index 8eec2a263228e..a1da44a8f2dfd 100644 --- a/src/Analyzers/CSharp/Tests/GenerateDefaultConstructors/GenerateDefaultConstructorsTests.cs +++ b/src/Analyzers/CSharp/Tests/GenerateDefaultConstructors/GenerateDefaultConstructorsTests.cs @@ -66,7 +66,6 @@ private static async Task TestRefactoringMissingAsync(string source) await new VerifyRefactoring.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -78,7 +77,6 @@ private static async Task TestCodeFixMissingAsync(string source) await new VerifyCodeFix.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/ImplementInterface/ImplementInterfaceTests.cs b/src/Analyzers/CSharp/Tests/ImplementInterface/ImplementInterfaceTests.cs index 1fcfb16183639..ef616c40e1044 100644 --- a/src/Analyzers/CSharp/Tests/ImplementInterface/ImplementInterfaceTests.cs +++ b/src/Analyzers/CSharp/Tests/ImplementInterface/ImplementInterfaceTests.cs @@ -11924,7 +11924,6 @@ event System.EventHandler I.Click await new VerifyCS.Test { TestCode = code, - FixedCode = code, //LanguageVersion = LanguageVersion.CSharp12, ExpectedDiagnostics = { diff --git a/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs b/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs index d3e2d6a664fa5..8809c0bf1db00 100644 --- a/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs +++ b/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs @@ -52,7 +52,6 @@ void N(Action a) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp8 }.RunAsync(); } @@ -82,7 +81,6 @@ void N(Action a) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, Options = { diff --git a/src/Analyzers/CSharp/Tests/MakeMemberRequired/MakeMemberRequiredTests.cs b/src/Analyzers/CSharp/Tests/MakeMemberRequired/MakeMemberRequiredTests.cs index 877aaf5f02adb..3af2b2741c6af 100644 --- a/src/Analyzers/CSharp/Tests/MakeMemberRequired/MakeMemberRequiredTests.cs +++ b/src/Analyzers/CSharp/Tests/MakeMemberRequired/MakeMemberRequiredTests.cs @@ -135,7 +135,6 @@ class MyClass await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp11, ReferenceAssemblies = ReferenceAssemblies.Net.Net60, }.RunAsync(); @@ -204,7 +203,6 @@ class MyClass await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp11, ReferenceAssemblies = ReferenceAssemblies.Net.Net70 }.RunAsync(); @@ -453,7 +451,6 @@ class MyClass await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, ReferenceAssemblies = ReferenceAssemblies.Net.Net70 }.RunAsync(); @@ -475,7 +472,6 @@ class MyClass await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp11, ReferenceAssemblies = ReferenceAssemblies.Net.Net70 }.RunAsync(); @@ -496,7 +492,6 @@ class MyClass await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp11, ReferenceAssemblies = ReferenceAssemblies.Net.Net70 }.RunAsync(); diff --git a/src/Analyzers/CSharp/Tests/MakeStructMemberReadOnly/MakeStructMemberReadOnlyTests.cs b/src/Analyzers/CSharp/Tests/MakeStructMemberReadOnly/MakeStructMemberReadOnlyTests.cs index d607fc390888c..9b914d0ea1da3 100644 --- a/src/Analyzers/CSharp/Tests/MakeStructMemberReadOnly/MakeStructMemberReadOnlyTests.cs +++ b/src/Analyzers/CSharp/Tests/MakeStructMemberReadOnly/MakeStructMemberReadOnlyTests.cs @@ -53,7 +53,6 @@ void M() { } await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -69,7 +68,6 @@ void M() { } await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -85,7 +83,6 @@ readonly void M() { } await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -104,7 +101,6 @@ void M() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -125,7 +121,6 @@ static void G(ref S s) { } await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -149,7 +144,6 @@ public static void G(ref this S s) { } await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -295,7 +289,6 @@ void M() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -315,7 +308,6 @@ void M() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -337,7 +329,6 @@ static void G(ref int x) { } await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -359,7 +350,6 @@ void M() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -379,7 +369,6 @@ void M() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -403,7 +392,6 @@ void M() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -428,7 +416,6 @@ void M() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -453,7 +440,6 @@ void M() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -469,7 +455,6 @@ void M() { } await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp7, }.RunAsync(); } @@ -507,7 +492,6 @@ struct S await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -919,7 +903,6 @@ unsafe void M() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -944,7 +927,6 @@ void X() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -966,7 +948,6 @@ void M() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -991,7 +972,6 @@ void X() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -1016,7 +996,6 @@ void X() await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -1167,7 +1146,6 @@ void M() await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, }.RunAsync(); } @@ -1194,7 +1172,6 @@ void Dispose() await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, }.RunAsync(); } @@ -1252,7 +1229,6 @@ struct T where X : IComparable await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, }.RunAsync(); } @@ -1270,7 +1246,6 @@ struct T where X : struct, IComparable await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, }.RunAsync(); } @@ -1310,7 +1285,6 @@ struct S await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -1329,7 +1303,6 @@ void M() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -1348,7 +1321,6 @@ void M() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/MakeTypePartial/MakeTypePartialTests.cs b/src/Analyzers/CSharp/Tests/MakeTypePartial/MakeTypePartialTests.cs index 377704d006a21..f58d5f83e2b14 100644 --- a/src/Analyzers/CSharp/Tests/MakeTypePartial/MakeTypePartialTests.cs +++ b/src/Analyzers/CSharp/Tests/MakeTypePartial/MakeTypePartialTests.cs @@ -376,7 +376,6 @@ namespace TestNamespace2 await new VerifyCS.Test { TestCode = markup, - FixedCode = markup, LanguageVersion = LanguageVersion.CSharp10 }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/NewLines/ArrowExpressionClausePlacement/ArrowExpressionClausePlacementTests.cs b/src/Analyzers/CSharp/Tests/NewLines/ArrowExpressionClausePlacement/ArrowExpressionClausePlacementTests.cs index 46710ac1f8dbe..f1a7c333867f5 100644 --- a/src/Analyzers/CSharp/Tests/NewLines/ArrowExpressionClausePlacement/ArrowExpressionClausePlacementTests.cs +++ b/src/Analyzers/CSharp/Tests/NewLines/ArrowExpressionClausePlacement/ArrowExpressionClausePlacementTests.cs @@ -34,7 +34,6 @@ public int Add() => await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInArrowExpressionClause, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -53,7 +52,6 @@ class C await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInArrowExpressionClause, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -72,7 +70,6 @@ class C await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInArrowExpressionClause, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -94,7 +91,6 @@ public void Main() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInArrowExpressionClause, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -119,7 +115,6 @@ public void Goo(System.Func action) { } await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInArrowExpressionClause, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -171,7 +166,6 @@ public int Add await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInArrowExpressionClause, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -255,7 +249,6 @@ public int Add(int{|CS1001:)|} => await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInArrowExpressionClause, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -275,7 +268,6 @@ public int Add() => await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInArrowExpressionClause, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -295,7 +287,6 @@ public int Add() => await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInArrowExpressionClause, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -317,7 +308,6 @@ public int Add() => await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInArrowExpressionClause, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -339,7 +329,6 @@ public int Add() => await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInArrowExpressionClause, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/NewLines/ConditionalExpressionPlacement/ConditionalExpressionPlacementTests.cs b/src/Analyzers/CSharp/Tests/NewLines/ConditionalExpressionPlacement/ConditionalExpressionPlacementTests.cs index b4e535b6ca768..de48866e2e01c 100644 --- a/src/Analyzers/CSharp/Tests/NewLines/ConditionalExpressionPlacement/ConditionalExpressionPlacementTests.cs +++ b/src/Analyzers/CSharp/Tests/NewLines/ConditionalExpressionPlacement/ConditionalExpressionPlacementTests.cs @@ -37,7 +37,6 @@ public C() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -98,7 +97,6 @@ public C() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -122,7 +120,6 @@ public C() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -146,7 +143,6 @@ public C() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -169,7 +165,6 @@ public C() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -192,7 +187,6 @@ public C() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -215,7 +209,6 @@ public C() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -241,7 +234,6 @@ public C() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -267,7 +259,6 @@ public C() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -293,7 +284,6 @@ public C() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -319,7 +309,6 @@ public C() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -345,7 +334,6 @@ public C() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/NewLines/ConsecutiveBracePlacement/ConsecutiveBracePlacementTests.cs b/src/Analyzers/CSharp/Tests/NewLines/ConsecutiveBracePlacement/ConsecutiveBracePlacementTests.cs index 0fffe3768cf00..47f074cc96a13 100644 --- a/src/Analyzers/CSharp/Tests/NewLines/ConsecutiveBracePlacement/ConsecutiveBracePlacementTests.cs +++ b/src/Analyzers/CSharp/Tests/NewLines/ConsecutiveBracePlacement/ConsecutiveBracePlacementTests.cs @@ -26,7 +26,6 @@ public async Task NotForBracesOnSameLineDirectlyTouching() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -40,7 +39,6 @@ public async Task NotForBracesOnSameLineWithSpace() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -54,7 +52,6 @@ public async Task NotForBracesOnSameLineWithComment() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -68,7 +65,6 @@ public async Task NotForBracesOnSameLineWithCommentAndSpaces() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -89,7 +85,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -110,7 +105,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -131,7 +125,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -155,7 +148,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -179,7 +171,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -203,7 +194,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -227,7 +217,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -251,7 +240,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -275,7 +263,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); @@ -303,7 +290,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -330,7 +316,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -357,7 +342,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); @@ -411,7 +395,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.TrueWithSuggestionEnforcement } } }.RunAsync(); } @@ -817,7 +800,6 @@ internal interface IOption2 await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); @@ -864,7 +846,6 @@ internal interface IOption2 await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); diff --git a/src/Analyzers/CSharp/Tests/NewLines/ConsecutiveStatementPlacement/ConsecutiveStatementPlacementTests.cs b/src/Analyzers/CSharp/Tests/NewLines/ConsecutiveStatementPlacement/ConsecutiveStatementPlacementTests.cs index 8b7a4122d28b4..744d87aad7170 100644 --- a/src/Analyzers/CSharp/Tests/NewLines/ConsecutiveStatementPlacement/ConsecutiveStatementPlacementTests.cs +++ b/src/Analyzers/CSharp/Tests/NewLines/ConsecutiveStatementPlacement/ConsecutiveStatementPlacementTests.cs @@ -32,7 +32,6 @@ class C await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -52,7 +51,6 @@ void Y() { } await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -74,7 +72,6 @@ void M() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -96,7 +93,6 @@ void M() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -121,7 +117,6 @@ void M() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -146,7 +141,6 @@ void M() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -172,7 +166,6 @@ void M() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -198,7 +191,6 @@ void M() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -224,7 +216,6 @@ void M() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -250,7 +241,6 @@ void M() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -277,7 +267,6 @@ void M() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -303,7 +292,6 @@ void M() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -333,7 +321,6 @@ void M() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -391,7 +378,6 @@ void M() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -941,7 +927,6 @@ public void TestTernaryConstruction() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/NewLines/ConstructorInitializerPlacement/ConstructorInitializerPlacementTests.cs b/src/Analyzers/CSharp/Tests/NewLines/ConstructorInitializerPlacement/ConstructorInitializerPlacementTests.cs index da77078eaf284..a65f1879335f7 100644 --- a/src/Analyzers/CSharp/Tests/NewLines/ConstructorInitializerPlacement/ConstructorInitializerPlacementTests.cs +++ b/src/Analyzers/CSharp/Tests/NewLines/ConstructorInitializerPlacement/ConstructorInitializerPlacementTests.cs @@ -34,7 +34,6 @@ public C() : await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterColonInConstructorInitializer, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -88,7 +87,6 @@ public C() : base() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterColonInConstructorInitializer, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -110,7 +108,6 @@ public C() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterColonInConstructorInitializer, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -132,7 +129,6 @@ public C() : //comment await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterColonInConstructorInitializer, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -258,7 +254,6 @@ public C() : await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterColonInConstructorInitializer, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/NewLines/EmbeddedStatementPlacement/EmbeddedStatementPlacementTests.cs b/src/Analyzers/CSharp/Tests/NewLines/EmbeddedStatementPlacement/EmbeddedStatementPlacementTests.cs index 8388ab43f13c9..cda350f8e7e79 100644 --- a/src/Analyzers/CSharp/Tests/NewLines/EmbeddedStatementPlacement/EmbeddedStatementPlacementTests.cs +++ b/src/Analyzers/CSharp/Tests/NewLines/EmbeddedStatementPlacement/EmbeddedStatementPlacementTests.cs @@ -33,7 +33,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, Options = { { CSharpCodeStyleOptions.AllowEmbeddedStatementsOnSameLine, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -106,7 +105,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, Options = { { CSharpCodeStyleOptions.AllowEmbeddedStatementsOnSameLine, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -202,7 +200,6 @@ int Prop2 await new VerifyCS.Test { TestCode = source, - FixedCode = source, Options = { { CSharpCodeStyleOptions.AllowEmbeddedStatementsOnSameLine, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/NewLines/MultipleBlankLines/MultipleBlankLinesTests.cs b/src/Analyzers/CSharp/Tests/NewLines/MultipleBlankLines/MultipleBlankLinesTests.cs index b1dcc6edc3e62..e997fbbbeef2c 100644 --- a/src/Analyzers/CSharp/Tests/NewLines/MultipleBlankLines/MultipleBlankLinesTests.cs +++ b/src/Analyzers/CSharp/Tests/NewLines/MultipleBlankLines/MultipleBlankLinesTests.cs @@ -27,7 +27,6 @@ public async Task TestOneBlankLineAtTopOfFile() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -62,7 +61,6 @@ public async Task TestTwoBlankLineAtTopOfFile_NotWithOptionOff() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.TrueWithSuggestionEnforcement } } }.RunAsync(); } @@ -118,7 +116,6 @@ public async Task TestOneBlankLineAtTopOfEmptyFile() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -194,7 +191,6 @@ public async Task TestNoBlankLineAtEndOfFile_1() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -211,7 +207,6 @@ public async Task TestNoBlankLineAtEndOfFile_2() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -229,7 +224,6 @@ public async Task TestOneBlankLineAtEndOfFile() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -323,7 +317,6 @@ public async Task TestNoBlankLineBetweenTokens() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -340,7 +333,6 @@ public async Task TestOneBlankLineBetweenTokens() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -429,7 +421,6 @@ public async Task TestNoBlankLineAfterComment() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -447,7 +438,6 @@ public async Task TestOneBlankLineAfterComment() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -541,7 +531,6 @@ public async Task TestNoBlankLineAfterDirective() await new Verify.Test { TestCode = code, - FixedCode = code, LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); @@ -560,7 +549,6 @@ public async Task TestOneBlankLineAfterDirective() await new Verify.Test { TestCode = code, - FixedCode = code, LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); @@ -659,7 +647,6 @@ class C await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -678,7 +665,6 @@ class C await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -781,7 +767,6 @@ class C await new Verify.Test { TestCode = code, - FixedCode = code, LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); @@ -805,7 +790,6 @@ class C await new Verify.Test { TestCode = code, - FixedCode = code, LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); diff --git a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryCast/RemoveUnnecessaryCastTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryCast/RemoveUnnecessaryCastTests.cs index ca6f3fe43ab08..0c7ae7293a84d 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryCast/RemoveUnnecessaryCastTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryCast/RemoveUnnecessaryCastTests.cs @@ -5741,7 +5741,6 @@ void Goo() await new VerifyCS.Test { TestCode = source, - FixedCode = source, }.RunAsync(); } @@ -5766,7 +5765,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp7_1, }.RunAsync(); } @@ -5792,7 +5790,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp7_1, }.RunAsync(); } @@ -5818,7 +5815,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp7_1, }.RunAsync(); } @@ -5910,7 +5906,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp7_1, }.RunAsync(); } @@ -5936,7 +5931,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp7_1, }.RunAsync(); } @@ -5962,7 +5956,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp7_1, }.RunAsync(); } @@ -6064,7 +6057,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp7_1, }.RunAsync(); } @@ -6086,7 +6078,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp7_1, }.RunAsync(); } @@ -6108,7 +6099,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp7_1, }.RunAsync(); } @@ -6164,7 +6154,6 @@ void M(string s) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -6188,7 +6177,6 @@ void M(string s) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -8238,7 +8226,6 @@ static int Main() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -8311,7 +8298,6 @@ void M(bool x) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp8 }.RunAsync(); } @@ -8360,7 +8346,6 @@ void M(bool x) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp8 }.RunAsync(); } @@ -8409,7 +8394,6 @@ void M(bool x) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp8, }.RunAsync(); } @@ -8509,7 +8493,6 @@ void M(bool x) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp8 }.RunAsync(); } @@ -8558,7 +8541,6 @@ void M(bool x) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp8 }.RunAsync(); } @@ -8607,7 +8589,6 @@ void M(bool x) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp8 }.RunAsync(); } @@ -9659,7 +9640,6 @@ public class C { var test = new VerifyCS.Test() { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9 }; @@ -9711,7 +9691,6 @@ public class C { var test = new VerifyCS.Test() { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9 }; @@ -9733,7 +9712,6 @@ public class C { var test = new VerifyCS.Test() { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9 }; @@ -9819,7 +9797,6 @@ public static ulong P(ulong a, uint b) var test = new VerifyCS.Test() { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9 }; @@ -9843,7 +9820,6 @@ public static nuint N(nuint a, uint b) var test = new VerifyCS.Test() { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9 }; @@ -9867,7 +9843,6 @@ public static ulong N() var test = new VerifyCS.Test() { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9 }; @@ -10004,7 +9979,6 @@ static void Main() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -10033,7 +10007,6 @@ public E First await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -10057,7 +10030,6 @@ public TestClass(Func value) { } await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -10080,7 +10052,6 @@ unsafe void M(nint** ptr) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -10916,7 +10887,6 @@ void M(object o) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -12120,7 +12090,6 @@ void AddHandler(Delegate handler) { } await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = version, }.RunAsync(); } @@ -12151,7 +12120,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12171,7 +12139,6 @@ class C await new VerifyCS.Test { TestCode = source, - FixedCode = source, }.RunAsync(); } @@ -12189,7 +12156,6 @@ class C await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12214,7 +12180,6 @@ static IEnumerable DoThis(IEnumerable notreallynull) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12237,7 +12202,6 @@ bool M(object obj) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12259,7 +12223,6 @@ public IEnumerable Bar() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12281,7 +12244,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12305,7 +12267,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12328,7 +12289,6 @@ public static void MethodName() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12345,7 +12305,6 @@ static class Program await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12368,7 +12327,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12393,7 +12351,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12413,7 +12370,6 @@ public uint fn1(sbyte a, sbyte b) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12433,7 +12389,6 @@ public void fn1(int start, int end) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12458,7 +12413,6 @@ public Code(DoSomething f) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12482,7 +12436,6 @@ static void CheckRedundantCast() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12514,7 +12467,6 @@ enum Numbers await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12565,7 +12517,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12607,7 +12558,6 @@ void Run() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12635,7 +12585,6 @@ public X(double d) { } await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12683,7 +12632,6 @@ private C(ReadOnlyMemory? buffer = null) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12729,7 +12677,6 @@ public unsafe static implicit operator PointerDelegate(delegate*(object o) where T : Delegate await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -13128,7 +13072,6 @@ public enum Test await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryLambdaExpression/RemoveUnnecessaryLambdaExpressionTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryLambdaExpression/RemoveUnnecessaryLambdaExpressionTests.cs index 7bdc80f506142..6bc07cf4af140 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryLambdaExpression/RemoveUnnecessaryLambdaExpressionTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryLambdaExpression/RemoveUnnecessaryLambdaExpressionTests.cs @@ -119,7 +119,6 @@ void Bar(Func f) { } await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp12, Options = { { CSharpCodeStyleOptions.PreferMethodGroupConversion, new CodeStyleOption2(false, NotificationOption2.None) } } }.RunAsync(); diff --git a/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs index d7ab7dbffa2a7..fbc00d0c64c7e 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs @@ -438,7 +438,6 @@ public async Task EntryPointMethodNotFlagged_06() await new VerifyCS.Test { TestCode = code, - FixedCode = code, ExpectedDiagnostics = { // /0/Test0.cs(2,1): error CS8805: Program using top-level statements must be an executable. diff --git a/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedValueAssignmentTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedValueAssignmentTests.cs index 7000b894c5edb..48b3eab5b066c 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedValueAssignmentTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedValueAssignmentTests.cs @@ -65,7 +65,6 @@ int M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, Options = { { CSharpCodeStyleOptions.UnusedValueAssignment, UnusedValuePreference.DiscardVariable, NotificationOption2.None }, @@ -93,7 +92,6 @@ int M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, Options = { { CSharpCodeStyleOptions.UnusedValueAssignment, UnusedValuePreference.DiscardVariable, NotificationOption2.None }, @@ -406,7 +404,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, Options = { { CSharpCodeStyleOptions.UnusedValueAssignment, UnusedValuePreference.UnusedLocalVariable }, @@ -1317,7 +1314,6 @@ int M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, Options = { { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option }, @@ -1401,7 +1397,6 @@ int M(int x) await new VerifyCS.Test { TestCode = source, - FixedCode = source, Options = { { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option }, @@ -1525,7 +1520,6 @@ int M(int x) await new VerifyCS.Test { TestCode = source, - FixedCode = source, Options = { { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option }, @@ -1555,7 +1549,6 @@ bool M(bool x) await new VerifyCS.Test { TestCode = source, - FixedCode = source, Options = { { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option }, @@ -1586,7 +1579,6 @@ bool M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, Options = { { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option }, @@ -10063,7 +10055,6 @@ public void Reset() { await new VerifyCS.Test { TestCode = source, - FixedCode = source, Options = { { CSharpCodeStyleOptions.UnusedValueAssignment, UnusedValuePreference.DiscardVariable }, diff --git a/src/Analyzers/CSharp/Tests/UseCoalesceExpression/UseCoalesceExpressionForIfNullStatementCheckTests.cs b/src/Analyzers/CSharp/Tests/UseCoalesceExpression/UseCoalesceExpressionForIfNullStatementCheckTests.cs index b90c9f295ab96..065c91b83751d 100644 --- a/src/Analyzers/CSharp/Tests/UseCoalesceExpression/UseCoalesceExpressionForIfNullStatementCheckTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCoalesceExpression/UseCoalesceExpressionForIfNullStatementCheckTests.cs @@ -202,7 +202,6 @@ void M(C item1) await new VerifyCS.Test { TestCode = text, - FixedCode = text, }.RunAsync(); } @@ -226,7 +225,6 @@ void M() await new VerifyCS.Test { TestCode = text, - FixedCode = text, }.RunAsync(); } @@ -250,7 +248,6 @@ void M() await new VerifyCS.Test { TestCode = text, - FixedCode = text, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -275,7 +272,6 @@ void M(C item1) await new VerifyCS.Test { TestCode = text, - FixedCode = text, }.RunAsync(); } @@ -301,7 +297,6 @@ void M(C item1) await new VerifyCS.Test { TestCode = text, - FixedCode = text, }.RunAsync(); } @@ -328,7 +323,6 @@ void M(C item1) await new VerifyCS.Test { TestCode = text, - FixedCode = text, }.RunAsync(); } @@ -353,7 +347,6 @@ void M(C item1) await new VerifyCS.Test { TestCode = text, - FixedCode = text, }.RunAsync(); } @@ -383,7 +376,6 @@ void M() await new VerifyCS.Test { TestCode = text, - FixedCode = text, }.RunAsync(); } @@ -407,7 +399,6 @@ void M() await new VerifyCS.Test { TestCode = text, - FixedCode = text, }.RunAsync(); } @@ -431,7 +422,6 @@ void M() await new VerifyCS.Test { TestCode = text, - FixedCode = text, }.RunAsync(); } @@ -455,7 +445,6 @@ void M() await new VerifyCS.Test { TestCode = text, - FixedCode = text, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests.cs b/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests.cs index 5d9f3f4dcd37c..d70e33ad6cbad 100644 --- a/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests.cs @@ -42,7 +42,6 @@ private static async Task TestMissingInRegularAndScriptAsync(string testCode, La var test = new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, }; if (languageVersion != null) diff --git a/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundAssignmentTests.cs b/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundAssignmentTests.cs index fb61260019e04..ea80f822af76d 100644 --- a/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundAssignmentTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundAssignmentTests.cs @@ -283,7 +283,6 @@ void M(int? a) await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_3 }.RunAsync(); } @@ -305,7 +304,6 @@ void M(int? a) await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp8 }.RunAsync(); } @@ -958,7 +956,6 @@ InsertionPoint Up() await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, ReferenceAssemblies = ReferenceAssemblies.Net.Net60 }.RunAsync(); diff --git a/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundCoalesceAssignmentTests.cs b/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundCoalesceAssignmentTests.cs index eb005bef46ee7..ab6bafab2e87b 100644 --- a/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundCoalesceAssignmentTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundCoalesceAssignmentTests.cs @@ -33,7 +33,6 @@ private static async Task TestMissingAsync(string testCode, LanguageVersion lang await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, LanguageVersion = languageVersion, ReferenceAssemblies = ReferenceAssemblies.Net.Net60, }.RunAsync(); diff --git a/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForAssignmentTests.cs b/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForAssignmentTests.cs index 548e764d0a344..03048bdecccd8 100644 --- a/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForAssignmentTests.cs +++ b/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForAssignmentTests.cs @@ -29,7 +29,6 @@ private static async Task TestMissingAsync( var test = new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, LanguageVersion = languageVersion, Options = { options }, }; diff --git a/src/Analyzers/CSharp/Tests/UseDefaultLiteral/UseDefaultLiteralTests.cs b/src/Analyzers/CSharp/Tests/UseDefaultLiteral/UseDefaultLiteralTests.cs index e7651ba47f906..b5e69a7715a2a 100644 --- a/src/Analyzers/CSharp/Tests/UseDefaultLiteral/UseDefaultLiteralTests.cs +++ b/src/Analyzers/CSharp/Tests/UseDefaultLiteral/UseDefaultLiteralTests.cs @@ -34,7 +34,6 @@ class C await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7 }.RunAsync(); } @@ -134,7 +133,6 @@ string Goo() await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_1 }.RunAsync(); } @@ -188,7 +186,6 @@ void Goo() await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_1 }.RunAsync(); } @@ -236,7 +233,6 @@ void Goo() await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_1 }.RunAsync(); } @@ -257,7 +253,6 @@ void Goo() await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_1 }.RunAsync(); } @@ -312,7 +307,6 @@ void Bar(int i) { } await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_1 }.RunAsync(); } @@ -482,7 +476,6 @@ public override bool Equals(object obj) await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_1 }.RunAsync(); } @@ -509,7 +502,6 @@ public override bool Equals(object obj) await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_1 }.RunAsync(); } @@ -566,7 +558,6 @@ void M() await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_1 }.RunAsync(); } @@ -590,7 +581,6 @@ void M() await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_1 }.RunAsync(); } @@ -647,7 +637,6 @@ void M() await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_1 }.RunAsync(); } @@ -671,7 +660,6 @@ void M() await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_1 }.RunAsync(); } @@ -758,7 +746,6 @@ void M() await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_1 }.RunAsync(); } @@ -779,7 +766,6 @@ void M() await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_1 }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/UseExplicitTupleName/UseExplicitTupleNameTests.cs b/src/Analyzers/CSharp/Tests/UseExplicitTupleName/UseExplicitTupleNameTests.cs index 7e24bf2ee9a9a..f173c92ce2cd2 100644 --- a/src/Analyzers/CSharp/Tests/UseExplicitTupleName/UseExplicitTupleNameTests.cs +++ b/src/Analyzers/CSharp/Tests/UseExplicitTupleName/UseExplicitTupleNameTests.cs @@ -220,7 +220,6 @@ void M() await new VerifyCS.Test() { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.PreferExplicitTupleNames, false, NotificationOption2.Warning } @@ -245,7 +244,6 @@ void M() await new VerifyCS.Test() { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.PreferExplicitTupleNames, false, NotificationOption2.Warning } @@ -298,7 +296,6 @@ void M() await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp11 }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/UseExpressionBody/UseExpressionBodyForAccessorsAnalyzerTests.cs b/src/Analyzers/CSharp/Tests/UseExpressionBody/UseExpressionBodyForAccessorsAnalyzerTests.cs index c496185cb85d1..de8c9a8781e08 100644 --- a/src/Analyzers/CSharp/Tests/UseExpressionBody/UseExpressionBodyForAccessorsAnalyzerTests.cs +++ b/src/Analyzers/CSharp/Tests/UseExpressionBody/UseExpressionBodyForAccessorsAnalyzerTests.cs @@ -575,7 +575,6 @@ C this[int index] await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, ExpressionBodyPreference.WhenOnSingleLine, NotificationOption2.None }, diff --git a/src/Analyzers/CSharp/Tests/UseIndexOrRangeOperator/UseIndexOperatorTests.cs b/src/Analyzers/CSharp/Tests/UseIndexOrRangeOperator/UseIndexOperatorTests.cs index aaa1b12f1e9a8..22027513b98fb 100644 --- a/src/Analyzers/CSharp/Tests/UseIndexOrRangeOperator/UseIndexOperatorTests.cs +++ b/src/Analyzers/CSharp/Tests/UseIndexOrRangeOperator/UseIndexOperatorTests.cs @@ -38,7 +38,6 @@ void Goo(string s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp7, }.RunAsync(); } @@ -243,7 +242,6 @@ void Goo(string s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, }.RunAsync(); } @@ -265,7 +263,6 @@ void Goo(string s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, }.RunAsync(); } @@ -288,7 +285,6 @@ void Goo(S s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, }.RunAsync(); } @@ -379,7 +375,6 @@ void Goo(S s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, }.RunAsync(); } @@ -402,7 +397,6 @@ void Goo(S s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, }.RunAsync(); } @@ -493,7 +487,6 @@ void Goo(S s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, }.RunAsync(); } @@ -516,7 +509,6 @@ void Goo(S s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, }.RunAsync(); } @@ -538,7 +530,6 @@ void Goo(string[] s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp20, TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp8, }.RunAsync(); } @@ -797,7 +788,6 @@ void Goo(Dictionary s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, }.RunAsync(); } @@ -822,7 +812,6 @@ void Goo(List s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, }.RunAsync(); } } diff --git a/src/Analyzers/CSharp/Tests/UseIndexOrRangeOperator/UseRangeOperatorTests.cs b/src/Analyzers/CSharp/Tests/UseIndexOrRangeOperator/UseRangeOperatorTests.cs index 1465302c90134..14447543578e5 100644 --- a/src/Analyzers/CSharp/Tests/UseIndexOrRangeOperator/UseRangeOperatorTests.cs +++ b/src/Analyzers/CSharp/Tests/UseIndexOrRangeOperator/UseRangeOperatorTests.cs @@ -38,7 +38,6 @@ void Goo(string s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp7, }.RunAsync(); } @@ -98,7 +97,6 @@ void Goo(string s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp20, TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp8, }.RunAsync(); } @@ -399,7 +397,6 @@ void Goo(string s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp7, }.RunAsync(); } @@ -700,7 +697,6 @@ void Goo(S s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, }.RunAsync(); } [Fact] @@ -1311,7 +1307,6 @@ void M() { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/UseNullPropagation/UseNullPropagationTests.cs b/src/Analyzers/CSharp/Tests/UseNullPropagation/UseNullPropagationTests.cs index fd23f28af5bf6..7b76c674dd345 100644 --- a/src/Analyzers/CSharp/Tests/UseNullPropagation/UseNullPropagationTests.cs +++ b/src/Analyzers/CSharp/Tests/UseNullPropagation/UseNullPropagationTests.cs @@ -43,7 +43,6 @@ private static async Task TestMissingInRegularAndScriptAsync(string testCode, La await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, LanguageVersion = languageVersion, }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/UseObjectInitializer/UseObjectInitializerTests.cs b/src/Analyzers/CSharp/Tests/UseObjectInitializer/UseObjectInitializerTests.cs index 252322bd3d60e..9dbe9d05342a3 100644 --- a/src/Analyzers/CSharp/Tests/UseObjectInitializer/UseObjectInitializerTests.cs +++ b/src/Analyzers/CSharp/Tests/UseObjectInitializer/UseObjectInitializerTests.cs @@ -25,7 +25,6 @@ private static async Task TestMissingInRegularAndScriptAsync(string testCode, La var test = new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, }; if (languageVersion != null) diff --git a/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpAsAndMemberAccessTests.cs b/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpAsAndMemberAccessTests.cs index a8abf3c3d92cc..07fd91862e4ce 100644 --- a/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpAsAndMemberAccessTests.cs +++ b/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpAsAndMemberAccessTests.cs @@ -67,7 +67,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp7, }.RunAsync(); } @@ -89,7 +88,6 @@ void M(object o, int length) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp7, }.RunAsync(); } @@ -109,7 +107,6 @@ void M(object o, int length) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp7, }.RunAsync(); } @@ -134,7 +131,6 @@ void M(object o, int length) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp7, }.RunAsync(); } @@ -156,7 +152,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp8, }.RunAsync(); } @@ -178,7 +173,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -200,7 +194,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp8, }.RunAsync(); } @@ -222,7 +215,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -247,7 +239,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -271,7 +262,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp8, }.RunAsync(); } @@ -367,7 +357,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp8, }.RunAsync(); } @@ -391,7 +380,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp8, }.RunAsync(); } @@ -608,7 +596,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -630,7 +617,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -652,7 +638,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -677,7 +662,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -847,7 +831,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/UseTupleSwap/UseTupleSwapTests.cs b/src/Analyzers/CSharp/Tests/UseTupleSwap/UseTupleSwapTests.cs index dd1ac5b1f3dc4..6f7d0c2b76112 100644 --- a/src/Analyzers/CSharp/Tests/UseTupleSwap/UseTupleSwapTests.cs +++ b/src/Analyzers/CSharp/Tests/UseTupleSwap/UseTupleSwapTests.cs @@ -39,7 +39,6 @@ void M(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp6, }.RunAsync(); } @@ -62,7 +61,6 @@ void M(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.PreferTupleSwap, false, CodeStyle.NotificationOption2.Silent } @@ -114,7 +112,6 @@ void M(ref int a, ref int b) await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -216,7 +213,6 @@ void M(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -238,7 +234,6 @@ void M(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -260,7 +255,6 @@ void M(string[] args, string temp1) await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -282,7 +276,6 @@ void M(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -304,7 +297,6 @@ void M(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -326,7 +318,6 @@ void M(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -348,7 +339,6 @@ void M(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -442,7 +432,6 @@ void M(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -495,7 +484,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -555,7 +543,6 @@ unsafe void M(int* v0, int* v1) await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } } diff --git a/src/EditorFeatures/CSharpTest/CodeActions/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs index 7254e15840547..1b977d47ab288 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs @@ -3969,7 +3969,6 @@ class Program await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } diff --git a/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/AddParameterCheckTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/AddParameterCheckTests.cs index cf878d73011f8..51e66334cc26d 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/AddParameterCheckTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/AddParameterCheckTests.cs @@ -49,8 +49,7 @@ public C([||]string s) await new VerifyCS.Test { LanguageVersion = LanguageVersion.CSharp11, - TestCode = testCode, - FixedCode = testCode + TestCode = testCode }.RunAsync(); } @@ -261,8 +260,7 @@ partial void M(string s) await new VerifyCS.Test { LanguageVersion = languageVersion, - TestCode = code, - FixedCode = code + TestCode = code }.RunAsync(); } @@ -284,8 +282,7 @@ public partial void M(string s) await new VerifyCS.Test { LanguageVersion = LanguageVersion.CSharp9, - TestCode = code, - FixedCode = code + TestCode = code }.RunAsync(); } @@ -309,8 +306,7 @@ partial void M(string s) await new VerifyCS.Test { LanguageVersion = languageVersion, - TestCode = code, - FixedCode = code + TestCode = code }.RunAsync(); } @@ -332,8 +328,7 @@ public partial void M(string s) await new VerifyCS.Test { LanguageVersion = LanguageVersion.CSharp9, - TestCode = code, - FixedCode = code + TestCode = code }.RunAsync(); } @@ -1717,8 +1712,7 @@ public C() await new VerifyCS.Test { LanguageVersion = LanguageVersion.CSharp11, - TestCode = testCode, - FixedCode = testCode + TestCode = testCode }.RunAsync(); } @@ -1831,7 +1825,6 @@ int this[[||]string s] await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp11, ReferenceAssemblies = ReferenceAssemblies.Net.Net50, }.RunAsync(); @@ -2933,7 +2926,6 @@ record C([||]string s) { public string s; } { LanguageVersion = version, TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -2947,7 +2939,6 @@ class C([||]string s) { public string s; } { LanguageVersion = LanguageVersion.CSharp12, TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -2961,7 +2952,6 @@ struct C([||]string s) { public string s; } { LanguageVersion = LanguageVersion.CSharp12, TestCode = code, - FixedCode = code, }.RunAsync(); } diff --git a/src/EditorFeatures/CSharpTest/CodeActions/MoveStaticMembers/CSharpMoveStaticMembersTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/MoveStaticMembers/CSharpMoveStaticMembersTests.cs index 62a5bf040e58a..356a59d1b690b 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/MoveStaticMembers/CSharpMoveStaticMembersTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/MoveStaticMembers/CSharpMoveStaticMembersTests.cs @@ -3224,7 +3224,6 @@ public class Class1 await new Test("", ImmutableArray.Empty, "") { TestCode = initialMarkup, - FixedCode = initialMarkup, }.RunAsync().ConfigureAwait(false); } @@ -3242,7 +3241,6 @@ public class Class1 await new Test("", ImmutableArray.Empty, "") { TestCode = initialMarkup, - FixedCode = initialMarkup, }.RunAsync().ConfigureAwait(false); } @@ -3260,7 +3258,6 @@ public class Class1 await new Test("", ImmutableArray.Empty, "") { TestCode = initialMarkup, - FixedCode = initialMarkup, }.RunAsync().ConfigureAwait(false); } @@ -3278,7 +3275,6 @@ public class Class1 await new Test("", ImmutableArray.Empty, "") { TestCode = initialMarkup, - FixedCode = initialMarkup, }.RunAsync().ConfigureAwait(false); } @@ -3296,7 +3292,6 @@ public class Class1 await new Test("", ImmutableArray.Empty, "") { TestCode = initialMarkup, - FixedCode = initialMarkup, }.RunAsync().ConfigureAwait(false); } @@ -3389,7 +3384,6 @@ public async Task TestSelectTopLevelStatement_NoAction1() await new Test("", ImmutableArray.Empty, "") { TestCode = initialMarkup, - FixedCode = initialMarkup, LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.CSharp10, TestState = { @@ -3410,7 +3404,6 @@ public async Task TestSelectTopLevelStatement_NoAction2() await new Test("", ImmutableArray.Empty, "") { TestCode = initialMarkup, - FixedCode = initialMarkup, LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.CSharp10, TestState = { @@ -3434,7 +3427,6 @@ public async Task TestSelectTopLevelLocalFunction_NoAction() await new Test("", ImmutableArray.Empty, "") { TestCode = initialMarkup, - FixedCode = initialMarkup, LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.CSharp10, TestState = { @@ -3557,7 +3549,6 @@ private static async Task TestNoRefactoringAsync(string initialMarkup) await new Test("", ImmutableArray.Empty, "") { TestCode = initialMarkup, - FixedCode = initialMarkup, }.RunAsync().ConfigureAwait(false); } } diff --git a/src/Features/CSharpTest/ConvertCast/ConvertDirectCastToTryCastTests.cs b/src/Features/CSharpTest/ConvertCast/ConvertDirectCastToTryCastTests.cs index dcdd2d76d2b55..8347779d25a12 100644 --- a/src/Features/CSharpTest/ConvertCast/ConvertDirectCastToTryCastTests.cs +++ b/src/Features/CSharpTest/ConvertCast/ConvertDirectCastToTryCastTests.cs @@ -103,7 +103,6 @@ public static void Main() await new VerifyCS.Test { TestCode = InitialMarkup, - FixedCode = InitialMarkup, OffersEmptyRefactoring = false, CodeActionValidationMode = CodeActionValidationMode.None, }.RunAsync(); @@ -125,7 +124,6 @@ public void M() where T: struct await new VerifyCS.Test { TestCode = InitialMarkup, - FixedCode = InitialMarkup, OffersEmptyRefactoring = false, CodeActionValidationMode = CodeActionValidationMode.None, }.RunAsync(); @@ -147,7 +145,6 @@ public void M() await new VerifyCS.Test { TestCode = InitialMarkup, - FixedCode = InitialMarkup, OffersEmptyRefactoring = false, CodeActionValidationMode = CodeActionValidationMode.None, }.RunAsync(); diff --git a/src/Features/CSharpTest/ConvertCast/ConvertTryCastToDirectCastTests.cs b/src/Features/CSharpTest/ConvertCast/ConvertTryCastToDirectCastTests.cs index 2edc7508b99c5..3e9cd7af78c82 100644 --- a/src/Features/CSharpTest/ConvertCast/ConvertTryCastToDirectCastTests.cs +++ b/src/Features/CSharpTest/ConvertCast/ConvertTryCastToDirectCastTests.cs @@ -62,7 +62,6 @@ public static void Main() await new VerifyCS.Test { TestCode = InitialMarkup, - FixedCode = InitialMarkup, CompilerDiagnostics = CompilerDiagnostics.None, // CS0077 is present, but we present the refactoring anyway (this may overlap with a diagnostic fixer) OffersEmptyRefactoring = false, CodeActionValidationMode = CodeActionValidationMode.Full, @@ -84,7 +83,6 @@ public static void Main() await new VerifyCS.Test { TestCode = InitialMarkup, - FixedCode = InitialMarkup, CompilerDiagnostics = CompilerDiagnostics.None, OffersEmptyRefactoring = false, CodeActionValidationMode = CodeActionValidationMode.None, diff --git a/src/Features/CSharpTest/ConvertIfToSwitch/ConvertIfToSwitchTests.cs b/src/Features/CSharpTest/ConvertIfToSwitch/ConvertIfToSwitchTests.cs index d16e6a17d6fbd..dd2c121016149 100644 --- a/src/Features/CSharpTest/ConvertIfToSwitch/ConvertIfToSwitchTests.cs +++ b/src/Features/CSharpTest/ConvertIfToSwitch/ConvertIfToSwitchTests.cs @@ -2428,7 +2428,6 @@ void M(int i) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp8, }.RunAsync(); } @@ -2498,7 +2497,6 @@ void M(int i) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp8, }.RunAsync(); } @@ -2616,7 +2614,6 @@ void M(int i) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp8, }.RunAsync(); } @@ -2886,7 +2883,6 @@ void M(char c) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -2911,7 +2907,6 @@ int M(char c) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -2934,7 +2929,6 @@ void M(char c) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -2957,7 +2951,6 @@ void M(char c) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } diff --git a/src/Features/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs b/src/Features/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs index 37b1cc83d28ab..fa645a11949f8 100644 --- a/src/Features/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs +++ b/src/Features/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs @@ -36,7 +36,6 @@ namespace $$N await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, Options = { @@ -56,7 +55,6 @@ namespace $$N await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -119,7 +117,6 @@ namespace N await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -139,7 +136,6 @@ namespace N await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -163,7 +159,6 @@ namespace N2 await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -186,7 +181,6 @@ namespace N2 await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -209,7 +203,6 @@ namespace $$N2 await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -231,7 +224,6 @@ namespace $$N await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -253,7 +245,6 @@ namespace $$N await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -623,7 +614,6 @@ namespace $$N; await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -684,7 +674,6 @@ namespace N; await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -703,7 +692,6 @@ namespace N; await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -721,7 +709,6 @@ namespace N; $$ await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { diff --git a/src/Features/CSharpTest/ConvertProgram/ConvertToProgramMainAnalyzerTests.cs b/src/Features/CSharpTest/ConvertProgram/ConvertToProgramMainAnalyzerTests.cs index 3d57289bb6c0e..929676950bda8 100644 --- a/src/Features/CSharpTest/ConvertProgram/ConvertToProgramMainAnalyzerTests.cs +++ b/src/Features/CSharpTest/ConvertProgram/ConvertToProgramMainAnalyzerTests.cs @@ -28,7 +28,6 @@ public async Task NotOfferedWhenUserPrefersTopLevelStatements() await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true } }, @@ -51,7 +50,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, false } }, }.RunAsync(); @@ -139,7 +137,6 @@ public async Task NotOfferedInLibrary() await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, false, NotificationOption2.Silent } }, }.RunAsync(); @@ -155,7 +152,6 @@ public async Task NotOfferedWhenSuppressed() await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, false, NotificationOption2.None } }, diff --git a/src/Features/CSharpTest/ConvertProgram/ConvertToProgramMainRefactoringTests.cs b/src/Features/CSharpTest/ConvertProgram/ConvertToProgramMainRefactoringTests.cs index 6688cdae18cea..77cf9e04aaecb 100644 --- a/src/Features/CSharpTest/ConvertProgram/ConvertToProgramMainRefactoringTests.cs +++ b/src/Features/CSharpTest/ConvertProgram/ConvertToProgramMainRefactoringTests.cs @@ -31,7 +31,6 @@ class C await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, TestState = { OutputKind = OutputKind.ConsoleApplication }, ExpectedDiagnostics = @@ -51,7 +50,6 @@ public async Task TestNotOnEmptyFile() await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, TestState = { OutputKind = OutputKind.ConsoleApplication }, ExpectedDiagnostics = @@ -93,7 +91,6 @@ public async Task TestNotOfferedInLibrary() await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -133,7 +130,6 @@ public async Task TestNoConvertToProgramMainWithProgramMainPreferenceSuggestion( await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = @@ -152,7 +148,6 @@ public async Task TestNoConvertToProgramMainWithProgramMainPreferenceSilent() await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = diff --git a/src/Features/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsAnalyzerTests.cs b/src/Features/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsAnalyzerTests.cs index 90a0ff9dac731..0043e5831dc3a 100644 --- a/src/Features/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsAnalyzerTests.cs +++ b/src/Features/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsAnalyzerTests.cs @@ -44,7 +44,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, false } }, @@ -67,7 +66,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp8, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true } }, @@ -246,7 +244,6 @@ static void Main() await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true } }, }.RunAsync(); @@ -291,7 +288,6 @@ void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -319,7 +315,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -347,7 +342,6 @@ static void Main1(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -372,7 +366,6 @@ class Program await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -394,7 +387,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -417,7 +409,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -440,7 +431,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -467,7 +457,6 @@ partial class Program await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -490,7 +479,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -514,7 +502,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -538,7 +525,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -589,7 +575,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -615,7 +600,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -641,7 +625,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -667,7 +650,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -692,7 +674,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -717,7 +698,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -744,7 +724,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -771,7 +750,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -796,7 +774,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -821,7 +798,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -846,7 +822,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -869,7 +844,6 @@ static void Main(string[] args1) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, diff --git a/src/Features/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsRefactoringTests.cs b/src/Features/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsRefactoringTests.cs index d429d6b707a7d..95721cc2dbbb9 100644 --- a/src/Features/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsRefactoringTests.cs +++ b/src/Features/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsRefactoringTests.cs @@ -31,7 +31,6 @@ public async Task TestNotOnEmptyFile() await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, TestState = { OutputKind = OutputKind.ConsoleApplication }, ExpectedDiagnostics = @@ -59,7 +58,6 @@ class Program await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, TestState = { OutputKind = OutputKind.ConsoleApplication }, }.RunAsync(); @@ -108,7 +106,6 @@ class Program await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -133,7 +130,6 @@ class Program await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = @@ -165,7 +161,6 @@ class Program await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp8, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = @@ -190,7 +185,6 @@ class Program await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = @@ -215,7 +209,6 @@ class Program await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = diff --git a/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.cs b/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.cs index cfad68025c998..93af30d6414fe 100644 --- a/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.cs +++ b/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.cs @@ -1024,7 +1024,6 @@ class C { LanguageVersion = LanguageVersion.CSharp9, TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -1081,7 +1080,6 @@ void M() { { LanguageVersion = LanguageVersion.CSharp9, TestCode = code, - FixedCode = code, }.RunAsync(); await new VerifyCS.Test diff --git a/src/Features/CSharpTest/EmbeddedLanguages/JsonStringDetectorTests.cs b/src/Features/CSharpTest/EmbeddedLanguages/JsonStringDetectorTests.cs index ace78f9016557..192c6fc3a0e01 100644 --- a/src/Features/CSharpTest/EmbeddedLanguages/JsonStringDetectorTests.cs +++ b/src/Features/CSharpTest/EmbeddedLanguages/JsonStringDetectorTests.cs @@ -118,7 +118,6 @@ void Goo() await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -137,7 +136,6 @@ void Goo() await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } } diff --git a/src/Features/CSharpTest/EnableNullable/EnableNullableTests.cs b/src/Features/CSharpTest/EnableNullable/EnableNullableTests.cs index eee1e1fd5efa7..bcf701a0ec174 100644 --- a/src/Features/CSharpTest/EnableNullable/EnableNullableTests.cs +++ b/src/Features/CSharpTest/EnableNullable/EnableNullableTests.cs @@ -545,7 +545,6 @@ public async Task DisabledIfSetInProject(NullableContextOptions nullableContextO await new VerifyCS.Test { TestCode = code, - FixedCode = code, SolutionTransforms = { (solution, projectId) => diff --git a/src/Features/CSharpTest/ExtractClass/ExtractClassTests.cs b/src/Features/CSharpTest/ExtractClass/ExtractClassTests.cs index 447e92479f21a..53100c1716a75 100644 --- a/src/Features/CSharpTest/ExtractClass/ExtractClassTests.cs +++ b/src/Features/CSharpTest/ExtractClass/ExtractClassTests.cs @@ -115,7 +115,6 @@ class Test : ErrorBase await new Test { TestCode = input, - FixedCode = input, }.RunAsync(); } @@ -135,7 +134,6 @@ class Test await new Test { TestCode = input, - FixedCode = input, WorkspaceKind = WorkspaceKind.MiscellaneousFiles }.RunAsync(); } @@ -432,7 +430,6 @@ record R(string $$S) await new Test { TestCode = input, - FixedCode = input, LanguageVersion = LanguageVersion.CSharp9, ReferenceAssemblies = ReferenceAssemblies.Net.Net50, }.RunAsync(); @@ -450,7 +447,6 @@ class R(string $$S) await new Test { TestCode = input, - FixedCode = input, LanguageVersion = LanguageVersion.CSharp12, ReferenceAssemblies = ReferenceAssemblies.Net.Net50, }.RunAsync(); @@ -466,7 +462,6 @@ class R(string $$S); await new Test { TestCode = input, - FixedCode = input, LanguageVersion = LanguageVersion.CSharp12, ReferenceAssemblies = ReferenceAssemblies.Net.Net50, }.RunAsync(); @@ -484,7 +479,6 @@ struct R(string $$S) await new Test { TestCode = input, - FixedCode = input, LanguageVersion = LanguageVersion.CSharp12, ReferenceAssemblies = ReferenceAssemblies.Net.Net50, }.RunAsync(); @@ -500,7 +494,6 @@ struct R(string $$S); await new Test { TestCode = input, - FixedCode = input, LanguageVersion = LanguageVersion.CSharp12, ReferenceAssemblies = ReferenceAssemblies.Net.Net50, }.RunAsync(); @@ -521,7 +514,6 @@ record struct R(string S) await new Test { TestCode = input, - FixedCode = input, LanguageVersion = LanguageVersion.CSharp10, ReferenceAssemblies = ReferenceAssemblies.Net.Net50, }.RunAsync(); @@ -542,7 +534,6 @@ struct R(string S) await new Test { TestCode = input, - FixedCode = input, LanguageVersion = LanguageVersion.CSharp12, ReferenceAssemblies = ReferenceAssemblies.Net.Net50, }.RunAsync(); @@ -2817,8 +2808,7 @@ class C """; await new Test { - TestCode = input, - FixedCode = input + TestCode = input }.RunAsync(); } @@ -2836,8 +2826,7 @@ class C """; await new Test { - TestCode = input, - FixedCode = input + TestCode = input }.RunAsync(); } @@ -2850,7 +2839,6 @@ public async Task TestTopLevelStatementSelection_NoAction() await new Test { TestCode = input, - FixedCode = input, LanguageVersion = LanguageVersion.CSharp10, TestState = { @@ -2987,8 +2975,7 @@ public void N() { }|] await new Test() { - TestCode = code, - FixedCode = code + TestCode = code }.RunAsync(); } @@ -3009,8 +2996,7 @@ public void N() { } await new Test() { - TestCode = code, - FixedCode = code + TestCode = code }.RunAsync(); } diff --git a/src/Features/CSharpTest/GenerateFromMembers/AddConstructorParametersFromMembers/AddConstructorParametersFromMembersTests.cs b/src/Features/CSharpTest/GenerateFromMembers/AddConstructorParametersFromMembers/AddConstructorParametersFromMembersTests.cs index 3b5fa7bc1a794..9f908a3862680 100644 --- a/src/Features/CSharpTest/GenerateFromMembers/AddConstructorParametersFromMembers/AddConstructorParametersFromMembersTests.cs +++ b/src/Features/CSharpTest/GenerateFromMembers/AddConstructorParametersFromMembers/AddConstructorParametersFromMembersTests.cs @@ -1835,7 +1835,6 @@ public C() await new VerifyCS.Test { TestCode = source, - FixedCode = source, EditorConfig = FieldNamesCamelCaseWithFieldUnderscorePrefixEndUnderscoreSuffixEditorConfig }.RunAsync(); } diff --git a/src/Features/CSharpTest/SimplifyPropertyPattern/SimplifyPropertyPatternTests.cs b/src/Features/CSharpTest/SimplifyPropertyPattern/SimplifyPropertyPatternTests.cs index d9bb2f0109845..3d4befd345afc 100644 --- a/src/Features/CSharpTest/SimplifyPropertyPattern/SimplifyPropertyPatternTests.cs +++ b/src/Features/CSharpTest/SimplifyPropertyPattern/SimplifyPropertyPatternTests.cs @@ -42,7 +42,6 @@ void S(Type t) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -108,7 +107,6 @@ void S(Type t) await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -134,7 +132,6 @@ void S(Type t) await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -160,7 +157,6 @@ void S(Type t) await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -186,7 +182,6 @@ void S(Type t) await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -212,7 +207,6 @@ void S(Type t) await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -238,7 +232,6 @@ void S(Type t) await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -750,7 +743,6 @@ void S(R r) await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, LanguageVersion = LanguageVersion.CSharp10, ReferenceAssemblies = ReferenceAssemblies.Net.Net50, }.RunAsync(); @@ -773,7 +765,6 @@ void S(R r) await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, LanguageVersion = LanguageVersion.CSharp10, ReferenceAssemblies = ReferenceAssemblies.Net.Net50, }.RunAsync(); diff --git a/src/Features/CSharpTest/UseNameofInAttribute/UseNameofInAttributeTests.cs b/src/Features/CSharpTest/UseNameofInAttribute/UseNameofInAttributeTests.cs index 21e46e4308899..fa81ca0737c9d 100644 --- a/src/Features/CSharpTest/UseNameofInAttribute/UseNameofInAttributeTests.cs +++ b/src/Features/CSharpTest/UseNameofInAttribute/UseNameofInAttributeTests.cs @@ -174,7 +174,6 @@ class C await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, ReferenceAssemblies = ReferenceAssemblies.Net.Net60, }.RunAsync(); @@ -196,7 +195,6 @@ class C await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp11, ReferenceAssemblies = ReferenceAssemblies.Net.Net60, }.RunAsync(); @@ -218,7 +216,6 @@ class C await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp11, ReferenceAssemblies = ReferenceAssemblies.Net.Net60, }.RunAsync(); @@ -240,7 +237,6 @@ class C await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp11, ReferenceAssemblies = ReferenceAssemblies.Net.Net60, }.RunAsync(); From 6fb1f239cadae6e6085fabcf8732eee6c83bd836 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 15:08:34 -0500 Subject: [PATCH 437/508] Support unbound generic types in 'nameof' operator. (#75368) Co-authored-by: Julien Couvreur --- .../CSharp/Portable/Binder/Binder_Symbols.cs | 32 +- .../CSharp/Portable/Binder/NameofBinder.cs | 12 +- .../CSharp/Portable/Binder/OpenTypeVisitor.cs | 99 ++ .../CSharp/Portable/Binder/TypeofBinder.cs | 87 -- .../CSharp/Portable/CSharpResources.resx | 3 + .../CSharp/Portable/Errors/MessageID.cs | 2 + .../CSharp/Portable/Parser/LanguageParser.cs | 9 + .../Portable/xlf/CSharpResources.cs.xlf | 5 + .../Portable/xlf/CSharpResources.de.xlf | 5 + .../Portable/xlf/CSharpResources.es.xlf | 5 + .../Portable/xlf/CSharpResources.fr.xlf | 5 + .../Portable/xlf/CSharpResources.it.xlf | 5 + .../Portable/xlf/CSharpResources.ja.xlf | 5 + .../Portable/xlf/CSharpResources.ko.xlf | 5 + .../Portable/xlf/CSharpResources.pl.xlf | 5 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 5 + .../Portable/xlf/CSharpResources.ru.xlf | 5 + .../Portable/xlf/CSharpResources.tr.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 5 + .../Emit3/FlowAnalysis/FlowDiagnosticTests.cs | 15 +- .../Test/Semantic/Semantics/NameOfTests.cs | 713 +++++++++++++- .../Test/Semantic/Semantics/TypeOfTests.cs | 84 ++ .../Parsing/TypeArgumentListParsingTests.cs | 878 +++++++++++++++++- 24 files changed, 1887 insertions(+), 112 deletions(-) create mode 100644 src/Compilers/CSharp/Portable/Binder/OpenTypeVisitor.cs diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index c35c4512565cd..b3c42874d46a0 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -1373,14 +1373,32 @@ private NamedTypeSymbol ConstructNamedTypeUnlessTypeArgumentOmitted(SyntaxNode t { if (typeArgumentsSyntax.Any(SyntaxKind.OmittedTypeArgument)) { - // Note: lookup won't have reported this, since the arity was correct. - // CONSIDER: the text of this error message makes sense, but we might want to add a separate code. - Error(diagnostics, ErrorCode.ERR_BadArity, typeSyntax, type, MessageID.IDS_SK_TYPE.Localize(), typeArgumentsSyntax.Count); + if (this.IsInsideNameof) + { + // Inside a nameof an open-generic type is acceptable. Fall through and bind the remainder accordingly. + CheckFeatureAvailability(typeSyntax, MessageID.IDS_FeatureUnboundGenericTypesInNameof, diagnostics); + + // From the spec: + // + // Member lookup on an unbound type expression will be performed the same way as for a `this` + // expression within that type declaration. + // + // So we want to just return the originating type symbol as is (e.g. List in nameof(List<>)). + // This is distinctly different than how typeof(List<>) works, where it returns an unbound generic + // type. + } + else + { + // Note: lookup won't have reported this, since the arity was correct. + // CONSIDER: the text of this error message makes sense, but we might want to add a separate code. + + // If the syntax looks like an unbound generic type, then they probably wanted the definition. + // Give an error indicating that the syntax is incorrect and then use the definition. + // CONSIDER: we could construct an unbound generic type symbol, but that would probably be confusing + // outside a typeof. + Error(diagnostics, ErrorCode.ERR_BadArity, typeSyntax, type, MessageID.IDS_SK_TYPE.Localize(), typeArgumentsSyntax.Count); + } - // If the syntax looks like an unbound generic type, then they probably wanted the definition. - // Give an error indicating that the syntax is incorrect and then use the definition. - // CONSIDER: we could construct an unbound generic type symbol, but that would probably be confusing - // outside a typeof. return type; } else diff --git a/src/Compilers/CSharp/Portable/Binder/NameofBinder.cs b/src/Compilers/CSharp/Portable/Binder/NameofBinder.cs index 668cad30a97f3..acf4ba3dfcf59 100644 --- a/src/Compilers/CSharp/Portable/Binder/NameofBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/NameofBinder.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Generic; using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp @@ -19,19 +21,25 @@ namespace Microsoft.CodeAnalysis.CSharp /// internal sealed class NameofBinder : Binder { - private readonly SyntaxNode _nameofArgument; + private readonly ExpressionSyntax _nameofArgument; private readonly WithTypeParametersBinder? _withTypeParametersBinder; private readonly Binder? _withParametersBinder; private ThreeState _lazyIsNameofOperator; - internal NameofBinder(SyntaxNode nameofArgument, Binder next, WithTypeParametersBinder? withTypeParametersBinder, Binder? withParametersBinder) + private readonly Dictionary? _allowedMap; + + internal NameofBinder(ExpressionSyntax nameofArgument, Binder next, WithTypeParametersBinder? withTypeParametersBinder, Binder? withParametersBinder) : base(next) { _nameofArgument = nameofArgument; _withTypeParametersBinder = withTypeParametersBinder; _withParametersBinder = withParametersBinder; + OpenTypeVisitor.Visit(nameofArgument, out _allowedMap); } + protected override bool IsUnboundTypeAllowed(GenericNameSyntax syntax) + => _allowedMap != null && _allowedMap.TryGetValue(syntax, out bool allowed) && allowed; + private bool IsNameofOperator { get diff --git a/src/Compilers/CSharp/Portable/Binder/OpenTypeVisitor.cs b/src/Compilers/CSharp/Portable/Binder/OpenTypeVisitor.cs new file mode 100644 index 0000000000000..7654a5e40e9e1 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Binder/OpenTypeVisitor.cs @@ -0,0 +1,99 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Microsoft.CodeAnalysis.CSharp; + +internal partial class Binder +{ + /// + /// This visitor walks over a type expression looking for open types. + /// + /// Open types are allowed if an only if: + /// + /// There is no constructed generic type elsewhere in the visited syntax; and + /// The open type is not used as a type argument or array/pointer/nullable element type. + /// + /// + /// Open types can be used both in typeof(...) and nameof(...) expressions. + /// + protected sealed class OpenTypeVisitor : CSharpSyntaxVisitor + { + private Dictionary? _allowedMap; + private bool _seenConstructed; + + /// The argument to typeof. + /// + /// Keys are GenericNameSyntax nodes representing unbound generic types. + /// Values are false if the node should result in an error and true otherwise. + /// + public static void Visit(ExpressionSyntax typeSyntax, out Dictionary? allowedMap) + { + OpenTypeVisitor visitor = new OpenTypeVisitor(); + visitor.Visit(typeSyntax); + allowedMap = visitor._allowedMap; + } + + public override void VisitGenericName(GenericNameSyntax node) + { + SeparatedSyntaxList typeArguments = node.TypeArgumentList.Arguments; + if (node.IsUnboundGenericName) + { + _allowedMap ??= new Dictionary(); + _allowedMap[node] = !_seenConstructed; + } + else + { + _seenConstructed = true; + foreach (TypeSyntax arg in typeArguments) + { + Visit(arg); + } + } + } + + public override void VisitQualifiedName(QualifiedNameSyntax node) + { + bool seenConstructedBeforeRight = _seenConstructed; + + // Visit Right first because it's smaller (to make backtracking cheaper). + Visit(node.Right); + + bool seenConstructedBeforeLeft = _seenConstructed; + + Visit(node.Left); + + // If the first time we saw a constructed type was in Left, then we need to re-visit Right + if (!seenConstructedBeforeRight && !seenConstructedBeforeLeft && _seenConstructed) + { + Visit(node.Right); + } + } + + public override void VisitAliasQualifiedName(AliasQualifiedNameSyntax node) + { + Visit(node.Name); + } + + public override void VisitArrayType(ArrayTypeSyntax node) + { + _seenConstructed = true; + Visit(node.ElementType); + } + + public override void VisitPointerType(PointerTypeSyntax node) + { + _seenConstructed = true; + Visit(node.ElementType); + } + + public override void VisitNullableType(NullableTypeSyntax node) + { + _seenConstructed = true; + Visit(node.ElementType); + } + } +} diff --git a/src/Compilers/CSharp/Portable/Binder/TypeofBinder.cs b/src/Compilers/CSharp/Portable/Binder/TypeofBinder.cs index 279fbd58e052c..7f0c3d231363e 100644 --- a/src/Compilers/CSharp/Portable/Binder/TypeofBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/TypeofBinder.cs @@ -36,92 +36,5 @@ protected override bool IsUnboundTypeAllowed(GenericNameSyntax syntax) bool allowed; return _allowedMap != null && _allowedMap.TryGetValue(syntax, out allowed) && allowed; } - - /// - /// This visitor walks over a type expression looking for open types. - /// Open types are allowed if an only if: - /// 1) There is no constructed generic type elsewhere in the visited syntax; and - /// 2) The open type is not used as a type argument or array/pointer/nullable - /// element type. - /// - private class OpenTypeVisitor : CSharpSyntaxVisitor - { - private Dictionary _allowedMap; - private bool _seenConstructed; - - /// The argument to typeof. - /// - /// Keys are GenericNameSyntax nodes representing unbound generic types. - /// Values are false if the node should result in an error and true otherwise. - /// - public static void Visit(ExpressionSyntax typeSyntax, out Dictionary allowedMap) - { - OpenTypeVisitor visitor = new OpenTypeVisitor(); - visitor.Visit(typeSyntax); - allowedMap = visitor._allowedMap; - } - - public override void VisitGenericName(GenericNameSyntax node) - { - SeparatedSyntaxList typeArguments = node.TypeArgumentList.Arguments; - if (node.IsUnboundGenericName) - { - if (_allowedMap == null) - { - _allowedMap = new Dictionary(); - } - _allowedMap[node] = !_seenConstructed; - } - else - { - _seenConstructed = true; - foreach (TypeSyntax arg in typeArguments) - { - Visit(arg); - } - } - } - - public override void VisitQualifiedName(QualifiedNameSyntax node) - { - bool seenConstructedBeforeRight = _seenConstructed; - - // Visit Right first because it's smaller (to make backtracking cheaper). - Visit(node.Right); - - bool seenConstructedBeforeLeft = _seenConstructed; - - Visit(node.Left); - - // If the first time we saw a constructed type was in Left, then we need to re-visit Right - if (!seenConstructedBeforeRight && !seenConstructedBeforeLeft && _seenConstructed) - { - Visit(node.Right); - } - } - - public override void VisitAliasQualifiedName(AliasQualifiedNameSyntax node) - { - Visit(node.Name); - } - - public override void VisitArrayType(ArrayTypeSyntax node) - { - _seenConstructed = true; - Visit(node.ElementType); - } - - public override void VisitPointerType(PointerTypeSyntax node) - { - _seenConstructed = true; - Visit(node.ElementType); - } - - public override void VisitNullableType(NullableTypeSyntax node) - { - _seenConstructed = true; - Visit(node.ElementType); - } - } } } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 2a355f98b4b39..85dd758873c3f 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -4932,6 +4932,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ nameof operator + + unbound generic types in nameof operator + dictionary initializer diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index ddf0e8d382d5b..0783ddd7a7a0f 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -294,6 +294,7 @@ internal enum MessageID IDS_OverloadResolutionPriority = MessageBase + 12848, IDS_FeatureFirstClassSpan = MessageBase + 12849, + IDS_FeatureUnboundGenericTypesInNameof = MessageBase + 12850, } // Message IDs may refer to strings that need to be localized. @@ -476,6 +477,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) // C# preview features. case MessageID.IDS_FeatureFieldKeyword: case MessageID.IDS_FeatureFirstClassSpan: + case MessageID.IDS_FeatureUnboundGenericTypesInNameof: return LanguageVersion.Preview; // C# 13.0 features. diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 37d5e9817022e..8ed96d2f9c71a 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -6052,6 +6052,15 @@ private ScanTypeFlags ScanPossibleTypeArgumentList( return result; } + // Allow for any chain of errant commas in the generic name. like `Dictionary<,int>` or + // `Dictionary` We still want to think of these as generics, just with missing type-arguments, vs + // some invalid tree-expression that we would otherwise form. + if (this.CurrentToken.Kind == SyntaxKind.CommaToken) + { + lastScannedType = default; + continue; + } + lastScannedType = this.ScanType(out _); switch (lastScannedType) { diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 9280853e7d4e2..a5d4e7d459849 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -2577,6 +2577,11 @@ Inicializátory polí struktury + + unbound generic types in nameof operator + unbound generic types in nameof operator + + unsigned right shift nepodepsaný pravý posun diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index f1ab55a44fbfe..49661004a2962 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -2577,6 +2577,11 @@ Strukturfeldinitialisierer + + unbound generic types in nameof operator + unbound generic types in nameof operator + + unsigned right shift Unsignierte Rechtsverschiebung diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 8fcc5196b9532..581451baade5e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -2577,6 +2577,11 @@ inicializadores de campo de estructura + + unbound generic types in nameof operator + unbound generic types in nameof operator + + unsigned right shift cambio derecho sin firmar diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index e0a6124ed6b93..246bddbdd5cc6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -2577,6 +2577,11 @@ initialiseurs de champ de struct + + unbound generic types in nameof operator + unbound generic types in nameof operator + + unsigned right shift shift droit non signé diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 6e9edfb0ce2a4..438db326e9229 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -2577,6 +2577,11 @@ inizializzatori di campo struct + + unbound generic types in nameof operator + unbound generic types in nameof operator + + unsigned right shift spostamento a destra senza segno diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index c631942fc593f..e6eb82d333c76 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -2577,6 +2577,11 @@ 構造体フィールド初期化子 + + unbound generic types in nameof operator + unbound generic types in nameof operator + + unsigned right shift 符号なし右シフト diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 4e72992be7e73..303ca7e9dc7a5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -2577,6 +2577,11 @@ 구조체 필드 이니셜라이저 + + unbound generic types in nameof operator + unbound generic types in nameof operator + + unsigned right shift 부호 없는 오른쪽 시프트 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 2377df8f9e139..a0d2f13e94d99 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -2577,6 +2577,11 @@ inicjatory pola struktury + + unbound generic types in nameof operator + unbound generic types in nameof operator + + unsigned right shift niepodpisane przesunięcie w prawo diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 5a9e8a14a525c..e6828357934e3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -2577,6 +2577,11 @@ inicializadores de campo de struct + + unbound generic types in nameof operator + unbound generic types in nameof operator + + unsigned right shift deslocamento direito não atribuído diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index afd7cc97f6b04..af40b0f4b46af 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -2577,6 +2577,11 @@ инициализаторы полей структуры + + unbound generic types in nameof operator + unbound generic types in nameof operator + + unsigned right shift сдвиг вправо без знака diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 5feae747be33d..62efb622ebeaf 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -2577,6 +2577,11 @@ struct alan başlatıcıları + + unbound generic types in nameof operator + unbound generic types in nameof operator + + unsigned right shift işaretsiz sağ kaydırma diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index bfdbc17e43f04..6f8caf4fd86c1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -2577,6 +2577,11 @@ 结构字段初始化表达式 + + unbound generic types in nameof operator + unbound generic types in nameof operator + + unsigned right shift 未签名的右移位 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index c510281698946..c8ffa8fac8617 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -2577,6 +2577,11 @@ 結構欄位初始設定式 + + unbound generic types in nameof operator + unbound generic types in nameof operator + + unsigned right shift 未簽署右移位 diff --git a/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/FlowDiagnosticTests.cs b/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/FlowDiagnosticTests.cs index a85ac73bb0e85..dd184de3140b9 100644 --- a/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/FlowDiagnosticTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/FlowDiagnosticTests.cs @@ -858,10 +858,17 @@ public static int goo(int i) return 1; } }"; - var comp = CreateCompilation(program); - var parseErrors = comp.SyntaxTrees[0].GetDiagnostics(); - var errors = comp.GetDiagnostics(); - Assert.Equal(parseErrors.Count(), errors.Count()); + var comp = CreateCompilation(program).VerifyDiagnostics( + // (6,17): error CS0305: Using the generic method 'Program.goo(int)' requires 1 type arguments + // var s = goo<,int>(123); + Diagnostic(ErrorCode.ERR_BadArity, "goo<,int>").WithArguments("Program.goo(int)", "method", "1").WithLocation(6, 17), + // (6,21): error CS1031: Type expected + // var s = goo<,int>(123); + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(6, 21)); + comp.SyntaxTrees[0].GetDiagnostics().Verify( + // (6,21): error CS1031: Type expected + // var s = goo<,int>(123); + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(6, 21)); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index 572f5f8ddd22d..216a495096a2a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -4,7 +4,6 @@ #nullable disable -using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; @@ -16,7 +15,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests { - public class NameofTests : CSharpTestBase + public sealed class NameofTests : CSharpTestBase { [Fact] public void TestGoodNameofInstances() @@ -249,9 +248,6 @@ class Test // (17,66): error CS1031: Type expected // s = nameof(System.Collections.Generic.Dictionary.KeyCollection); Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(17, 66), - // (11,27): error CS0305: Using the generic type 'Action' requires 1 type arguments - // s = nameof(System.Action<>); - Diagnostic(ErrorCode.ERR_BadArity, "Action<>").WithArguments("System.Action", "type", "1").WithLocation(11, 27), // (16,20): error CS0103: The name 'List' does not exist in the current context // s = nameof(List.Enumerator); Diagnostic(ErrorCode.ERR_NameNotInContext, "List").WithArguments("List").WithLocation(16, 20), @@ -282,9 +278,6 @@ class Test // (31,20): error CS8083: An alias-qualified name is not an expression. // s = nameof(global::Program); // not an expression Diagnostic(ErrorCode.ERR_AliasQualifiedNameNotAnExpression, "global::Program").WithLocation(31, 20), - // (32,20): error CS0305: Using the generic type 'Test' requires 1 type arguments - // s = nameof(Test<>.s); // inaccessible - Diagnostic(ErrorCode.ERR_BadArity, "Test<>").WithArguments("Test", "type", "1").WithLocation(32, 20), // (32,27): error CS0122: 'Test.s' is inaccessible due to its protection level // s = nameof(Test<>.s); // inaccessible Diagnostic(ErrorCode.ERR_BadAccess, "s").WithArguments("Test.s").WithLocation(32, 27), @@ -2373,5 +2366,709 @@ class Attr : System.Attribute { public Attr(string s) {} }"; CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics(expectedDiagnostics); } + + [Fact] + public void OpenTypeInNameof_Preview() + { + CompileAndVerify(""" + using System; + using System.Collections.Generic; + + var v = nameof(List<>); + Console.WriteLine(v); + """, expectedOutput: "List").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_CSharp13() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List<>); + Console.WriteLine(v); + """, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (4,16): error CS8652: The feature 'unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var v = nameof(List<>); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "List<>").WithArguments("unbound generic types in nameof operator").WithLocation(4, 16)); + } + + [Fact] + public void OpenTypeInNameof_Next() + { + CompileAndVerify(""" + using System; + using System.Collections.Generic; + + var v = nameof(List<>); + Console.WriteLine(v); + """, parseOptions: TestOptions.RegularNext, expectedOutput: "List").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_CSharp13_Nested1() + { + CreateCompilation(""" + using System; + + var v = nameof(A<>.B); + Console.WriteLine(v); + + class A { public class B; } + """, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (3,16): error CS8652: The feature 'unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var v = nameof(A<>.B); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "A<>").WithArguments("unbound generic types in nameof operator").WithLocation(3, 16)); + } + + [Fact] + public void OpenTypeInNameof_CSharp13_Nested2() + { + CreateCompilation(""" + using System; + + var v = nameof(A.B<>); + Console.WriteLine(v); + + class A { public class B; } + """, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (3,23): error CS8652: The feature 'unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var v = nameof(A.B<>); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "B<>").WithArguments("unbound generic types in nameof operator").WithLocation(3, 23)); + } + + [Fact] + public void OpenTypeInNameof_CSharp13_Nested3() + { + CreateCompilation(""" + using System; + + var v = nameof(A<>.B<>); + Console.WriteLine(v); + + class A { public class B; } + """, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (3,16): error CS8652: The feature 'unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var v = nameof(A<>.B<>); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "A<>").WithArguments("unbound generic types in nameof operator").WithLocation(3, 16), + // (3,20): error CS8652: The feature 'unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var v = nameof(A<>.B<>); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "B<>").WithArguments("unbound generic types in nameof operator").WithLocation(3, 20)); + } + + [Fact] + public void OpenTypeInNameof_BaseCase() + { + CompileAndVerify(""" + using System; + using System.Collections.Generic; + + var v = nameof(List<>); + Console.WriteLine(v); + """, expectedOutput: "List").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_Nested1() + { + CompileAndVerify(""" + using System; + + var v = nameof(A<>.B); + Console.WriteLine(v); + + class A { public class B; } + """, expectedOutput: "B").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_Nested2() + { + CompileAndVerify(""" + using System; + + var v = nameof(A.B<>); + Console.WriteLine(v); + + class A { public class B; } + """, expectedOutput: "B").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_Nested3() + { + CompileAndVerify(""" + using System; + + var v = nameof(A<>.B<>); + Console.WriteLine(v); + + class A { public class B; } + """, expectedOutput: "B").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_MultipleTypeArguments() + { + CompileAndVerify(""" + using System; + using System.Collections.Generic; + + var v = nameof(Dictionary<,>); + Console.WriteLine(v); + """, expectedOutput: "Dictionary").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_IncorrectTypeArgumentCount1() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(Dictionary<>); + Console.WriteLine(v); + """).VerifyDiagnostics( + // (2,1): hidden CS8019: Unnecessary using directive. + // using System.Collections.Generic; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using System.Collections.Generic;").WithLocation(2, 1), + // (4,16): error CS0305: Using the generic type 'Dictionary' requires 2 type arguments + // var v = nameof(Dictionary<>); + Diagnostic(ErrorCode.ERR_BadArity, "Dictionary<>").WithArguments("System.Collections.Generic.Dictionary", "type", "2").WithLocation(4, 16)); + } + + [Fact] + public void OpenTypeInNameof_IncorrectTypeArgumentCount2() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List<,>); + Console.WriteLine(v); + """).VerifyDiagnostics( + // (2,1): hidden CS8019: Unnecessary using directive. + // using System.Collections.Generic; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using System.Collections.Generic;").WithLocation(2, 1), + // (4,16): error CS0305: Using the generic type 'List' requires 1 type arguments + // var v = nameof(List<,>); + Diagnostic(ErrorCode.ERR_BadArity, "List<,>").WithArguments("System.Collections.Generic.List", "type", "1").WithLocation(4, 16)); + } + + [Fact] + public void OpenTypeInNameof_NoNestedOpenTypes1() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List>); + Console.WriteLine(v); + """).VerifyDiagnostics( + // (4,21): error CS7003: Unexpected use of an unbound generic name + // var v = nameof(List>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(4, 21)); + } + + [Fact] + public void OpenTypeInNameof_NoNestedOpenTypes2() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List[]>); + Console.WriteLine(v); + """).VerifyDiagnostics( + // (4,21): error CS7003: Unexpected use of an unbound generic name + // var v = nameof(List>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(4, 21)); + } + + [Fact] + public void OpenTypeInNameof_NoNestedOpenTypes3() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List.Inner>); + Console.WriteLine(v); + + public class Outer { public class Inner { } } + """).VerifyDiagnostics( + // (4,21): error CS7003: Unexpected use of an unbound generic name + // var v = nameof(List.Inner>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "Outer<>").WithLocation(4, 21)); + } + + [Fact] + public void OpenTypeInNameof_NoNestedOpenTypes4() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List>); + Console.WriteLine(v); + + public class Outer { public class Inner { } } + """).VerifyDiagnostics( + // (4,27): error CS7003: Unexpected use of an unbound generic name + // var v = nameof(List>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "Inner<>").WithLocation(4, 27)); + } + + [Fact] + public void Nameof_NestedClosedType1() + { + CompileAndVerify(""" + using System; + using System.Collections.Generic; + + var v = nameof(List>); + Console.WriteLine(v); + """, expectedOutput: "List").VerifyDiagnostics(); + } + + [Fact] + public void Nameof_NestedClosedType2() + { + CompileAndVerify(""" + using System; + using System.Collections.Generic; + + var v = nameof(List[]>); + Console.WriteLine(v); + """, expectedOutput: "List").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_NoPartialOpenTypes_1() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(Dictionary<,int>); + Console.WriteLine(v); + """).VerifyDiagnostics( + // (4,27): error CS1031: Type expected + // var v = nameof(Dictionary<,int>); + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(4, 27)); + } + + [Fact] + public void OpenTypeInNameof_NoPartialOpenTypes_2() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(Dictionary); + Console.WriteLine(v); + """).VerifyDiagnostics( + // (4,31): error CS1031: Type expected + // var v = nameof(Dictionary); + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(4, 31)); + } + + [Fact] + public void OpenTypeInNameof_MemberAccessThatDoesNotUseTypeArgument() + { + CompileAndVerify(""" + using System; + using System.Collections.Generic; + + var v = nameof(List<>.Count); + Console.WriteLine(v); + """, expectedOutput: "Count").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument() + { + CompileAndVerify(""" + using System; + + var v = nameof(IGoo<>.Count); + Console.WriteLine(v); + + interface IGoo + { + T Count { get; } + } + """, expectedOutput: "Count").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument_ReferenceObjectMember() + { + CompileAndVerify(""" + using System; + + var v = nameof(IGoo<>.Count.ToString); + Console.WriteLine(v); + + interface IGoo + { + T Count { get; } + } + """, expectedOutput: "ToString").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument_ReferenceConstraintMember_Interface() + { + CompileAndVerify(""" + using System; + + var v = nameof(IGoo<>.X.CompareTo); + Console.WriteLine(v); + + interface IGoo where T : IComparable + { + T X { get; } + } + """, expectedOutput: "CompareTo").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument_ReferenceConstraintMember_ThroughTypeParameter() + { + CompileAndVerify(""" + using System; + + var v = nameof(IGoo<,>.X.CompareTo); + Console.WriteLine(v); + + interface IGoo where T : U where U : IComparable + { + T X { get; } + } + """, expectedOutput: "CompareTo").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument_ReferenceConstraintMember_Class() + { + CompileAndVerify(""" + using System; + + var v = nameof(IGoo<>.X.Z); + Console.WriteLine(v); + + class Base + { + public int Z { get; } + } + + interface IGoo where T : Base + { + T X { get; } + } + """, expectedOutput: "Z").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_GenericMethod1() + { + CompileAndVerify(""" + using System; + + var v = nameof(IGoo.M); + Console.WriteLine(v); + + interface IGoo + { + void M(); + } + """, expectedOutput: "M").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_GenericMethod2() + { + CreateCompilation(""" + using System; + + var v = nameof(IGoo.M<>); + Console.WriteLine(v); + + interface IGoo + { + void M(); + } + """).VerifyDiagnostics( + // (3,16): error CS0305: Using the generic method group 'M' requires 1 type arguments + // var v = nameof(IGoo.M<>); + Diagnostic(ErrorCode.ERR_BadArity, "IGoo.M<>").WithArguments("M", "method group", "1").WithLocation(3, 16)); + } + + [Fact] + public void OpenTypeInNameof_GenericMethod3() + { + CreateCompilation(""" + using System; + + var v = nameof(IGoo.M); + Console.WriteLine(v); + + interface IGoo + { + void M(); + } + """).VerifyDiagnostics( + // (3,16): error CS8084: Type parameters are not allowed on a method group as an argument to 'nameof'. + // var v = nameof(IGoo.M); + Diagnostic(ErrorCode.ERR_NameofMethodGroupWithTypeParameters, "IGoo.M").WithLocation(3, 16)); + } + + [Fact] + public void NameofFunctionPointer1() + { + CreateCompilation(""" + class C + { + unsafe void M() + { + var v = nameof(delegate*); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (5,32): error CS1514: { expected + // var v = nameof(delegate*); + Diagnostic(ErrorCode.ERR_LbraceExpected, "*").WithLocation(5, 32), + // (5,32): warning CS8848: Operator '*' cannot be used here due to precedence. Use parentheses to disambiguate. + // var v = nameof(delegate*); + Diagnostic(ErrorCode.WRN_PrecedenceInversion, "*").WithArguments("*").WithLocation(5, 32), + // (5,33): error CS1525: Invalid expression term '<' + // var v = nameof(delegate*); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "<").WithArguments("<").WithLocation(5, 33), + // (5,34): error CS1525: Invalid expression term 'int' + // var v = nameof(delegate*); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(5, 34), + // (5,38): error CS1525: Invalid expression term ')' + // var v = nameof(delegate*); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(5, 38)); + } + + [Fact] + public void NameofFunctionPointer2() + { + CreateCompilation(""" + using System.Collections.Generic; + + class C + { + unsafe void M() + { + var v = nameof(delegate*>); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (7,32): error CS1514: { expected + // var v = nameof(delegate*>); + Diagnostic(ErrorCode.ERR_LbraceExpected, "*").WithLocation(7, 32), + // (7,32): warning CS8848: Operator '*' cannot be used here due to precedence. Use parentheses to disambiguate. + // var v = nameof(delegate*>); + Diagnostic(ErrorCode.WRN_PrecedenceInversion, "*").WithArguments("*").WithLocation(7, 32), + // (7,33): error CS1525: Invalid expression term '<' + // var v = nameof(delegate*>); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "<").WithArguments("<").WithLocation(7, 33), + // (7,34): error CS0119: 'List' is a type, which is not valid in the given context + // var v = nameof(delegate*>); + Diagnostic(ErrorCode.ERR_BadSKunknown, "List<>").WithArguments("System.Collections.Generic.List", "type").WithLocation(7, 34), + // (7,41): error CS1525: Invalid expression term ')' + // var v = nameof(delegate*>); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(7, 41)); + } + + [Fact] + public void NameofFunctionPointer3() + { + CreateCompilation(""" + using System.Collections.Generic; + + class C + { + unsafe void M() + { + var v = nameof(List>); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (7,13): warning CS0219: The variable 'v' is assigned but its value is never used + // var v = nameof(List>); + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "v").WithArguments("v").WithLocation(7, 13), + // (7,29): error CS0306: The type 'delegate*' may not be used as a type argument + // var v = nameof(List>); + Diagnostic(ErrorCode.ERR_BadTypeArgument, "delegate*").WithArguments("delegate*").WithLocation(7, 29)); + } + + [Fact] + public void NameofFunctionPointer4() + { + CreateCompilation(""" + using System.Collections.Generic; + + class C + { + unsafe void M() + { + var v = nameof(List>>); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (7,13): warning CS0219: The variable 'v' is assigned but its value is never used + // var v = nameof(List>>); + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "v").WithArguments("v").WithLocation(7, 13), + // (7,29): error CS0306: The type 'delegate*>' may not be used as a type argument + // var v = nameof(List>>); + Diagnostic(ErrorCode.ERR_BadTypeArgument, "delegate*>").WithArguments("delegate*>").WithLocation(7, 29), + // (7,39): error CS7003: Unexpected use of an unbound generic name + // var v = nameof(List>>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(7, 39)); + } + + [Fact] + public void NameofFunctionPointer5() + { + CreateCompilation(""" + using System.Collections.Generic; + + class D + { + unsafe void M() + { + var v = nameof(D<, delegate*, List<>>); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (7,26): error CS1031: Type expected + // var v = nameof(D<, delegate*, List<>>); + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(7, 26), + // (7,44): error CS7003: Unexpected use of an unbound generic name + // var v = nameof(D<, delegate*, List<>>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(7, 44)); + } + + [Fact] + public void Nameof_NestedOpenType1() + { + CompileAndVerify(""" + using System; + using System.Collections.Generic; + + var v = nameof(List[]>); + Console.WriteLine(v); + """, expectedOutput: "List").VerifyDiagnostics(); + } + + [Fact] + public void Nameof_NestedOpenType2() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List[]>); + Console.WriteLine(v); + """).VerifyDiagnostics( + // (4,21): error CS7003: Unexpected use of an unbound generic name + // var v = nameof(List[]>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(4, 21)); + } + + [Fact] + public void Nameof_NestedOpenType3() + { + CompileAndVerify(""" + #nullable enable + using System; + using System.Collections.Generic; + + var v = nameof(List?>); + Console.WriteLine(v); + """, expectedOutput: "List").VerifyDiagnostics(); + } + + [Fact] + public void Nameof_NestedOpenType4() + { + CreateCompilation(""" + #nullable enable + using System; + using System.Collections.Generic; + + var v = nameof(List?>); + Console.WriteLine(v); + """).VerifyDiagnostics( + // (5,21): error CS7003: Unexpected use of an unbound generic name + // var v = nameof(List?>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(5, 21)); + } + + [Fact] + public void Nameof_AliasQualifiedName() + { + CompileAndVerify(""" + using System; + + var v = nameof(global::System.Collections.Generic.List<>); + Console.WriteLine(v); + """, expectedOutput: "List").VerifyDiagnostics(); + } + + [Theory] + [InlineData("IGoo<>")] + [InlineData("IGoo<>.Count")] + public void OpenTypeInNameof_SemanticModelTest1(string nameofTypeString) + { + var compilation = CreateCompilation($$""" + using System; + + var v1 = nameof({{nameofTypeString}}); + var v2 = typeof(IGoo<>); + Console.WriteLine(v1 + v2); + + interface IGoo { public T Count { get; } } + """).VerifyDiagnostics(); + var tree = compilation.SyntaxTrees.Single(); + var semanticModel = compilation.GetSemanticModel(tree); + + var root = tree.GetRoot(); + + var firstGeneric = root.DescendantNodes().OfType().First(); + var lastGeneric = root.DescendantNodes().OfType().Last(); + + Assert.NotSame(firstGeneric, lastGeneric); + + // Ensure the type inside the nameof is the same as the type inside the typeof. + var nameofType = semanticModel.GetTypeInfo(firstGeneric).Type; + var typeofType = semanticModel.GetTypeInfo(lastGeneric).Type; + + Assert.NotNull(nameofType); + Assert.NotNull(typeofType); + + // typeof will produce IGoo<>, while nameof will produce IGoo. These are distinctly different types (the + // latter has members for example). + Assert.NotEqual(nameofType, typeofType); + + Assert.True(nameofType.IsDefinition); + Assert.False(nameofType.IsUnboundGenericType()); + + Assert.False(typeofType.IsDefinition); + Assert.True(typeofType.IsUnboundGenericType()); + + Assert.Empty(typeofType.GetMembers("Count")); + Assert.Single(nameofType.GetMembers("Count")); + + var igooType = compilation.GetTypeByMetadataName("IGoo`1").GetPublicSymbol(); + Assert.NotNull(igooType); + + Assert.Equal(igooType, nameofType); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/TypeOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/TypeOfTests.cs index 61ee254c26203..fbb75ecdb1d65 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/TypeOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/TypeOfTests.cs @@ -37,7 +37,91 @@ public C(int i) Assert.Equal("C..ctor(System.Int32 i)", symbolInfo.Symbol.ToTestDisplayString()); var typeInfo = model.GetTypeInfo(node); Assert.Equal("C", typeInfo.Type.ToTestDisplayString()); + } + + [Fact] + public void TypeofPointer() + { + CreateCompilation(""" + class C + { + unsafe void M() + { + var v = typeof(int*); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void TypeofFunctionPointer1() + { + CreateCompilation(""" + class C + { + unsafe void M() + { + var v = typeof(delegate*); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void TypeofFunctionPointer2() + { + CreateCompilation(""" + using System.Collections.Generic; + + class C + { + unsafe void M() + { + var v = typeof(delegate*,int>); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void TypeofFunctionPointer3() + { + CreateCompilation(""" + using System.Collections.Generic; + + class C + { + unsafe void M() + { + var v = typeof(delegate*,int>); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (7,34): error CS7003: Unexpected use of an unbound generic name + // var v = typeof(delegate*,int>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(7, 34)); + } + + [Fact] + public void TypeofFunctionPointer4() + { + CreateCompilation(""" + using System.Collections.Generic; + class D + { + unsafe void M() + { + var v = typeof(D<, delegate*, List<>>); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (7,26): error CS1031: Type expected + // var v = typeof(D<, delegate*, List<>>); + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(7, 26), + // (7,44): error CS7003: Unexpected use of an unbound generic name + // var v = typeof(D<, delegate*, List<>>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(7, 44)); } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/TypeArgumentListParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/TypeArgumentListParsingTests.cs index 9a47468d3af2b..4a14eda2eeba3 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/TypeArgumentListParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/TypeArgumentListParsingTests.cs @@ -2,19 +2,17 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.CSharp.UnitTests { - public class TypeArgumentListParsingTests : ParsingTests + public sealed class TypeArgumentListParsingTests : ParsingTests { public TypeArgumentListParsingTests(ITestOutputHelper output) : base(output) { } - protected override SyntaxTree ParseTree(string text, CSharpParseOptions options) + protected override SyntaxTree ParseTree(string text, CSharpParseOptions? options) { return SyntaxFactory.ParseSyntaxTree(text, options: options); } @@ -2826,5 +2824,877 @@ void M() } EOF(); } + + [Fact] + public void TestGenericWithExtraCommasAndMissingTypes1() + { + UsingTree(""" + class C + { + void M() + { + var added = Goo.Instance; + } + } + """, + // (5,32): error CS1031: Type expected + // var added = Goo.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 32), + // (5,33): error CS1031: Type expected + // var added = Goo.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(5, 33)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "added"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Goo"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Instance"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void TestGenericWithExtraCommasAndMissingTypes2() + { + UsingTree(""" + class C + { + void M() + { + var added = Goo.Instance; + } + } + """, + // (5,28): error CS1031: Type expected + // var added = Goo.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 28), + // (5,29): error CS1031: Type expected + // var added = Goo.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(5, 29)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "added"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Goo"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Id"); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Instance"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void TestGenericWithExtraCommasAndMissingTypes3() + { + UsingTree(""" + class C + { + void M() + { + var added = Goo<,Id,>.Instance; + } + } + """, + // (5,25): error CS1031: Type expected + // var added = Goo<,Id,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 25), + // (5,29): error CS1031: Type expected + // var added = Goo<,Id,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(5, 29)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "added"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Goo"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Id"); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Instance"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void TestGenericWithExtraCommasAndMissingTypes4() + { + UsingTree(""" + class C + { + void M() + { + var added = Goo<,,Id>.Instance; + } + } + """, + // (5,25): error CS1031: Type expected + // var added = Goo<,,Id>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 25), + // (5,26): error CS1031: Type expected + // var added = Goo<,,Id>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 26)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "added"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Goo"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Id"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Instance"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void TestGenericWithExtraCommasAndMissingTypes5() + { + UsingTree(""" + class C + { + void M() + { + var added = Goo.Instance; + } + } + """, + // (5,30): error CS1031: Type expected + // var added = Goo.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 30), + // (5,31): error CS1031: Type expected + // var added = Goo.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(5, 31)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "added"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Goo"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Id"); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Instance"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void TestGenericWithExtraCommasAndMissingTypes6() + { + UsingTree(""" + class C + { + void M() + { + var added = Goo<(int i, int j),,>.Instance; + } + } + """, + // (5,40): error CS1031: Type expected + // var added = Goo<(int i, int j),,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 40), + // (5,41): error CS1031: Type expected + // var added = Goo<(int i, int j),,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(5, 41)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "added"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Goo"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "i"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "j"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Instance"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void TestGenericWithExtraCommasAndMissingTypes7() + { + UsingTree(""" + class C + { + void M() + { + var added = Goo,,>.Instance; + } + } + """, + // (5,32): error CS1031: Type expected + // var added = Goo,,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 32), + // (5,33): error CS1031: Type expected + // var added = Goo,,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(5, 33)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "added"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Goo"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "K"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Instance"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void TestGenericWithExtraCommasAndMissingTypes8() + { + UsingTree(""" + class C + { + void M() + { + var added = Goo,,>.Instance; + } + } + """, + // (5,31): error CS1031: Type expected + // var added = Goo,,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 31), + // (5,32): error CS1031: Type expected + // var added = Goo,,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(5, 32), + // (5,34): error CS1031: Type expected + // var added = Goo,,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 34), + // (5,35): error CS1031: Type expected + // var added = Goo,,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(5, 35)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "added"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Goo"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "K"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Instance"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } } } From d432a234cb43910e849f3551bab57a7960cb62be Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 13:00:31 -0800 Subject: [PATCH 438/508] update test --- .../CSharpTest/IntroduceVariable/IntroduceVariableTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs b/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs index dd30dd77f6304..abe706c762c20 100644 --- a/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs +++ b/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs @@ -2624,7 +2624,7 @@ static void Main() { byte z = 0; Func {|Rename:p|} = x => 0; - Goo(p, y => (byte)0, z, z); + Goo(p, y => 0, z, z); } static void Goo(Func p, Func q, T r, S s) { Console.WriteLine(1); } From 5e9b7ed19a5cc47bf5952745256b5c2c8d2c91c5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 13:32:58 -0800 Subject: [PATCH 439/508] Rename --- .../CSharp/Analyzers/CSharpAnalyzersResources.resx | 4 ++-- .../UseUnboundTypeInNameOfDiagnosticAnalyzer.cs | 2 +- .../CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf | 6 +++--- .../CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf | 6 +++--- .../CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf | 6 +++--- .../CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf | 6 +++--- .../CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf | 6 +++--- .../CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf | 6 +++--- .../CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf | 6 +++--- .../CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf | 6 +++--- .../CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf | 6 +++--- .../CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf | 6 +++--- .../CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf | 6 +++--- .../Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf | 6 +++--- .../Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf | 6 +++--- .../CSharpUseUnboundTypeInNameOfCodeFixProvider.cs | 2 +- src/Features/RulesMissingDocumentation.md | 1 + 17 files changed, 44 insertions(+), 43 deletions(-) diff --git a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx index 80bef57ff4fc5..1574a4e49a86a 100644 --- a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx +++ b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx @@ -422,7 +422,7 @@ Use 'System.Threading.Lock' {Locked="System.Threading.Lock"} "System.Threading.Lock" is the name of a .Net type and should not be localized. - - Use unbound type + + Use unbound generic type \ No newline at end of file diff --git a/src/Analyzers/CSharp/Analyzers/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfDiagnosticAnalyzer.cs index a49846a7d4429..25c93e54a9740 100644 --- a/src/Analyzers/CSharp/Analyzers/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfDiagnosticAnalyzer.cs @@ -34,7 +34,7 @@ internal sealed class CSharpUseUnboundTypeInNameOfDiagnosticAnalyzer() EnforceOnBuildValues.UseUnboundTypeInNameOf, CSharpCodeStyleOptions.PreferUnboundTypeInNameOf, new LocalizableResourceString( - nameof(CSharpAnalyzersResources.Use_unbound_type), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources))) + nameof(CSharpAnalyzersResources.Use_unbound_generic_type), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources))) { public override DiagnosticAnalyzerCategory GetAnalyzerCategory() => DiagnosticAnalyzerCategory.SemanticSpanAnalysis; diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf index 4120b434e04c8..fd92a148a5131 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf @@ -422,9 +422,9 @@ Prohození hodnot pomocí řazené kolekce členů - - Use unbound type - Use unbound type + + Use unbound generic type + Use unbound generic type diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf index 1ecca56c733c9..62aefa1dfa65f 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf @@ -422,9 +422,9 @@ Tupel zum Tauschen von Werten verwenden - - Use unbound type - Use unbound type + + Use unbound generic type + Use unbound generic type diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf index 7a3316a683e24..1c664a907efdc 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf @@ -422,9 +422,9 @@ Utilizar tupla para intercambiar valores - - Use unbound type - Use unbound type + + Use unbound generic type + Use unbound generic type diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf index 554357ff6103a..3104c437e6a0d 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf @@ -422,9 +422,9 @@ Utilisez le tuple pour échanger des valeurs - - Use unbound type - Use unbound type + + Use unbound generic type + Use unbound generic type diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf index 9f5e02b744ead..c140510bbdf48 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf @@ -422,9 +422,9 @@ Usa la tupla per scambiare i valori - - Use unbound type - Use unbound type + + Use unbound generic type + Use unbound generic type diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf index f8e4896ed13ef..4b66e8433b16d 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf @@ -422,9 +422,9 @@ タプルを使用して値をスワップする - - Use unbound type - Use unbound type + + Use unbound generic type + Use unbound generic type diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf index 18a8a2428729d..b31a57f0641d0 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf @@ -422,9 +422,9 @@ 튜플을 사용하여 값 바꾸기 - - Use unbound type - Use unbound type + + Use unbound generic type + Use unbound generic type diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf index 2ba611f598b83..72bec10276155 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf @@ -422,9 +422,9 @@ Użyj krotki do zamiany wartości - - Use unbound type - Use unbound type + + Use unbound generic type + Use unbound generic type diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf index b900d31459b5d..0b0f9af906709 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf @@ -422,9 +422,9 @@ Usar a tupla para trocar valores - - Use unbound type - Use unbound type + + Use unbound generic type + Use unbound generic type diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf index fb664006d0c91..2498b23b3f3be 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf @@ -422,9 +422,9 @@ Использовать кортеж для переключения значений - - Use unbound type - Use unbound type + + Use unbound generic type + Use unbound generic type diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf index 00a5b3754369e..973bf1a1f2495 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf @@ -422,9 +422,9 @@ Değerleri değiştirmek için demeti kullanın - - Use unbound type - Use unbound type + + Use unbound generic type + Use unbound generic type diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf index aae6920af745f..e3251991e3d76 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf @@ -422,9 +422,9 @@ 使用元组交换值 - - Use unbound type - Use unbound type + + Use unbound generic type + Use unbound generic type diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf index 12ee577953fcf..81083e318ef16 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf @@ -422,9 +422,9 @@ 使用元組交換值 - - Use unbound type - Use unbound type + + Use unbound generic type + Use unbound generic type diff --git a/src/Analyzers/CSharp/CodeFixes/UseUnboundTypeInNameOf/CSharpUseUnboundTypeInNameOfCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseUnboundTypeInNameOf/CSharpUseUnboundTypeInNameOfCodeFixProvider.cs index 955fe33c458a2..dfe5b2a10a4d3 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseUnboundTypeInNameOf/CSharpUseUnboundTypeInNameOfCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseUnboundTypeInNameOf/CSharpUseUnboundTypeInNameOfCodeFixProvider.cs @@ -32,7 +32,7 @@ internal sealed partial class CSharpUseUnboundTypeInNameOfCodeFixProvider() : Sy public override Task RegisterCodeFixesAsync(CodeFixContext context) { - RegisterCodeFix(context, CSharpAnalyzersResources.Use_unbound_type, nameof(CSharpAnalyzersResources.Use_unbound_type)); + RegisterCodeFix(context, CSharpAnalyzersResources.Use_unbound_generic_type, nameof(CSharpAnalyzersResources.Use_unbound_generic_type)); return Task.CompletedTask; } diff --git a/src/Features/RulesMissingDocumentation.md b/src/Features/RulesMissingDocumentation.md index 68c85896685b2..0e66ef5c49bf1 100644 --- a/src/Features/RulesMissingDocumentation.md +++ b/src/Features/RulesMissingDocumentation.md @@ -14,6 +14,7 @@ IDE0305 | | Simplify collection initialization | IDE0320 | | Make anonymous function static | IDE0330 | | Use 'System.Threading.Lock' | +IDE0340 | | Use unbound generic type | IDE1007 | | | IDE2000 | | Avoid multiple blank lines | IDE2001 | | Embedded statements must be on their own line | From bf1b9db0ea482b3c69580fd0e7fe782396bae5e6 Mon Sep 17 00:00:00 2001 From: Arun Chander Date: Tue, 19 Nov 2024 13:59:11 -0800 Subject: [PATCH 440/508] Postpone the code-style analysis config rule until NET10 Until we can connect the dots better for the user that has analysislevel set to latest-all (which currently is a lot of users), that this is the exact action and result that caused these errors in the IDE and cmdline, lets postpone enabling this. --- src/CodeStyle/Tools/Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CodeStyle/Tools/Program.cs b/src/CodeStyle/Tools/Program.cs index adbc52aa691ee..37d5417a4dce7 100644 --- a/src/CodeStyle/Tools/Program.cs +++ b/src/CodeStyle/Tools/Program.cs @@ -258,9 +258,9 @@ and an implied numerical option (such as '4') --> <_GlobalAnalyzerConfigFile_MicrosoftCodeAnalysis{language}CodeStyle Condition="'$(_GlobalAnalyzerConfigFileName_MicrosoftCodeAnalysis{language}CodeStyle)' != ''">$(_GlobalAnalyzerConfigDir_MicrosoftCodeAnalysis{language}CodeStyle)\$(_GlobalAnalyzerConfigFileName_MicrosoftCodeAnalysis{language}CodeStyle) - + + ('$(AnalysisLevelStyle)' != '$(AnalysisLevel)' or '$(AnalysisModeStyle)' != '$(AnalysisMode)' or ('$(EffectiveAnalysisLevelStyle)' != '' and $([MSBuild]::VersionGreaterThanOrEquals('$(EffectiveAnalysisLevelStyle)', '10.0'))))"> From 5197c8e8f862cf3ba23fdce56e4d42fbc0bc1afe Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 14:06:24 -0800 Subject: [PATCH 441/508] inherit refactoring state --- .../CodeActions/CSharpCodeRefactoringVerifier`1+Test.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Features/DiagnosticsTestUtilities/CodeActions/CSharpCodeRefactoringVerifier`1+Test.cs b/src/Features/DiagnosticsTestUtilities/CodeActions/CSharpCodeRefactoringVerifier`1+Test.cs index 06dd59037896e..2585055daa8dc 100644 --- a/src/Features/DiagnosticsTestUtilities/CodeActions/CSharpCodeRefactoringVerifier`1+Test.cs +++ b/src/Features/DiagnosticsTestUtilities/CodeActions/CSharpCodeRefactoringVerifier`1+Test.cs @@ -51,6 +51,7 @@ static Test() public Test() { _sharedState = new SharedVerifierState(this, DefaultFileExt); + this.FixedState.InheritanceMode = StateInheritanceMode.AutoInherit; } /// From 9d5d7d7bba8e65537bef16f8ec0a56cc2021b003 Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Tue, 19 Nov 2024 14:55:53 -0800 Subject: [PATCH 442/508] Allow AnalysisModeStyle to override AnalysisLevelSuffixStyle --- src/CodeStyle/Tools/Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CodeStyle/Tools/Program.cs b/src/CodeStyle/Tools/Program.cs index 37d5417a4dce7..5468a0705ee99 100644 --- a/src/CodeStyle/Tools/Program.cs +++ b/src/CodeStyle/Tools/Program.cs @@ -245,8 +245,8 @@ and an implied numerical option (such as '4') --> '$(AnalysisLevelStyle)' != ''">$(AnalysisLevelStyle) - <_GlobalAnalyzerConfigAnalysisMode_MicrosoftCodeAnalysis{language}CodeStyle>$(AnalysisLevelSuffixStyle) - <_GlobalAnalyzerConfigAnalysisMode_MicrosoftCodeAnalysis{language}CodeStyle Condition="'$(_GlobalAnalyzerConfigAnalysisMode_MicrosoftCodeAnalysis{language}CodeStyle)' == ''">$(AnalysisModeStyle) + <_GlobalAnalyzerConfigAnalysisMode_MicrosoftCodeAnalysis{language}CodeStyle>$(AnalysisModeStyle) + <_GlobalAnalyzerConfigAnalysisMode_MicrosoftCodeAnalysis{language}CodeStyle Condition="'$(_GlobalAnalyzerConfigAnalysisMode_MicrosoftCodeAnalysis{language}CodeStyle)' == ''">$(AnalysisLevelSuffixStyle) <_GlobalAnalyzerConfigAnalysisMode_MicrosoftCodeAnalysis{language}CodeStyle Condition="'$(_GlobalAnalyzerConfigAnalysisMode_MicrosoftCodeAnalysis{language}CodeStyle)' == 'AllEnabledByDefault'">{nameof(AnalysisMode.All)} <_GlobalAnalyzerConfigAnalysisMode_MicrosoftCodeAnalysis{language}CodeStyle Condition="'$(_GlobalAnalyzerConfigAnalysisMode_MicrosoftCodeAnalysis{language}CodeStyle)' == 'AllDisabledByDefault'">{nameof(AnalysisMode.None)} <_GlobalAnalyzerConfigAnalysisMode_MicrosoftCodeAnalysis{language}CodeStyle Condition="'$(_GlobalAnalyzerConfigAnalysisMode_MicrosoftCodeAnalysis{language}CodeStyle)' == ''">{nameof(AnalysisMode.Default)} From 3643cac435fb88973bd6c41e46f482c9ba19b09c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 16:07:32 -0800 Subject: [PATCH 443/508] Renames --- .../CodeStyle/CSharpAnalyzerOptionsProvider.cs | 2 +- .../UseUnboundTypeInNameOfDiagnosticAnalyzer.cs | 12 ++++++------ .../CSharpUseUnboundTypeInNameOfCodeFixProvider.cs | 8 ++++---- .../UseUnboundTypeInNameOfTests.cs | 14 +++++++------- .../Core/Analyzers/EnforceOnBuildValues.cs | 2 +- src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs | 2 +- .../CodeFixes/PredefinedCodeFixProviderNames.cs | 2 +- src/Compilers/Test/Core/Traits/Traits.cs | 2 +- .../IDEDiagnosticIDConfigurationTests.cs | 4 ++++ .../CSharp/CodeStyle/CSharpCodeStyleOptions.cs | 4 ++-- 10 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs b/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs index 83667b65925c2..a2fc11e8f70c3 100644 --- a/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs +++ b/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs @@ -69,7 +69,7 @@ internal CSharpSimplifierOptions GetSimplifierOptions() public CodeStyleOption2 PreferMethodGroupConversion => GetOption(CSharpCodeStyleOptions.PreferMethodGroupConversion); public CodeStyleOption2 PreferPrimaryConstructors => GetOption(CSharpCodeStyleOptions.PreferPrimaryConstructors); public CodeStyleOption2 PreferSystemThreadingLock => GetOption(CSharpCodeStyleOptions.PreferSystemThreadingLock); - public CodeStyleOption2 PreferUnboundTypeInNameOf => GetOption(CSharpCodeStyleOptions.PreferUnboundTypeInNameOf); + public CodeStyleOption2 PreferUnboundGenericTypeInNameOf => GetOption(CSharpCodeStyleOptions.PreferUnboundGenericTypeInNameOf); // CodeGenerationOptions diff --git a/src/Analyzers/CSharp/Analyzers/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfDiagnosticAnalyzer.cs index 25c93e54a9740..09891f4af4519 100644 --- a/src/Analyzers/CSharp/Analyzers/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfDiagnosticAnalyzer.cs @@ -11,7 +11,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Text; -namespace Microsoft.CodeAnalysis.CSharp.UseUnboundTypeInNameOf; +namespace Microsoft.CodeAnalysis.CSharp.UseUnboundGenericTypeInNameOf; /// /// Looks for code of the form: @@ -28,11 +28,11 @@ namespace Microsoft.CodeAnalysis.CSharp.UseUnboundTypeInNameOf; /// /// [DiagnosticAnalyzer(LanguageNames.CSharp)] -internal sealed class CSharpUseUnboundTypeInNameOfDiagnosticAnalyzer() +internal sealed class CSharpUseUnboundGenericTypeInNameOfDiagnosticAnalyzer() : AbstractBuiltInCodeStyleDiagnosticAnalyzer( - IDEDiagnosticIds.UseUnboundTypeInNameOfDiagnosticId, - EnforceOnBuildValues.UseUnboundTypeInNameOf, - CSharpCodeStyleOptions.PreferUnboundTypeInNameOf, + IDEDiagnosticIds.UseUnboundGenericTypeInNameOfDiagnosticId, + EnforceOnBuildValues.UseUnboundGenericTypeInNameOf, + CSharpCodeStyleOptions.PreferUnboundGenericTypeInNameOf, new LocalizableResourceString( nameof(CSharpAnalyzersResources.Use_unbound_generic_type), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources))) { @@ -57,7 +57,7 @@ protected override void InitializeWorker(AnalysisContext context) private void AnalyzeInvocationExpression(SyntaxNodeAnalysisContext syntaxContext) { var cancellationToken = syntaxContext.CancellationToken; - var styleOption = syntaxContext.GetCSharpAnalyzerOptions().PreferUnboundTypeInNameOf; + var styleOption = syntaxContext.GetCSharpAnalyzerOptions().PreferUnboundGenericTypeInNameOf; if (!styleOption.Value || ShouldSkipAnalysis(syntaxContext, styleOption.Notification)) return; diff --git a/src/Analyzers/CSharp/CodeFixes/UseUnboundTypeInNameOf/CSharpUseUnboundTypeInNameOfCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseUnboundTypeInNameOf/CSharpUseUnboundTypeInNameOfCodeFixProvider.cs index dfe5b2a10a4d3..657f4593e77d7 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseUnboundTypeInNameOf/CSharpUseUnboundTypeInNameOfCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseUnboundTypeInNameOf/CSharpUseUnboundTypeInNameOfCodeFixProvider.cs @@ -16,19 +16,19 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Extensions; -namespace Microsoft.CodeAnalysis.CSharp.UseUnboundTypeInNameOf; +namespace Microsoft.CodeAnalysis.CSharp.UseUnboundGenericTypeInNameOf; using static SyntaxFactory; -[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.UseUnboundTypeInNameOf), Shared] +[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.UseUnboundGenericTypeInNameOf), Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal sealed partial class CSharpUseUnboundTypeInNameOfCodeFixProvider() : SyntaxEditorBasedCodeFixProvider +internal sealed partial class CSharpUseUnboundGenericTypeInNameOfCodeFixProvider() : SyntaxEditorBasedCodeFixProvider { private static readonly SyntaxNodeOrToken s_omittedArgument = (SyntaxNodeOrToken)OmittedTypeArgument(); public override ImmutableArray FixableDiagnosticIds { get; } - = [IDEDiagnosticIds.UseUnboundTypeInNameOfDiagnosticId]; + = [IDEDiagnosticIds.UseUnboundGenericTypeInNameOfDiagnosticId]; public override Task RegisterCodeFixesAsync(CodeFixContext context) { diff --git a/src/Analyzers/CSharp/Tests/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfTests.cs b/src/Analyzers/CSharp/Tests/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfTests.cs index fdc1df5db9dee..a7ce55c97dc66 100644 --- a/src/Analyzers/CSharp/Tests/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfTests.cs +++ b/src/Analyzers/CSharp/Tests/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfTests.cs @@ -7,21 +7,21 @@ using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.CSharp.UseTupleSwap; -using Microsoft.CodeAnalysis.CSharp.UseUnboundTypeInNameOf; +using Microsoft.CodeAnalysis.CSharp.UseUnboundGenericTypeInNameOf; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Testing; using Roslyn.Test.Utilities; using Xunit; -namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseUnboundTypeInNameOf; +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseUnboundGenericTypeInNameOf; using VerifyCS = CSharpCodeFixVerifier< - CSharpUseUnboundTypeInNameOfDiagnosticAnalyzer, - CSharpUseUnboundTypeInNameOfCodeFixProvider>; + CSharpUseUnboundGenericTypeInNameOfDiagnosticAnalyzer, + CSharpUseUnboundGenericTypeInNameOfCodeFixProvider>; -[Trait(Traits.Feature, Traits.Features.CodeActionsUseUnboundTypeInNameOf)] -public sealed class UseUnboundTypeInNameOfTests +[Trait(Traits.Feature, Traits.Features.CodeActionsUseUnboundGenericTypeInNameOf)] +public sealed class UseUnboundGenericTypeInNameOfTests { [Fact] public async Task TestBaseCase() @@ -112,7 +112,7 @@ void M(string[] args) """, Options = { - { CSharpCodeStyleOptions.PreferUnboundTypeInNameOf, false, CodeStyle.NotificationOption2.Silent } + { CSharpCodeStyleOptions.PreferUnboundGenericTypeInNameOf, false, CodeStyle.NotificationOption2.Silent } }, LanguageVersion = LanguageVersionExtensions.CSharpNext, }.RunAsync(); diff --git a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs index 0da7ce88a6b53..832acfb42498e 100644 --- a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs +++ b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs @@ -99,7 +99,7 @@ internal static class EnforceOnBuildValues public const EnforceOnBuild UseCollectionExpressionForNew = /*IDE0306*/ EnforceOnBuild.Recommended; public const EnforceOnBuild MakeAnonymousFunctionStatic = /*IDE0320*/ EnforceOnBuild.Recommended; public const EnforceOnBuild UseSystemThreadingLock = /*IDE0330*/ EnforceOnBuild.Recommended; - public const EnforceOnBuild UseUnboundTypeInNameOf = /*IDE0340*/ EnforceOnBuild.Recommended; + public const EnforceOnBuild UseUnboundGenericTypeInNameOf = /*IDE0340*/ EnforceOnBuild.Recommended; /* EnforceOnBuild.WhenExplicitlyEnabled */ public const EnforceOnBuild RemoveUnnecessaryCast = /*IDE0004*/ EnforceOnBuild.WhenExplicitlyEnabled; // TODO: Move to 'Recommended' OR 'HighlyRecommended' bucket once performance problems are addressed: https://github.com/dotnet/roslyn/issues/43304 diff --git a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs index 3a9749fbe2faf..e4b9e037db772 100644 --- a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs +++ b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs @@ -204,7 +204,7 @@ internal static class IDEDiagnosticIds public const string UseSystemThreadingLockDiagnosticId = "IDE0330"; - public const string UseUnboundTypeInNameOfDiagnosticId = "IDE0340"; + public const string UseUnboundGenericTypeInNameOfDiagnosticId = "IDE0340"; // Analyzer error Ids public const string AnalyzerChangedId = "IDE1001"; diff --git a/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs b/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs index af2c3512a5167..df67659b2018e 100644 --- a/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs +++ b/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs @@ -178,6 +178,6 @@ internal static class PredefinedCodeFixProviderNames public const string UseSystemThreadingLock = nameof(UseSystemThreadingLock); public const string UseThrowExpression = nameof(UseThrowExpression); public const string UseTupleSwap = nameof(UseTupleSwap); - public const string UseUnboundTypeInNameOf = nameof(UseUnboundTypeInNameOf); + public const string UseUnboundGenericTypeInNameOf = nameof(UseUnboundGenericTypeInNameOf); public const string UseUtf8StringLiteral = nameof(UseUtf8StringLiteral); } diff --git a/src/Compilers/Test/Core/Traits/Traits.cs b/src/Compilers/Test/Core/Traits/Traits.cs index 88fee8711d2bf..cced8ae0268cd 100644 --- a/src/Compilers/Test/Core/Traits/Traits.cs +++ b/src/Compilers/Test/Core/Traits/Traits.cs @@ -219,7 +219,7 @@ public static class Features public const string CodeActionsUseSystemThreadingLock = "CodeActions.UseSystemThreadingLock"; public const string CodeActionsUseThrowExpression = "CodeActions.UseThrowExpression"; public const string CodeActionsUseTupleSwap = "CodeActions.UseTupleSwap"; - public const string CodeActionsUseUnboundTypeInNameOf = "CodeActions.UseUnboundTypeInNameOf"; + public const string CodeActionsUseUnboundGenericTypeInNameOf = "CodeActions.UseUnboundGenericTypeInNameOf"; public const string CodeActionsUseUtf8StringLiteral = "CodeActions.CodeActionsUseUtf8StringLiteral"; public const string CodeActionsWrapping = "CodeActions.Wrapping"; public const string CodeCleanup = nameof(CodeCleanup); diff --git a/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs b/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs index 04c391b5490ac..35ec9ef54e094 100644 --- a/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs @@ -487,6 +487,9 @@ public void CSharp_VerifyIDEDiagnosticSeveritiesAreConfigurable() # IDE0330 dotnet_diagnostic.IDE0330.severity = %value% + + # IDE0330 + dotnet_diagnostic.IDE0340.severity = %value% # IDE1005 dotnet_diagnostic.IDE1005.severity = %value% @@ -904,6 +907,7 @@ public void CSharp_VerifyIDECodeStyleOptionsAreConfigurable() ("IDE0306", "dotnet_style_prefer_collection_expression", "when_types_loosely_match"), ("IDE0320", "csharp_prefer_static_anonymous_function", "true"), ("IDE0330", "csharp_prefer_system_threading_lock", "true"), + ("IDE0340", "csharp_prefer_unbound_generic_type_in_nameof", "true"), ("IDE1005", "csharp_style_conditional_delegate_call", "true"), ("IDE1006", null, null), ("IDE1007", null, null), diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs index ff49ffeafa15e..596029387e652 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs @@ -88,8 +88,8 @@ private static Option2> CreateOption( CodeStyleOptionGroups.ExpressionLevelPreferences, "csharp_style_prefer_utf8_string_literals", defaultValue: CodeStyleOption2.TrueWithSuggestionEnforcement); - public static readonly Option2> PreferUnboundTypeInNameOf = CreateOption( - CodeStyleOptionGroups.ExpressionLevelPreferences, "csharp_style_prefer_unbound_type_in_nameof", + public static readonly Option2> PreferUnboundGenericTypeInNameOf = CreateOption( + CodeStyleOptionGroups.ExpressionLevelPreferences, "csharp_style_prefer_unbound_generic_type_in_nameof", defaultValue: CodeStyleOption2.TrueWithSuggestionEnforcement); public static readonly CodeStyleOption2 NeverWithSilentEnforcement = From 7ad7e1123803478e791158c0f351ae97353197ef Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 16:09:38 -0800 Subject: [PATCH 444/508] Renames --- .../Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs b/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs index 35ec9ef54e094..c676221e32194 100644 --- a/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs @@ -488,7 +488,7 @@ public void CSharp_VerifyIDEDiagnosticSeveritiesAreConfigurable() # IDE0330 dotnet_diagnostic.IDE0330.severity = %value% - # IDE0330 + # IDE0340 dotnet_diagnostic.IDE0340.severity = %value% # IDE1005 @@ -907,7 +907,7 @@ public void CSharp_VerifyIDECodeStyleOptionsAreConfigurable() ("IDE0306", "dotnet_style_prefer_collection_expression", "when_types_loosely_match"), ("IDE0320", "csharp_prefer_static_anonymous_function", "true"), ("IDE0330", "csharp_prefer_system_threading_lock", "true"), - ("IDE0340", "csharp_prefer_unbound_generic_type_in_nameof", "true"), + ("IDE0340", "csharp_style_prefer_unbound_generic_type_in_nameof", "true"), ("IDE1005", "csharp_style_conditional_delegate_call", "true"), ("IDE1006", null, null), ("IDE1007", null, null), From f14f7b559b649265069a6c031cac6adf4d4ca73a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 16:11:08 -0800 Subject: [PATCH 445/508] update test --- .../CSharpTest/Formatting/CodeCleanupTests.cs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs b/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs index aebbe79ee16bc..fc5e10a37f257 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs @@ -627,20 +627,14 @@ private void Method() } [Theory] - [InlineData(LanguageNames.CSharp, 51)] - [InlineData(LanguageNames.VisualBasic, 88)] - public void VerifyAllCodeStyleFixersAreSupportedByCodeCleanup(string language, int numberOfUnsupportedDiagnosticIds) + [InlineData(LanguageNames.CSharp)] + [InlineData(LanguageNames.VisualBasic)] + public void VerifyAllCodeStyleFixersAreSupportedByCodeCleanup(string language) { var supportedDiagnostics = GetSupportedDiagnosticIdsForCodeCleanupService(language); // No Duplicates Assert.Equal(supportedDiagnostics, supportedDiagnostics.Distinct()); - - // Exact Number of Unsupported Diagnostic Ids - var ideDiagnosticIds = typeof(IDEDiagnosticIds).GetFields().Select(f => f.GetValue(f) as string).ToArray(); - var unsupportedDiagnosticIds = ideDiagnosticIds.Except(supportedDiagnostics).ToArray(); - - Assert.Equal(numberOfUnsupportedDiagnosticIds, unsupportedDiagnosticIds.Length); } private const string _code = """ From 60f3dcdd7ae1b1a8d6a3b4bcf0d2445a55e74575 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 16:12:17 -0800 Subject: [PATCH 446/508] update test --- src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs index c73758af0f661..96995ad0c8d53 100644 --- a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs +++ b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs @@ -225,6 +225,7 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti {"csharp_style_prefer_switch_expression", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferSwitchExpression")}, {"csharp_style_prefer_top_level_statements", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferTopLevelStatements")}, {"csharp_style_prefer_tuple_swap", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferTupleSwap")}, + {"csharp_style_prefer_unbound_generic_type_in_nameof", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferUnboundGenericTypeInNameOf")}, {"csharp_style_prefer_utf8_string_literals", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferUtf8StringLiterals")}, {"csharp_style_throw_expression", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferThrowExpression")}, {"csharp_style_unused_value_assignment_preference", new RoamingProfileStorage("TextEditor.CSharp.Specific.UnusedValueAssignmentPreference")}, From 189cf9f139884bc4ef2cfe5f64fa7f2c686e5bb1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 16:21:58 -0800 Subject: [PATCH 447/508] Fix --- .../CodeStyle/CSharpCodeStyleSettingsProvider.cs | 1 + .../EditorConfigSettings/DataProvider/DataProviderTests.cs | 2 +- src/VisualStudio/Core/Def/ServicesVSResources.resx | 3 +++ src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf | 5 +++++ src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf | 5 +++++ src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf | 5 +++++ src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf | 5 +++++ src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf | 5 +++++ src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf | 5 +++++ src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf | 5 +++++ src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf | 5 +++++ src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf | 5 +++++ src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf | 5 +++++ src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf | 5 +++++ .../Core/Def/xlf/ServicesVSResources.zh-Hans.xlf | 5 +++++ .../Core/Def/xlf/ServicesVSResources.zh-Hant.xlf | 5 +++++ 16 files changed, 70 insertions(+), 1 deletion(-) diff --git a/src/VisualStudio/CSharp/Impl/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs b/src/VisualStudio/CSharp/Impl/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs index f0e072481b59b..c2bcace02d039 100644 --- a/src/VisualStudio/CSharp/Impl/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs +++ b/src/VisualStudio/CSharp/Impl/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs @@ -114,6 +114,7 @@ private static IEnumerable GetExpressionCodeStyleOptions(Tiere yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferRangeOperator, ServicesVSResources.Prefer_range_operator, options, updater); yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.ImplicitObjectCreationWhenTypeIsApparent, CSharpVSResources.Prefer_implicit_object_creation_when_type_is_apparent, options, updater); yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferTupleSwap, ServicesVSResources.Prefer_tuple_swap, options, updater); + yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferUnboundGenericTypeInNameOf, ServicesVSResources.Prefer_unbound_generic_type_in_nameof, options, updater); yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferUtf8StringLiterals, ServicesVSResources.Prefer_Utf8_string_literals, options, updater); } diff --git a/src/VisualStudio/CSharp/Test/EditorConfigSettings/DataProvider/DataProviderTests.cs b/src/VisualStudio/CSharp/Test/EditorConfigSettings/DataProvider/DataProviderTests.cs index cb15c66efafbc..1b829db2339a3 100644 --- a/src/VisualStudio/CSharp/Test/EditorConfigSettings/DataProvider/DataProviderTests.cs +++ b/src/VisualStudio/CSharp/Test/EditorConfigSettings/DataProvider/DataProviderTests.cs @@ -171,7 +171,7 @@ public void TestGettingCodeStyleSettingsProviderLanguageServiceAsync() var optionsWithUI = CSharpCodeStyleOptions.EditorConfigOptions .Remove(CSharpCodeStyleOptions.PreferredModifierOrder); - AssertEx.SetEqual(optionsWithUI, dataSnapShot.Select(setting => setting.Key.Option)); + AssertEx.SetEqual(optionsWithUI.OrderBy(o => o.Name), dataSnapShot.Select(setting => setting.Key.Option).OrderBy(o => o.Name)); } [Fact] diff --git a/src/VisualStudio/Core/Def/ServicesVSResources.resx b/src/VisualStudio/Core/Def/ServicesVSResources.resx index b0217c12d0415..0ecb04dcae98a 100644 --- a/src/VisualStudio/Core/Def/ServicesVSResources.resx +++ b/src/VisualStudio/Core/Def/ServicesVSResources.resx @@ -1937,6 +1937,9 @@ Additional information: {1} Prefer static anonymous functions + + Prefer unbound generic type in 'nameof' + Show hints for collection expressions diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf index ab6a97642382e..14ec395e6aa25 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf @@ -1137,6 +1137,11 @@ Preferovat prohození řazené kolekce členů + + Prefer unbound generic type in 'nameof' + Prefer unbound generic type in 'nameof' + + Projects Projekty diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf index 4661f9d499406..5e094a022f2aa 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf @@ -1137,6 +1137,11 @@ Tupeltausch bevorzugen + + Prefer unbound generic type in 'nameof' + Prefer unbound generic type in 'nameof' + + Projects Projekte diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf index 050c1396a632b..3fd9228e754aa 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf @@ -1137,6 +1137,11 @@ Preferir el intercambio de tupla + + Prefer unbound generic type in 'nameof' + Prefer unbound generic type in 'nameof' + + Projects Proyectos diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf index 4e747c8f91677..e30845d56ee56 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf @@ -1137,6 +1137,11 @@ Préférer l'échange de tuples + + Prefer unbound generic type in 'nameof' + Prefer unbound generic type in 'nameof' + + Projects Projets diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf index fed1715b3b0e6..3484693536d83 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf @@ -1137,6 +1137,11 @@ Preferisci lo scambio di tuple + + Prefer unbound generic type in 'nameof' + Prefer unbound generic type in 'nameof' + + Projects Progetti diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf index 0a4741c59619b..0858bfbcc0615 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf @@ -1137,6 +1137,11 @@ タプル スワップを優先する + + Prefer unbound generic type in 'nameof' + Prefer unbound generic type in 'nameof' + + Projects プロジェクト diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf index 81d5aa5c4d644..7a3b22ba4c79e 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf @@ -1137,6 +1137,11 @@ 튜플 교환 선호 + + Prefer unbound generic type in 'nameof' + Prefer unbound generic type in 'nameof' + + Projects 프로젝트 diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf index 1c458bf2c9478..4e05e910c707c 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf @@ -1137,6 +1137,11 @@ Preferuj zamianę krotki + + Prefer unbound generic type in 'nameof' + Prefer unbound generic type in 'nameof' + + Projects Projekty diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf index 896e88a2cf757..ae8395e136779 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf @@ -1137,6 +1137,11 @@ Prefiro troca de tupla + + Prefer unbound generic type in 'nameof' + Prefer unbound generic type in 'nameof' + + Projects Projetos diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf index c2a0f7fb1a35a..3f797ccbfc7a7 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf @@ -1137,6 +1137,11 @@ Предпочитать переключение кортежей + + Prefer unbound generic type in 'nameof' + Prefer unbound generic type in 'nameof' + + Projects Проекты diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf index 1562cb143bf76..4199fefd315d5 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf @@ -1137,6 +1137,11 @@ Demet değiştirmeyi tercih et + + Prefer unbound generic type in 'nameof' + Prefer unbound generic type in 'nameof' + + Projects Projeler diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf index 6c5841751c0ec..072fcff0111bd 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf @@ -1137,6 +1137,11 @@ 首选元组交换 + + Prefer unbound generic type in 'nameof' + Prefer unbound generic type in 'nameof' + + Projects 项目 diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf index 600e7685c0a2e..e2928546213c9 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf @@ -1137,6 +1137,11 @@ 偏好元組交換 + + Prefer unbound generic type in 'nameof' + Prefer unbound generic type in 'nameof' + + Projects 專案 From 991a2e388cb8a5973e67cfb34a660ea93abeaacc Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 16:25:56 -0800 Subject: [PATCH 448/508] update test --- .../Core/Test/Options/CSharpEditorConfigGeneratorTests.vb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb b/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb index e3be0cb90efd8..d04445a96d69a 100644 --- a/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb +++ b/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb @@ -158,6 +158,7 @@ csharp_style_prefer_local_over_anonymous_function = true csharp_style_prefer_null_check_over_type_check = true csharp_style_prefer_range_operator = true csharp_style_prefer_tuple_swap = true +csharp_style_prefer_unbound_generic_type_in_nameof = true csharp_style_prefer_utf8_string_literals = true csharp_style_throw_expression = true csharp_style_unused_value_assignment_preference = discard_variable @@ -417,6 +418,7 @@ csharp_style_prefer_local_over_anonymous_function = true csharp_style_prefer_null_check_over_type_check = true csharp_style_prefer_range_operator = true csharp_style_prefer_tuple_swap = true +csharp_style_prefer_unbound_generic_type_in_nameof = true csharp_style_prefer_utf8_string_literals = true csharp_style_throw_expression = true csharp_style_unused_value_assignment_preference = discard_variable From 26f113c6f13db0afaec81f2f0d34763313121098 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 16:28:43 -0800 Subject: [PATCH 449/508] rename files --- src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems | 2 +- .../CSharpUseUnboundGenericTypeInNameOfDiagnosticAnalyzer.cs} | 0 src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems | 2 +- .../CSharpUseUnboundGenericTypeInNameOfCodeFixProvider.cs} | 0 src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems | 2 +- .../UseUnboundGenericTypeInNameOfTests.cs} | 3 --- 6 files changed, 3 insertions(+), 6 deletions(-) rename src/Analyzers/CSharp/Analyzers/{UseUnboundTypeInNameOf/UseUnboundTypeInNameOfDiagnosticAnalyzer.cs => UseUnboundGenericTypeInNameOf/CSharpUseUnboundGenericTypeInNameOfDiagnosticAnalyzer.cs} (100%) rename src/Analyzers/CSharp/CodeFixes/{UseUnboundTypeInNameOf/CSharpUseUnboundTypeInNameOfCodeFixProvider.cs => UseUnboundGenericTypeInNameOf/CSharpUseUnboundGenericTypeInNameOfCodeFixProvider.cs} (100%) rename src/Analyzers/CSharp/Tests/{UseUnboundTypeInNameOf/UseUnboundTypeInNameOfTests.cs => UseUnboundGenericTypeInNameOf/UseUnboundGenericTypeInNameOfTests.cs} (98%) diff --git a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems index 32d0471897a72..ccb4024aa4b05 100644 --- a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems +++ b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems @@ -155,7 +155,7 @@ - + diff --git a/src/Analyzers/CSharp/Analyzers/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseUnboundGenericTypeInNameOf/CSharpUseUnboundGenericTypeInNameOfDiagnosticAnalyzer.cs similarity index 100% rename from src/Analyzers/CSharp/Analyzers/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfDiagnosticAnalyzer.cs rename to src/Analyzers/CSharp/Analyzers/UseUnboundGenericTypeInNameOf/CSharpUseUnboundGenericTypeInNameOfDiagnosticAnalyzer.cs diff --git a/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems b/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems index 0993c5600de6a..aabc8fa59af2f 100644 --- a/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems +++ b/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems @@ -176,7 +176,7 @@ - + diff --git a/src/Analyzers/CSharp/CodeFixes/UseUnboundTypeInNameOf/CSharpUseUnboundTypeInNameOfCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseUnboundGenericTypeInNameOf/CSharpUseUnboundGenericTypeInNameOfCodeFixProvider.cs similarity index 100% rename from src/Analyzers/CSharp/CodeFixes/UseUnboundTypeInNameOf/CSharpUseUnboundTypeInNameOfCodeFixProvider.cs rename to src/Analyzers/CSharp/CodeFixes/UseUnboundGenericTypeInNameOf/CSharpUseUnboundGenericTypeInNameOfCodeFixProvider.cs diff --git a/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems b/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems index e2fbf6cc404a6..5a1b4401b4b04 100644 --- a/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems +++ b/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems @@ -185,7 +185,7 @@ - + diff --git a/src/Analyzers/CSharp/Tests/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfTests.cs b/src/Analyzers/CSharp/Tests/UseUnboundGenericTypeInNameOf/UseUnboundGenericTypeInNameOfTests.cs similarity index 98% rename from src/Analyzers/CSharp/Tests/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfTests.cs rename to src/Analyzers/CSharp/Tests/UseUnboundGenericTypeInNameOf/UseUnboundGenericTypeInNameOfTests.cs index a7ce55c97dc66..4add90eb881f0 100644 --- a/src/Analyzers/CSharp/Tests/UseUnboundTypeInNameOf/UseUnboundTypeInNameOfTests.cs +++ b/src/Analyzers/CSharp/Tests/UseUnboundGenericTypeInNameOf/UseUnboundGenericTypeInNameOfTests.cs @@ -6,12 +6,9 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; -using Microsoft.CodeAnalysis.CSharp.UseTupleSwap; using Microsoft.CodeAnalysis.CSharp.UseUnboundGenericTypeInNameOf; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.CodeAnalysis.Testing; -using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseUnboundGenericTypeInNameOf; From 26bca3cee7bf6454426e74024d7ccc80634c2a01 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 20:18:44 -0800 Subject: [PATCH 450/508] inline --- .../AbstractLanguageServerProtocolTests.cs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs index 20270e5e4abce..ef7eda21bdaa2 100644 --- a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs +++ b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs @@ -104,7 +104,7 @@ public Task> MapSpansAsync(Document document, I private protected class OrderLocations : Comparer { - public override int Compare(LSP.Location? x, LSP.Location? y) => CompareNullableLocations(x, y); + public override int Compare(LSP.Location? x, LSP.Location? y) => CompareLocations(x, y); } protected virtual TestComposition Composition => EditorFeaturesLspComposition; @@ -148,10 +148,9 @@ private protected static void AssertLocationsEqual(IEnumerable exp AssertJsonEquals(orderedExpectedLocations, orderedActualLocations); } - private protected static int CompareNullableLocations(LSP.Location? l1, LSP.Location? l2) + private protected static int CompareLocations(LSP.Location? l1, LSP.Location? l2) { - var equalReferences = ReferenceEquals(l1, l2); - if (equalReferences) + if (ReferenceEquals(l1, l2)) return 0; if (l1 is null) @@ -160,11 +159,6 @@ private protected static int CompareNullableLocations(LSP.Location? l1, LSP.Loca if (l2 is null) return 1; - return CompareLocations(l1, l2); - } - - private protected static int CompareLocations(LSP.Location l1, LSP.Location l2) - { var compareDocument = l1.Uri.AbsoluteUri.CompareTo(l2.Uri.AbsoluteUri); var compareRange = CompareRange(l1.Range, l2.Range); return compareDocument != 0 ? compareDocument : compareRange; From 241b51b1acd169a33afe3c28163bebfffb6dd69a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 20:19:54 -0800 Subject: [PATCH 451/508] simplify --- .../References/FindAllReferencesHandlerTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs b/src/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs index 4634f92454e24..47b09605f0dc1 100644 --- a/src/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs @@ -292,7 +292,6 @@ namespace ComplexNamespace; class PREPROCESSING_SYMBOL { } - """; await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, CapabilitiesWithVSExtensions); From 6fe1e1b8954ee548714c67c34f0c58aaa671c75e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 20:21:45 -0800 Subject: [PATCH 452/508] Update tests --- .../CSharp/Test/F1Help/F1HelpTests.cs | 3953 +++++++++-------- 1 file changed, 2084 insertions(+), 1869 deletions(-) diff --git a/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs b/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs index 20734194f9488..915d724ce7b1b 100644 --- a/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs +++ b/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs @@ -2,8 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -16,2072 +15,2288 @@ using Roslyn.Utilities; using Xunit; -namespace Microsoft.VisualStudio.LanguageServices.CSharp.UnitTests.F1Help -{ - [UseExportProvider] - [Trait(Traits.Feature, Traits.Features.F1Help)] - public class F1HelpTests - { - private static async Task TestAsync(string markup, string expectedText) - { - using var workspace = TestWorkspace.CreateCSharp(markup, composition: VisualStudioTestCompositions.LanguageServices); - var caret = workspace.Documents.First().CursorPosition; - - var service = Assert.IsType(workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetService()); - var actualText = await service.GetHelpTermAsync(workspace.CurrentSolution.Projects.First().Documents.First(), workspace.Documents.First().SelectedSpans.First(), CancellationToken.None); - Assert.Equal(expectedText, actualText); - } - - private static async Task Test_KeywordAsync(string markup, string expectedText) - { - await TestAsync(markup, expectedText + "_CSharpKeyword"); - } - - [Fact] - public async Task TestInternal() - { - await Test_KeywordAsync( -@"intern[||]al class C -{ -}", "internal"); - } - - [Fact] - public async Task TestProtected() - { - await Test_KeywordAsync( -@"public class C -{ - protec[||]ted void goo(); -}", "protected"); - } - - [Fact] - public async Task TestProtectedInternal1() - { - await Test_KeywordAsync( -@"public class C -{ - internal protec[||]ted void goo(); -}", "protectedinternal"); - } - - [Fact] - public async Task TestProtectedInternal2() - { - await Test_KeywordAsync( -@"public class C -{ - protec[||]ted internal void goo(); -}", "protectedinternal"); - } - - [Fact] - public async Task TestPrivateProtected1() - { - await Test_KeywordAsync( -@"public class C -{ - private protec[||]ted void goo(); -}", "privateprotected"); - } - - [Fact] - public async Task TestPrivateProtected2() - { - await Test_KeywordAsync( -@"public class C -{ - priv[||]ate protected void goo(); -}", "privateprotected"); - } - - [Fact] - public async Task TestPrivateProtected3() - { - await Test_KeywordAsync( -@"public class C -{ - protected priv[||]ate void goo(); -}", "privateprotected"); - } - - [Fact] - public async Task TestPrivateProtected4() - { - await Test_KeywordAsync( -@"public class C +namespace Microsoft.VisualStudio.LanguageServices.CSharp.UnitTests.F1Help; + +[UseExportProvider] +[Trait(Traits.Feature, Traits.Features.F1Help)] +public sealed class F1HelpTests { - prot[||]ected private void goo(); -}", "privateprotected"); - } - - [Fact] - public async Task TestModifierSoup() - { - await Test_KeywordAsync( - @"public class C + private static async Task TestAsync([StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, string expectedText) + { + using var workspace = TestWorkspace.CreateCSharp(markup, composition: VisualStudioTestCompositions.LanguageServices); + var caret = workspace.Documents.First().CursorPosition; + + var service = Assert.IsType(workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetService()); + var actualText = await service.GetHelpTermAsync(workspace.CurrentSolution.Projects.First().Documents.First(), workspace.Documents.First().SelectedSpans.First(), CancellationToken.None); + Assert.Equal(expectedText, actualText); + } + + private static async Task Test_KeywordAsync([StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, string expectedText) + { + await TestAsync(markup, expectedText + "_CSharpKeyword"); + } + + [Fact] + public async Task TestInternal() + { + await Test_KeywordAsync( + """ + intern[||]al class C + { + } + """, "internal"); + } + + [Fact] + public async Task TestProtected() + { + await Test_KeywordAsync( + """ + public class C + { + protec[||]ted void goo(); + } + """, "protected"); + } + + [Fact] + public async Task TestProtectedInternal1() + { + await Test_KeywordAsync( + """ + public class C + { + internal protec[||]ted void goo(); + } + """, "protectedinternal"); + } + + [Fact] + public async Task TestProtectedInternal2() + { + await Test_KeywordAsync( + """ + public class C + { + protec[||]ted internal void goo(); + } + """, "protectedinternal"); + } + + [Fact] + public async Task TestPrivateProtected1() + { + await Test_KeywordAsync( + """ + public class C + { + private protec[||]ted void goo(); + } + """, "privateprotected"); + } + + [Fact] + public async Task TestPrivateProtected2() + { + await Test_KeywordAsync( + """ + public class C + { + priv[||]ate protected void goo(); + } + """, "privateprotected"); + } + + [Fact] + public async Task TestPrivateProtected3() + { + await Test_KeywordAsync( + """ + public class C + { + protected priv[||]ate void goo(); + } + """, "privateprotected"); + } + + [Fact] + public async Task TestPrivateProtected4() + { + await Test_KeywordAsync( + """ + public class C + { + prot[||]ected private void goo(); + } + """, "privateprotected"); + } + + [Fact] + public async Task TestModifierSoup() + { + await Test_KeywordAsync( +""" +public class C { private new prot[||]ected static unsafe void foo() { } -}", "privateprotected"); - } +} +""", "privateprotected"); + } + + [Fact] + public async Task TestModifierSoupField() + { + await Test_KeywordAsync( +""" +public class C +{ + new prot[||]ected static unsafe private goo; +} +""", "privateprotected"); + } + + [Fact] + public async Task TestVoid() + { + await Test_KeywordAsync( + """ + class C + { + vo[||]id goo() + { + } + } + """, "void"); + } + + [Fact] + public async Task TestReturn() + { + await Test_KeywordAsync( + """ + class C + { + void goo() + { + ret[||]urn; + } + } + """, "return"); + } + + [Fact] + public async Task TestClassPartialType() + { + await Test_KeywordAsync( + """ + part[||]ial class C + { + partial void goo(); + } + """, "partialtype"); + } + + [Fact] + public async Task TestRecordPartialType() + { + await Test_KeywordAsync( + """ + part[||]ial record C + { + partial void goo(); + } + """, "partialtype"); + } + + [Fact] + public async Task TestRecordWithPrimaryConstructorPartialType() + { + await Test_KeywordAsync( + """ + part[||]ial record C(string S) + { + partial void goo(); + } + """, "partialtype"); + } + + [Fact] + public async Task TestPartialMethodInClass() + { + await Test_KeywordAsync( + """ + partial class C + { + par[||]tial void goo(); + } + """, "partialmethod"); + } + + [Fact] + public async Task TestPartialMethodInRecord() + { + await Test_KeywordAsync( + """ + partial record C + { + par[||]tial void goo(); + } + """, "partialmethod"); + } + + [Fact] + public async Task TestExtendedPartialMethod() + { + await Test_KeywordAsync( + """ + partial class C + { + public par[||]tial void goo(); + } + """, "partialmethod"); + } + + [Fact] + public async Task TestWhereClause() + { + await Test_KeywordAsync( + """ + using System.Linq; + + class Program where T : class + { + void goo(string[] args) + { + var x = from a in args + whe[||]re a.Length > 0 + select a; + } + } + """, "whereclause"); + } + + [Fact] + public async Task TestWhereConstraint() + { + await Test_KeywordAsync( + """ + using System.Linq; + + class Program wh[||]ere T : class + { + void goo(string[] args) + { + var x = from a in args + where a.Length > 0 + select a; + } + } + """, "whereconstraint"); + } + + [Fact] + public async Task TestPreprocessor() + { + await TestAsync( + """ + #regi[||]on + #endregion + """, "#region"); + } + + [Fact] + public async Task TestPreprocessor2() + { + await TestAsync( + """ + #region[||] + #endregion + """, "#region"); + } + + [Fact] + public async Task TestConstructor() + { + await TestAsync( + """ + namespace N + { + class C + { + void goo() + { + var x = new [|C|](); + } + } + } + """, "N.C.#ctor"); + } + + [Fact] + public async Task TestGenericClass() + { + await TestAsync( + """ + namespace N + { + class C + { + void goo() + { + [|C|] c; + } + } + } + """, "N.C`1"); + } + + [Fact] + public async Task TestGenericMethod() + { + await TestAsync( + """ + namespace N + { + class C + { + void goo(T t, U u, V v) + { + C c; + c.g[|oo|](1, 1, 1); + } + } + } + """, "N.C`1.goo``3"); + } + + [Theory] + [InlineData("+")] + [InlineData("-")] + [InlineData("&")] + [InlineData("|")] + [InlineData("/")] + [InlineData("^")] + [InlineData(">")] + [InlineData(">=")] + [InlineData("!=")] + [InlineData("<")] + [InlineData("<=")] + [InlineData("<<")] + [InlineData(">>")] + [InlineData(">>>")] + [InlineData("*")] + [InlineData("%")] + [InlineData("&&")] + [InlineData("||")] + [InlineData("==")] + public async Task TestBinaryOperator(string operatorText) + { + await TestAsync( + $$""" + namespace N + { + class C + { + void goo() + { + var two = 1 [|{{operatorText}}|] 1; + } + } + } + """, $"{operatorText}_CSharpKeyword"); + } + + [Theory] + [InlineData("+=")] + [InlineData("-=")] + [InlineData("/=")] + [InlineData("*=")] + [InlineData("%=")] + [InlineData("&=")] + [InlineData("|=")] + [InlineData("^=")] + [InlineData("<<=")] + [InlineData(">>=")] + [InlineData(">>>=")] + public async Task TestCompoundOperator(string operatorText) + { + await TestAsync( + $$""" + namespace N + { + class C + { + void goo(int x) + { + x [|{{operatorText}}|] x; + } + } + } + """, $"{operatorText}_CSharpKeyword"); + } + + [Theory] + [InlineData("++")] + [InlineData("--")] + [InlineData("!")] + [InlineData("~")] + public async Task TestPrefixOperator(string operatorText) + { + await TestAsync( + $$""" + namespace N + { + class C + { + void goo(int x) + { + x = [|{{operatorText}}|]x; + } + } + } + """, $"{operatorText}_CSharpKeyword"); + } + + [Theory] + [InlineData("++")] + [InlineData("--")] + public async Task TestPostfixOperator(string operatorText) + { + await TestAsync( + $$""" + namespace N + { + class C + { + void goo(int x) + { + x = x[|{{operatorText}}|]; + } + } + } + """, $"{operatorText}_CSharpKeyword"); + } + + [Fact] + public async Task TestRelationalPattern() + { + await TestAsync( + """ + namespace N + { + class C + { + void goo(string x) + { + if (x is { Length: [||]> 5 }) { } + } + } + } + """, ">_CSharpKeyword"); + } + + [Fact] + public async Task TestGreaterThanInFunctionPointer() + { + await TestAsync(""" + unsafe class C + { + delegate*[||] f; + } + """, "functionPointer_CSharpKeyword"); + } + + [Fact] + public async Task TestLessThanInFunctionPointer() + { + await TestAsync(""" + unsafe class C + { + delegate*[||] f; + } + """, "functionPointer_CSharpKeyword"); + } + + [Fact] + public async Task TestEqualsOperatorInParameter() + { + await TestAsync( + """ + namespace N + { + class C + { + void goo(int x [|=|] 0) + { + } + } + } + """, "optionalParameter_CSharpKeyword"); + } + + [Fact] + public async Task TestEqualsOperatorInPropertyInitializer() + { + await TestAsync( + """ + namespace N + { + class C + { + int P { get; } [|=|] 5; + } + } + """, "propertyInitializer_CSharpKeyword"); + } + + [Fact] + public async Task TestVar() + { + await TestAsync( + """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + class Program + { + static void Main(string[] args) + { + var[||] x = 3; + } + } + """, "var_CSharpKeyword"); + } + + [Fact] + public async Task TestEquals() + { + await TestAsync( + """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + class Program + { + static void Main(string[] args) + { + var x =[||] 3; + } + } + """, "=_CSharpKeyword"); + } + + [Fact] + public async Task TestEqualsInEnum() + { + await TestAsync( + """ + enum E + { + A [||]= 1 + } + """, "enum_CSharpKeyword"); + } + + [Fact] + public async Task TestEqualsInAttribute() + { + await TestAsync( + """ + using System; + + [AttributeUsage(AttributeTargets.Class, Inherited [|=|] true)] + class MyAttribute : Attribute + { + } + """, "attributeNamedArgument_CSharpKeyword"); + } + + [Fact] + public async Task TestEqualsInUsingAlias() + { + await TestAsync( + """ + using SC [||]= System.Console; + """, "using_CSharpKeyword"); + } + + [Fact] + public async Task TestEqualsInAnonymousObjectMemberDeclarator() + { + await TestAsync( + """ + class C + { + void M() + { + var x = new { X [||]= 0 }; + } + } + """, "anonymousObject_CSharpKeyword"); + } + + [Fact] + public async Task TestEqualsInDocumentationComment() + { + await TestAsync( + """ + class C + { + /// + /// + /// + void M() + { + var x = new { X [||]= 0 }; + } + } + """, "see"); + } + + [Fact] + public async Task TestEqualsInLet() + { + await TestAsync( + """ + class C + { + void M() + { + var y = + from x1 in x2 + let x3 [||]= x4 + select x5; + } + } + """, "let_CSharpKeyword"); + } + + [Fact] + public async Task TestLetKeyword() + { + await TestAsync( + """ + class C + { + void M() + { + var y = + from x1 in x2 + [||]let x3 = x4 + select x5; + } + } + """, "let_CSharpKeyword"); + } + + [Fact] + public async Task TestFromIn() + { + await TestAsync( + """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + class Program + { + static void Main(string[] args) + { + var x = from n i[||]n { + 1} + + select n + } + } + """, "from_CSharpKeyword"); + } + + [Fact] + public async Task TestProperty() + { + await TestAsync( + """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + class Program + { + static void Main(string[] args) + { + new UriBuilder().Fragm[||]ent; + } + } + """, "System.UriBuilder.Fragment"); + } + + [Fact] + public async Task TestForeachIn() + { + await TestAsync( + """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + class Program + { + static void Main(string[] args) + { + foreach (var x in[||] { + 1} ) + { + } + } + } + """, "in_CSharpKeyword"); + } + + [Fact] + public async Task TestRegionDescription() + { + await TestAsync( + """ + class Program + { + static void Main(string[] args) + { + #region Begin MyR[||]egion for testing + #endregion End + } + } + """, "#region"); + } + + [Fact] + public async Task TestGenericAngle_LessThanToken_TypeArgument() + { + await TestAsync( + """ + class Program + { + static void generic(T t) + { + generic[||](0); + } + } + """, "generics_CSharpKeyword"); + } + + [Fact] + public async Task TestGenericAngle_GreaterThanToken_TypeArgument() + { + await TestAsync( + """ + class Program + { + static void generic(T t) + { + generic|](0); + } + } + """, "generics_CSharpKeyword"); + } + + [Fact] + public async Task TestGenericAngle_LessThanToken_TypeParameter() + { + await TestAsync( + """ + class Program + { + static void generic[|<|]T>(T t) + { + generic(0); + } + } + """, "generics_CSharpKeyword"); + } + + [Fact] + public async Task TestGenericAngle_GreaterThanToken_TypeParameter() + { + await TestAsync( + """ + class Program + { + static void generic|](T t) + { + generic(0); + } + } + """, "generics_CSharpKeyword"); + } + + [Fact] + public async Task TestLocalReferenceIsType() + { + await TestAsync( + """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + class Program + { + static void Main(string[] args) + { + int x; + x[||]; + } + } + """, "System.Int32"); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/864266")] + public async Task TestConstantField() + { + await TestAsync( + """ + class Program + { + static void Main(string[] args) + { + var i = int.Ma[||]xValue; + } + } + """, "System.Int32.MaxValue"); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/862420")] + public async Task TestParameter() + { + await TestAsync( + """ + class Class2 + { + void M1(int par[||]ameter) // 1 + { + } + + void M2() + { + int argument = 1; + M1(parameter: argument); // 2 + } + } + """, "System.Int32"); + } + + [Fact] + public async Task TestRefReadonlyParameter_Ref() + { + await TestAsync( + """ + class C + { + void M(r[||]ef readonly int x) + { + } + } + """, "ref_CSharpKeyword"); + } + + [Fact] + public async Task TestRefReadonlyParameter_ReadOnly() + { + await TestAsync( + """ + class C + { + void M(ref read[||]only int x) + { + } + } + """, "readonly_CSharpKeyword"); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/862420")] + public async Task TestArgumentType() + { + await TestAsync( + """ + class Class2 + { + void M1(int pa[||]rameter) // 1 + { + } + + void M2() + { + int argument = 1; + M1(parameter: argument); // 2 + } + } + """, "System.Int32"); + } + + [Fact] + public async Task TestYieldReturn_OnYield() + { + await TestAsync(""" + using System.Collections.Generic; + + public class C + { + public IEnumerable M() + { + [|yield|] return 0; + } + } + """, "yield_CSharpKeyword"); + } + + [Fact] + public async Task TestYieldReturn_OnReturn() + { + await TestAsync(""" + using System.Collections.Generic; + + public class C + { + public IEnumerable M() + { + yield [|return|] 0; + } + } + """, "yield_CSharpKeyword"); + } + + [Fact] + public async Task TestYieldBreak_OnYield() + { + await TestAsync(""" + using System.Collections.Generic; + + public class C + { + public IEnumerable M() + { + [|yield|] break; + } + } + """, "yield_CSharpKeyword"); + } + + [Fact] + public async Task TestYieldBreak_OnBreak() + { + await TestAsync(""" + using System.Collections.Generic; + + public class C + { + public IEnumerable M() + { + yield [|break|] 0; + } + } + """, "yield_CSharpKeyword"); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/862396")] + public async Task TestNoToken() + { + await TestAsync( + """ + class Program + { + static void Main(string[] args) + { + } + }[||] + """, "vs.texteditor"); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/862328")] + public async Task TestLiteral() + { + await TestAsync( + """ + class Program + { + static void Main(string[] args) + { + Main(new string[] { "fo[||]o" }); + } + } + """, "System.String"); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/862478")] + public async Task TestColonColon() + { + await TestAsync( + """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + class Program + { + static void Main(string[] args) + { + global:[||]:System.Console.Write("); + } + } + """, "::_CSharpKeyword"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46986")] + public async Task TestStringInterpolation() + { + await TestAsync( + """ + using System; + + class Program + { + static void Main(string[] args) + { + Console.WriteLine($[||]"Hello, {args[0]}"); + } + } + """, "$_CSharpKeyword"); + } + + [Fact] + public async Task TestUtf8String() + { + await TestAsync( + """ + using System; + + class Program + { + static void Main(string[] args) + { + var x = "Hel[||]lo"u8; + } + } + """, "Utf8StringLiteral_CSharpKeyword"); + } + + [Fact] + public async Task TestRawString() + { + await TestAsync( + """" + using System; + + class Program + { + static void Main(string[] args) + { + var x = """Hel[||]lo"""; + } + } + """", "RawStringLiteral_CSharpKeyword"); + } + + [Fact] + public async Task TestUtf8RawString() + { + await TestAsync( + """" + using System; + + class Program + { + static void Main(string[] args) + { + var x = """Hel[||]lo"""u8; + } + } + """", "Utf8StringLiteral_CSharpKeyword"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46986")] + public async Task TestVerbatimString() + { + await TestAsync( + """ + using System; + + class Program + { + static void Main(string[] args) + { + Console.WriteLine(@[||]"Hello\"); + } + } + """, "@_CSharpKeyword"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46986")] + public async Task TestVerbatimInterpolatedString1() + { + await TestAsync( + """ + using System; + + class Program + { + static void Main(string[] args) + { + Console.WriteLine(@[||]$"Hello\ {args[0]}"); + } + } + """, "@$_CSharpKeyword"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46986")] + public async Task TestVerbatimInterpolatedString2() + { + await TestAsync( + """ + using System; + + class Program + { + static void Main(string[] args) + { + Console.WriteLine($[||]@"Hello\ {args[0]}"); + } + } + """, "@$_CSharpKeyword"); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/864658")] + public async Task TestNullable() + { + await TestAsync( + """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + class Program + { + static void Main(string[] args) + { + int?[||] a = int.MaxValue; + a.Value.GetHashCode(); + } + } + """, "System.Nullable`1"); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/863517")] + public async Task TestAfterLastToken() + { + await TestAsync( + """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + class Program + { + static void Main(string[] args) + { + foreach (char var in "!!!")$$[||] + { + } + } + } + """, "vs.texteditor"); + } + + [Fact] + public async Task TestConditional() + { + await TestAsync( + """ + class Program + { + static void Main(string[] args) + { + var x = true [|?|] true : false; + } + } + """, "?_CSharpKeyword"); + } + + [Fact] + public async Task TestLocalVar() + { + await TestAsync( + """ + class C + { + void M() + { + var a = 0; + int v[||]ar = 1; + } + } + """, "System.Int32"); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/867574")] + public async Task TestFatArrow() + { + await TestAsync( + """ + class C + { + void M() + { + var a = new System.Action(() =[||]> { + }); + } + } + """, "=>_CSharpKeyword"); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/867572")] + public async Task TestSubscription() + { + await TestAsync( + """ + class CCC + { + event System.Action e; + + void M() + { + e +[||]= () => { + }; + } + } + """, "+=_CSharpKeyword"); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/867554")] + public async Task TestComment() + { + await TestAsync(@"// some comm[||]ents here", "comments"); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/867529")] + public async Task TestDynamic() + { + await TestAsync( + """ + class C + { + void M() + { + dyna[||]mic d = 0; + } + } + """, "dynamic_CSharpKeyword"); + } + + [Fact] + public async Task TestRangeVariable() + { + await TestAsync( + """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + class Program + { + static void Main(string[] args) + { + var zzz = from y in args + select [||]y; + } + } + """, "System.String"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36001")] + public async Task TestNameof() + { + await Test_KeywordAsync( + """ + class C + { + void goo() + { + var v = [||]nameof(goo); + } + } + """, "nameof"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46988")] + public async Task TestNullForgiving() + { + await Test_KeywordAsync( + """ + #nullable enable + class C + { + int goo(string? x) + { + return x[||]!.GetHashCode(); + } + } + """, "nullForgiving"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46988")] + public async Task TestLogicalNot() + { + await Test_KeywordAsync( + """ + class C + { + bool goo(bool x) + { + return [||]!x; + } + } + """, "!"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestDefaultSwitchCase() + { + await Test_KeywordAsync( + """ + class C + { + void M1(int parameter) + { + switch(parameter) { + defa[||]ult: + parameter = default; + break; + } + } + } + """, "defaultcase"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestDefaultLiteralExpressionInsideSwitch() + { + await Test_KeywordAsync( + """ + class C + { + void M1(int parameter) + { + switch(parameter) { + default: + parameter = defa[||]ult; + break; + } + } + } + """, "default"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestDefaultExpressionInsideSwitch() + { + await Test_KeywordAsync( + """ + class C + { + void M1(int parameter) + { + switch(parameter) { + default: + parameter = defa[||]ult(int); + break; + } + } + } + """, "default"); + } - [Fact] - public async Task TestModifierSoupField() - { - await Test_KeywordAsync( - @"public class C -{ - new prot[||]ected static unsafe private goo; -}", "privateprotected"); - } - - [Fact] - public async Task TestVoid() - { - await Test_KeywordAsync( -@"class C -{ - vo[||]id goo() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestDefaultLiteralExpression() { + await Test_KeywordAsync( + """ + class C + { + int field = defa[||]ult; + } + """, "default"); } -}", "void"); - } - [Fact] - public async Task TestReturn() - { - await Test_KeywordAsync( -@"class C -{ - void goo() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestDefaultExpression() { - ret[||]urn; + await Test_KeywordAsync( + """ + class C + { + int field = defa[||]ult(int); + } + """, "default"); } -}", "return"); - } - - [Fact] - public async Task TestClassPartialType() - { - await Test_KeywordAsync( -@"part[||]ial class C -{ - partial void goo(); -}", "partialtype"); - } - - [Fact] - public async Task TestRecordPartialType() - { - await Test_KeywordAsync( -@"part[||]ial record C -{ - partial void goo(); -}", "partialtype"); - } - - [Fact] - public async Task TestRecordWithPrimaryConstructorPartialType() - { - await Test_KeywordAsync( -@"part[||]ial record C(string S) -{ - partial void goo(); -}", "partialtype"); - } - - [Fact] - public async Task TestPartialMethodInClass() - { - await Test_KeywordAsync( -@"partial class C -{ - par[||]tial void goo(); -}", "partialmethod"); - } - - [Fact] - public async Task TestPartialMethodInRecord() - { - await Test_KeywordAsync( -@"partial record C -{ - par[||]tial void goo(); -}", "partialmethod"); - } - - [Fact] - public async Task TestExtendedPartialMethod() - { - await Test_KeywordAsync( -@"partial class C -{ - public par[||]tial void goo(); -}", "partialmethod"); - } - - [Fact] - public async Task TestWhereClause() - { - await Test_KeywordAsync( -@"using System.Linq; -class Program where T : class -{ - void goo(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestDefaultLiteralExpressionInOptionalParameter() { - var x = from a in args - whe[||]re a.Length > 0 - select a; + await Test_KeywordAsync( + """ + class C + { + void M1(int parameter = defa[||]ult) { + } + } + """, "default"); } -}", "whereclause"); - } - [Fact] - public async Task TestWhereConstraint() - { - await Test_KeywordAsync( -@"using System.Linq; - -class Program wh[||]ere T : class -{ - void goo(string[] args) - { - var x = from a in args - where a.Length > 0 - select a; - } -}", "whereconstraint"); - } - - [Fact] - public async Task TestPreprocessor() - { - await TestAsync( -@"#regi[||]on -#endregion", "#region"); - } - - [Fact] - public async Task TestPreprocessor2() - { - await TestAsync( -@"#region[||] -#endregion", "#region"); - } - - [Fact] - public async Task TestConstructor() - { - await TestAsync( -@"namespace N -{ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestDefaultExpressionInOptionalParameter() { - void goo() - { - var x = new [|C|](); - } + await Test_KeywordAsync( + """ + class C + { + void M1(int parameter = defa[||]ult(int)) { + } + } + """, "default"); } -}", "N.C.#ctor"); - } - [Fact] - public async Task TestGenericClass() - { - await TestAsync( -@"namespace N -{ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestDefaultLiteralExpressionInMethodCall() { - void goo() - { - [|C|] c; - } + await Test_KeywordAsync( + """ + class C + { + void M1() { + M2(defa[||]ult); + } + } + """, "default"); } -}", "N.C`1"); - } - [Fact] - public async Task TestGenericMethod() - { - await TestAsync( -@"namespace N -{ - class C - { - void goo(T t, U u, V v) - { - C c; - c.g[|oo|](1, 1, 1); - } - } -}", "N.C`1.goo``3"); - } - - [Theory] - [InlineData("+")] - [InlineData("-")] - [InlineData("&")] - [InlineData("|")] - [InlineData("/")] - [InlineData("^")] - [InlineData(">")] - [InlineData(">=")] - [InlineData("!=")] - [InlineData("<")] - [InlineData("<=")] - [InlineData("<<")] - [InlineData(">>")] - [InlineData(">>>")] - [InlineData("*")] - [InlineData("%")] - [InlineData("&&")] - [InlineData("||")] - [InlineData("==")] - public async Task TestBinaryOperator(string operatorText) - { - await TestAsync( -$@"namespace N -{{ - class C - {{ - void goo() - {{ - var two = 1 [|{operatorText}|] 1; - }} - }} -}}", $"{operatorText}_CSharpKeyword"); - } - - [Theory] - [InlineData("+=")] - [InlineData("-=")] - [InlineData("/=")] - [InlineData("*=")] - [InlineData("%=")] - [InlineData("&=")] - [InlineData("|=")] - [InlineData("^=")] - [InlineData("<<=")] - [InlineData(">>=")] - [InlineData(">>>=")] - public async Task TestCompoundOperator(string operatorText) - { - await TestAsync( -$@"namespace N -{{ - class C - {{ - void goo(int x) - {{ - x [|{operatorText}|] x; - }} - }} -}}", $"{operatorText}_CSharpKeyword"); - } - - [Theory] - [InlineData("++")] - [InlineData("--")] - [InlineData("!")] - [InlineData("~")] - public async Task TestPrefixOperator(string operatorText) - { - await TestAsync( -$@"namespace N -{{ - class C - {{ - void goo(int x) - {{ - x = [|{operatorText}|]x; - }} - }} -}}", $"{operatorText}_CSharpKeyword"); - } - - [Theory] - [InlineData("++")] - [InlineData("--")] - public async Task TestPostfixOperator(string operatorText) - { - await TestAsync( -$@"namespace N -{{ - class C - {{ - void goo(int x) - {{ - x = x[|{operatorText}|]; - }} - }} -}}", $"{operatorText}_CSharpKeyword"); - } - - [Fact] - public async Task TestRelationalPattern() - { - await TestAsync( -@"namespace N -{ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestDefaultExpressionInMethodCall() { - void goo(string x) - { - if (x is { Length: [||]> 5 }) { } - } + await Test_KeywordAsync( + """ + class C + { + void M1() { + M2(defa[||]ult(int)); + } + } + """, "default"); } -}", ">_CSharpKeyword"); - } - [Fact] - public async Task TestGreaterThanInFunctionPointer() - { - await TestAsync(@" -unsafe class C -{ - delegate*[||] f; -} -", "functionPointer_CSharpKeyword"); - } - - [Fact] - public async Task TestLessThanInFunctionPointer() - { - await TestAsync(@" -unsafe class C -{ - delegate*[||] f; -} -", "functionPointer_CSharpKeyword"); - } - - [Fact] - public async Task TestEqualsOperatorInParameter() - { - await TestAsync( -@"namespace N -{ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestOuterClassDeclaration() { - void goo(int x [|=|] 0) - { - } + await Test_KeywordAsync( + """ + cla[||]ss OuterClass where T : class + { + class InnerClass where T : class { } + } + """, "class"); } -}", "optionalParameter_CSharpKeyword"); - } - [Fact] - public async Task TestEqualsOperatorInPropertyInitializer() - { - await TestAsync( -@"namespace N -{ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestInnerClassDeclaration() { - int P { get; } [|=|] 5; + await Test_KeywordAsync( + """ + class OuterClass where T : class + { + cla[||]ss InnerClass where T : class { } + } + """, "class"); } -}", "propertyInitializer_CSharpKeyword"); - } - [Fact] - public async Task TestVar() - { - await TestAsync( -@"using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestClassConstraintInOuterClass() + { + await Test_KeywordAsync( + """ + class OuterClass where T : cla[||]ss + { + class InnerClass where T : class { } + } + """, "classconstraint"); + } -class Program -{ - static void Main(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestClassConstraintInInnerClass() { - var[||] x = 3; + await Test_KeywordAsync( + """ + class OuterClass where T : class + { + class InnerClass where T : cla[||]ss { } + } + """, "classconstraint"); } -}", "var_CSharpKeyword"); - } - [Fact] - public async Task TestEquals() - { - await TestAsync( -@"using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestClassConstraintInGenericMethod() + { + await Test_KeywordAsync( + """ + class C + { + void M1() where T : cla[||]ss { } + } + """, "classconstraint"); + } -class Program -{ - static void Main(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestClassConstraintInGenericDelegate() { - var x =[||] 3; + await Test_KeywordAsync( + """ + class C + { + delegate T MyDelegate() where T : cla[||]ss; + } + """, "classconstraint"); } -}", "=_CSharpKeyword"); - } - [Fact] - public async Task TestEqualsInEnum() - { - await TestAsync( -@" -enum E -{ - A [||]= 1 -}", "enum_CSharpKeyword"); - } - - [Fact] - public async Task TestEqualsInAttribute() - { - await TestAsync( -@" -using System; - -[AttributeUsage(AttributeTargets.Class, Inherited [|=|] true)] -class MyAttribute : Attribute -{ -} -", "attributeNamedArgument_CSharpKeyword"); - } - - [Fact] - public async Task TestEqualsInUsingAlias() - { - await TestAsync( -@" -using SC [||]= System.Console; -", "using_CSharpKeyword"); - } - - [Fact] - public async Task TestEqualsInAnonymousObjectMemberDeclarator() - { - await TestAsync( -@" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestOuterStructDeclaration() { - var x = new { X [||]= 0 }; + await Test_KeywordAsync( + """ + str[||]uct OuterStruct where T : struct + { + struct InnerStruct where T : struct { } + } + """, "struct"); } -} -", "anonymousObject_CSharpKeyword"); - } - - [Fact] - public async Task TestEqualsInDocumentationComment() - { - await TestAsync( -@" -class C -{ - /// - /// - /// - void M() + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestInnerStructDeclaration() { - var x = new { X [||]= 0 }; + await Test_KeywordAsync( + """ + struct OuterStruct where T : struct + { + str[||]uct InnerStruct where T : struct { } + } + """, "struct"); } -} -", "see"); - } - - [Fact] - public async Task TestEqualsInLet() - { - await TestAsync( -@" -class C -{ - void M() + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestStructConstraintInOuterStruct() { - var y = - from x1 in x2 - let x3 [||]= x4 - select x5; + await Test_KeywordAsync( + """ + struct OuterStruct where T : str[||]uct + { + struct InnerStruct where T : struct { } + } + """, "structconstraint"); } -} -", "let_CSharpKeyword"); - } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestStructConstraintInInnerStruct() + { + await Test_KeywordAsync( + """ + struct OuterStruct where T : struct + { + struct InnerStruct where T : str[||]uct { } + } + """, "structconstraint"); + } - [Fact] - public async Task TestLetKeyword() - { - await TestAsync( -@" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestStructConstraintInGenericMethod() { - var y = - from x1 in x2 - [||]let x3 = x4 - select x5; + await Test_KeywordAsync( + """ + struct C + { + void M1() where T : str[||]uct { } + } + """, "structconstraint"); } -} -", "let_CSharpKeyword"); - } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestStructConstraintInGenericDelegate() + { + await Test_KeywordAsync( + """ + struct C + { + delegate T MyDelegate() where T : str[||]uct; + } + """, "structconstraint"); + } - [Fact] - public async Task TestFromIn() - { - await TestAsync( -@"using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestAllowsRefStructAntiConstraint() + { + await Test_KeywordAsync( + """ + class C + { + void M() + where T : all[||]ows ref struct + { + } + } + """, "allows"); + } -class Program -{ - static void Main(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestUsingStaticOnUsingKeyword() { - var x = from n i[||]n { - 1} + await Test_KeywordAsync( + """ + us[||]ing static namespace.Class; - select n - } -}", "from_CSharpKeyword"); - } + static class C + { + static int Field; - [Fact] - public async Task TestProperty() - { - await TestAsync( -@"using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; + static void Method() {} + } + """, "using-static"); + } -class Program -{ - static void Main(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestNormalUsingDirective() { - new UriBuilder().Fragm[||]ent; - } -}", "System.UriBuilder.Fragment"); - } + await Test_KeywordAsync( + """ + us[||]ing namespace.Class; - [Fact] - public async Task TestForeachIn() - { - await TestAsync( -@"using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; + static class C + { + static int Field; -class Program -{ - static void Main(string[] args) - { - foreach (var x in[||] { - 1} ) - { - } + static void Method() {} + } + """, "using"); } -}", "in_CSharpKeyword"); - } - [Fact] - public async Task TestRegionDescription() - { - await TestAsync( -@"class Program -{ - static void Main(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestUsingStatement() { - #region Begin MyR[||]egion for testing - #endregion End + await Test_KeywordAsync( + """ + using namespace.Class; + + class C + { + void Method(String someString) { + us[||]ing (var reader = new StringReader(someString)) + { + } + } + } + """, "using-statement"); } -}", "#region"); - } - [Fact] - public async Task TestGenericAngle_LessThanToken_TypeArgument() - { - await TestAsync( -@"class Program -{ - static void generic(T t) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestUsingDeclaration() { - generic[||](0); + await Test_KeywordAsync( + """ + using namespace.Class; + + class C + { + void Method(String someString) { + us[||]ing var reader = new StringReader(someString); + } + } + """, "using-statement"); } -}", "generics_CSharpKeyword"); - } - [Fact] - public async Task TestGenericAngle_GreaterThanToken_TypeArgument() - { - await TestAsync( -@"class Program -{ - static void generic(T t) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestUsingStaticOnStaticKeyword() { - generic|](0); + await Test_KeywordAsync( + """ + using sta[||]tic namespace.Class; + + static class C + { + static int Field; + + static void Method() {} + } + """, "using-static"); } -}", "generics_CSharpKeyword"); - } - [Fact] - public async Task TestGenericAngle_LessThanToken_TypeParameter() - { - await TestAsync( -@"class Program -{ - static void generic[|<|]T>(T t) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestStaticClass() { - generic(0); + await Test_KeywordAsync( + """ + using static namespace.Class; + + sta[||]tic class C + { + static int Field; + + static void Method() {} + } + """, "static"); } -}", "generics_CSharpKeyword"); - } - [Fact] - public async Task TestGenericAngle_GreaterThanToken_TypeParameter() - { - await TestAsync( -@"class Program -{ - static void generic|](T t) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestStaticField() { - generic(0); - } -}", "generics_CSharpKeyword"); - } + await Test_KeywordAsync( + """ + using static namespace.Class; - [Fact] - public async Task TestLocalReferenceIsType() - { - await TestAsync( -@"using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; + static class C + { + sta[||]tic int Field; -class Program -{ - static void Main(string[] args) - { - int x; - x[||]; + static void Method() {} + } + """, "static"); } -}", "System.Int32"); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/864266")] - public async Task TestConstantField() - { - await TestAsync( -@"class Program -{ - static void Main(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestStaticMethod() { - var i = int.Ma[||]xValue; + await Test_KeywordAsync( + """ + using static namespace.Class; + + static class C + { + static int Field; + + sta[||]tic void Method() {} + } + """, "static"); } -}", "System.Int32.MaxValue"); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/862420")] - public async Task TestParameter() - { - await TestAsync( -@"class Class2 -{ - void M1(int par[||]ameter) // 1 + [Fact] + public async Task TestWithKeyword() { + await Test_KeywordAsync( + """ + public record Point(int X, int Y); + + public static class Program + { + public static void Main() + { + var p1 = new Point(0, 0); + var p2 = p1 w[||]ith { X = 5 }; + } + } + """, "with"); } - void M2() + [Fact] + public async Task TestDiscard() { - int argument = 1; - M1(parameter: argument); // 2 + await Test_KeywordAsync( + """ + class C + { + void M() + { + [||]_ = Goo(); + } + + object Goo() => null; + } + """, "discard"); } -}", "System.Int32"); - } - [Fact] - public async Task TestRefReadonlyParameter_Ref() - { - await TestAsync( - """ - class C + [Fact] + public async Task TestChecked_01() + { + await Test_KeywordAsync( + """ + public class C + { + void goo() { - void M(r[||]ef readonly int x) + chec[||]ked { } } - """, "ref_CSharpKeyword"); - } + } + """, "checked"); + } - [Fact] - public async Task TestRefReadonlyParameter_ReadOnly() - { - await TestAsync( - """ - class C + [Fact] + public async Task TestChecked_02() + { + await Test_KeywordAsync( + """ + public class C + { + int goo() { - void M(ref read[||]only int x) - { - } + return chec[||]ked(0); } - """, "readonly_CSharpKeyword"); - } + } + """, "checked"); + } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/862420")] - public async Task TestArgumentType() - { - await TestAsync( -@"class Class2 -{ - void M1(int pa[||]rameter) // 1 + [Fact] + public async Task TestChecked_03() { + await Test_KeywordAsync( + """ + public class C + { + C operator chec[||]ked -(C x) {} + } + """, "checked"); } - void M2() + [Fact] + public async Task TestChecked_04() { - int argument = 1; - M1(parameter: argument); // 2 + await Test_KeywordAsync( + """ + public class C + { + C operator chec[||]ked +(C x, C y) {} + } + """, "checked"); } -}", "System.Int32"); - } - [Fact] - public async Task TestYieldReturn_OnYield() - { - await TestAsync(@" -using System.Collections.Generic; - -public class C -{ - public IEnumerable M() + [Fact] + public async Task TestChecked_05() { - [|yield|] return 0; + await Test_KeywordAsync( + """ + public class C + { + explicit operator chec[||]ked string(C x) {} + } + """, "checked"); } -} -", "yield_CSharpKeyword"); - } - [Fact] - public async Task TestYieldReturn_OnReturn() - { - await TestAsync(@" -using System.Collections.Generic; - -public class C -{ - public IEnumerable M() + [Fact] + public async Task TestChecked_06() { - yield [|return|] 0; + await Test_KeywordAsync( + """ + public class C + { + C I1.operator chec[||]ked -(C x) {} + } + """, "checked"); } -} -", "yield_CSharpKeyword"); - } - [Fact] - public async Task TestYieldBreak_OnYield() - { - await TestAsync(@" -using System.Collections.Generic; - -public class C -{ - public IEnumerable M() + [Fact] + public async Task TestChecked_07() { - [|yield|] break; + await Test_KeywordAsync( + """ + public class C + { + C I1.operator chec[||]ked +(C x, C y) {} + } + """, "checked"); } -} -", "yield_CSharpKeyword"); - } - [Fact] - public async Task TestYieldBreak_OnBreak() - { - await TestAsync(@" -using System.Collections.Generic; - -public class C -{ - public IEnumerable M() + [Fact] + public async Task TestChecked_08() { - yield [|break|] 0; + await Test_KeywordAsync( + """ + public class C + { + explicit I1.operator chec[||]ked string(C x) {} + } + """, "checked"); } -} -", "yield_CSharpKeyword"); - } - - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/862396")] - public async Task TestNoToken() - { - await TestAsync( -@"class Program -{ - static void Main(string[] args) + + [Fact] + public async Task TestChecked_09() { + await Test_KeywordAsync( + """ + public class C + { + /// + /// + /// + void goo() + { + } + } + """, "checked"); } -}[||]", "vs.texteditor"); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/862328")] - public async Task TestLiteral() - { - await TestAsync( -@"class Program -{ - static void Main(string[] args) + [Fact] + public async Task TestChecked_10() { - Main(new string[] { ""fo[||]o"" }); + await Test_KeywordAsync( + """ + public class C + { + /// + /// + /// + void goo() + { + } + } + """, "checked"); } -}", "System.String"); - } - - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/862478")] - public async Task TestColonColon() - { - await TestAsync( -@"using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -class Program -{ - static void Main(string[] args) + [Fact] + public async Task TestChecked_11() { - global:[||]:System.Console.Write(""); + await Test_KeywordAsync( + """ + public class C + { + /// + /// + /// + void goo() + { + } + } + """, "checked"); } -}", "::_CSharpKeyword"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46986")] - public async Task TestStringInterpolation() - { - await TestAsync( -@"using System; -class Program -{ - static void Main(string[] args) + [Fact] + public async Task TestRequired() { - Console.WriteLine($[||]""Hello, {args[0]}""); + await Test_KeywordAsync(""" + public class C + { + re[||]quired int Field; + } + """, "required"); } -}", "$_CSharpKeyword"); - } - - [Fact] - public async Task TestUtf8String() - { - await TestAsync( -@"using System; -class Program -{ - static void Main(string[] args) + [Fact] + public async Task TestDefaultConstraint() { - var x = ""Hel[||]lo""u8; + await Test_KeywordAsync(""" + public class Base + { + virtual void M(T? t) { } + } + public class C + { + override void M() where T : def[||]ault { } + } + """, expectedText: "defaultconstraint"); } -}", "Utf8StringLiteral_CSharpKeyword"); - } - - [Fact] - public async Task TestRawString() - { - await TestAsync( -@"using System; -class Program -{ - static void Main(string[] args) + [Fact] + public async Task TestDefaultCase() { - var x = """"""Hel[||]lo""""""; + await Test_KeywordAsync(""" + public class C + { + void M(object o) + { + switch (o) + { + case 1: + goto def[||]ault; + default: + return; + } + } + } + """, expectedText: "defaultcase"); } -}", "RawStringLiteral_CSharpKeyword"); - } - - [Fact] - public async Task TestUtf8RawString() - { - await TestAsync( -@"using System; -class Program -{ - static void Main(string[] args) + [Fact] + public async Task TestGotoDefault() { - var x = """"""Hel[||]lo""""""u8; + await Test_KeywordAsync(""" + public class C + { + void M(object o) + { + switch (o) + { + case 1: + goto default; + def[||]ault: + return; + } + } + } + """, expectedText: "defaultcase"); } -}", "Utf8StringLiteral_CSharpKeyword"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46986")] - public async Task TestVerbatimString() - { - await TestAsync( -@"using System; -class Program -{ - static void Main(string[] args) + [Fact] + public async Task TestLineDefault() { - Console.WriteLine(@[||]""Hello\""); + await Test_KeywordAsync(""" + #line def[||]ault + """, expectedText: "defaultline"); } -}", "@_CSharpKeyword"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46986")] - public async Task TestVerbatimInterpolatedString1() - { - await TestAsync( -@"using System; -class Program -{ - static void Main(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65311")] + public async Task TestNotnull_OnType() { - Console.WriteLine(@[||]$""Hello\ {args[0]}""); + await Test_KeywordAsync(""" + public class C where T : not[||]null + { + } + """, expectedText: "notnull"); } -}", "@$_CSharpKeyword"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46986")] - public async Task TestVerbatimInterpolatedString2() - { - await TestAsync( -@"using System; -class Program -{ - static void Main(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65311")] + public async Task TestNotnull_OnMethod() { - Console.WriteLine($[||]@""Hello\ {args[0]}""); + await Test_KeywordAsync(""" + public class C + { + void M() where T : not[||]null + { + } + } + """, expectedText: "notnull"); } -}", "@$_CSharpKeyword"); - } - - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/864658")] - public async Task TestNullable() - { - await TestAsync( -@"using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -class Program -{ - static void Main(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65311")] + public async Task TestNotnull_FieldName() { - int?[||] a = int.MaxValue; - a.Value.GetHashCode(); + await TestAsync(""" + public class C + { + int not[||]null = 0; + } + """, expectedText: "C.notnull"); } -}", "System.Nullable`1"); - } - - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/863517")] - public async Task TestAfterLastToken() - { - await TestAsync( -@"using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -class Program -{ - static void Main(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65311")] + public async Task TestUnmanaged_OnType() { - foreach (char var in ""!!!"")$$[||] - { - } + await Test_KeywordAsync(""" + public class C where T : un[||]managed + { + } + """, expectedText: "unmanaged"); } -}", "vs.texteditor"); - } - [Fact] - public async Task TestConditional() - { - await TestAsync( -@"class Program -{ - static void Main(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65311")] + public async Task TestUnmanaged_OnMethod() { - var x = true [|?|] true : false; + await Test_KeywordAsync(""" + public class C + { + void M() where T : un[||]managed + { + } + } + """, expectedText: "unmanaged"); } -}", "?_CSharpKeyword"); - } - [Fact] - public async Task TestLocalVar() - { - await TestAsync( -@"class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65311")] + public async Task TestUnmanaged_LocalName() { - var a = 0; - int v[||]ar = 1; + await TestAsync(""" + int un[||]managed = 0; + """, expectedText: "System.Int32"); } -}", "System.Int32"); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/867574")] - public async Task TestFatArrow() - { - await TestAsync( -@"class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65312")] + public async Task TestSwitchStatement() { - var a = new System.Action(() =[||]> { - }); + await Test_KeywordAsync(""" + swit[||]ch (1) { default: break; } + """, expectedText: "switch"); } -}", "=>_CSharpKeyword"); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/867572")] - public async Task TestSubscription() - { - await TestAsync( -@"class CCC -{ - event System.Action e; - - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65312")] + public async Task TestSwitchExpression() { - e +[||]= () => { - }; + await Test_KeywordAsync(""" + _ = 1 swit[||]ch { _ => 0 }; + """, expectedText: "switch-expression"); } -}", "+=_CSharpKeyword"); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/867554")] - public async Task TestComment() - { - await TestAsync(@"// some comm[||]ents here", "comments"); - } - - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/867529")] - public async Task TestDynamic() - { - await TestAsync( -@"class C -{ - void M() + [Fact] + public async Task TestFile() { - dyna[||]mic d = 0; + await Test_KeywordAsync(""" + fi[||]le class C { } + """, expectedText: "file"); } -}", "dynamic_CSharpKeyword"); - } - [Fact] - public async Task TestRangeVariable() - { - await TestAsync( -@"using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -class Program -{ - static void Main(string[] args) + [Fact] + public async Task TestRightShift() { - var zzz = from y in args - select [||]y; + await Test_KeywordAsync(""" + _ = 1 >[||]> 2; + """, expectedText: ">>"); } -}", "System.String"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36001")] - public async Task TestNameof() - { - await Test_KeywordAsync( -@"class C -{ - void goo() + [Fact] + public async Task TestUnsignedRightShift() { - var v = [||]nameof(goo); + await Test_KeywordAsync(""" + _ = 1 >>[||]> 2; + """, expectedText: ">>>"); } -}", "nameof"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46988")] - public async Task TestNullForgiving() - { - await Test_KeywordAsync( -@"#nullable enable -class C -{ - int goo(string? x) + [Fact] + public async Task TestUnsignedRightShiftAssignment() { - return x[||]!.GetHashCode(); + await Test_KeywordAsync(""" + 1 >>[||]>= 2; + """, expectedText: ">>>="); } -}", "nullForgiving"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46988")] - public async Task TestLogicalNot() - { - await Test_KeywordAsync( -@"class C -{ - bool goo(bool x) + [Fact] + public async Task TestPreprocessorIf() { - return [||]!x; + await TestAsync( + """ + #i[||]f ANY + #endif + """, "#if"); } -}", "!"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestDefaultSwitchCase() - { - await Test_KeywordAsync( -@"class C -{ - void M1(int parameter) - { - switch(parameter) { - defa[||]ult: - parameter = default; - break; - } - } -}", "defaultcase"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestDefaultLiteralExpressionInsideSwitch() - { - await Test_KeywordAsync( -@"class C -{ - void M1(int parameter) - { - switch(parameter) { - default: - parameter = defa[||]ult; - break; - } - } -}", "default"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestDefaultExpressionInsideSwitch() - { - await Test_KeywordAsync( -@"class C -{ - void M1(int parameter) - { - switch(parameter) { - default: - parameter = defa[||]ult(int); - break; - } - } -}", "default"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestDefaultLiteralExpression() - { - await Test_KeywordAsync( -@"class C -{ - int field = defa[||]ult; -}", "default"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestDefaultExpression() - { - await Test_KeywordAsync( -@"class C -{ - int field = defa[||]ult(int); -}", "default"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestDefaultLiteralExpressionInOptionalParameter() - { - await Test_KeywordAsync( -@"class C -{ - void M1(int parameter = defa[||]ult) { + [Fact] + public async Task TestPreprocessorIf2() + { + await TestAsync( + """ + #if ANY[||] + #endif + """, "#if"); } -}", "default"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestDefaultExpressionInOptionalParameter() - { - await Test_KeywordAsync( -@"class C -{ - void M1(int parameter = defa[||]ult(int)) { + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] + public async Task TestPreprocessorIf3() + { + await TestAsync( + """ + #if ANY[||]THING + #endif + """, "#if"); } -}", "default"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestDefaultLiteralExpressionInMethodCall() - { - await Test_KeywordAsync( -@"class C -{ - void M1() { - M2(defa[||]ult); + [Fact] + public async Task TestPreprocessorEndIf() + { + await TestAsync( + """ + #if ANY + #en[||]dif + """, "#endif"); } -}", "default"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestDefaultExpressionInMethodCall() - { - await Test_KeywordAsync( -@"class C -{ - void M1() { - M2(defa[||]ult(int)); - } -}", "default"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestOuterClassDeclaration() - { - await Test_KeywordAsync( -@"cla[||]ss OuterClass where T : class -{ - class InnerClass where T : class { } -}", "class"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestInnerClassDeclaration() - { - await Test_KeywordAsync( -@"class OuterClass where T : class -{ - cla[||]ss InnerClass where T : class { } -}", "class"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestClassConstraintInOuterClass() - { - await Test_KeywordAsync( -@"class OuterClass where T : cla[||]ss -{ - class InnerClass where T : class { } -}", "classconstraint"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestClassConstraintInInnerClass() - { - await Test_KeywordAsync( -@"class OuterClass where T : class -{ - class InnerClass where T : cla[||]ss { } -}", "classconstraint"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestClassConstraintInGenericMethod() - { - await Test_KeywordAsync( -@"class C -{ - void M1() where T : cla[||]ss { } -}", "classconstraint"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestClassConstraintInGenericDelegate() - { - await Test_KeywordAsync( -@"class C -{ - delegate T MyDelegate() where T : cla[||]ss; -}", "classconstraint"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestOuterStructDeclaration() - { - await Test_KeywordAsync( -@"str[||]uct OuterStruct where T : struct -{ - struct InnerStruct where T : struct { } -}", "struct"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestInnerStructDeclaration() - { - await Test_KeywordAsync( -@"struct OuterStruct where T : struct -{ - str[||]uct InnerStruct where T : struct { } -}", "struct"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestStructConstraintInOuterStruct() - { - await Test_KeywordAsync( -@"struct OuterStruct where T : str[||]uct -{ - struct InnerStruct where T : struct { } -}", "structconstraint"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestStructConstraintInInnerStruct() - { - await Test_KeywordAsync( -@"struct OuterStruct where T : struct -{ - struct InnerStruct where T : str[||]uct { } -}", "structconstraint"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestStructConstraintInGenericMethod() - { - await Test_KeywordAsync( -@"struct C -{ - void M1() where T : str[||]uct { } -}", "structconstraint"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestStructConstraintInGenericDelegate() - { - await Test_KeywordAsync( -@"struct C -{ - delegate T MyDelegate() where T : str[||]uct; -}", "structconstraint"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestAllowsRefStructAntiConstraint() - { - await Test_KeywordAsync( -@"class C -{ - void M() - where T : all[||]ows ref struct - { - } -}", "allows"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestUsingStaticOnUsingKeyword() - { - await Test_KeywordAsync( -@"us[||]ing static namespace.Class; - -static class C -{ - static int Field; - - static void Method() {} -}", "using-static"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestNormalUsingDirective() - { - await Test_KeywordAsync( -@"us[||]ing namespace.Class; - -static class C -{ - static int Field; - - static void Method() {} -}", "using"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestUsingStatement() - { - await Test_KeywordAsync( -@"using namespace.Class; - -class C -{ - void Method(String someString) { - us[||]ing (var reader = new StringReader(someString)) - { - } - } -}", "using-statement"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestUsingDeclaration() - { - await Test_KeywordAsync( -@"using namespace.Class; - -class C -{ - void Method(String someString) { - us[||]ing var reader = new StringReader(someString); - } -}", "using-statement"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestUsingStaticOnStaticKeyword() - { - await Test_KeywordAsync( -@"using sta[||]tic namespace.Class; - -static class C -{ - static int Field; - - static void Method() {} -}", "using-static"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestStaticClass() - { - await Test_KeywordAsync( -@"using static namespace.Class; - -sta[||]tic class C -{ - static int Field; - - static void Method() {} -}", "static"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestStaticField() - { - await Test_KeywordAsync( -@"using static namespace.Class; - -static class C -{ - sta[||]tic int Field; - - static void Method() {} -}", "static"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestStaticMethod() - { - await Test_KeywordAsync( -@"using static namespace.Class; - -static class C -{ - static int Field; - - sta[||]tic void Method() {} -}", "static"); - } - - [Fact] - public async Task TestWithKeyword() - { - await Test_KeywordAsync( -@" -public record Point(int X, int Y); - -public static class Program -{ - public static void Main() - { - var p1 = new Point(0, 0); - var p2 = p1 w[||]ith { X = 5 }; - } -}", "with"); - } - - [Fact] - public async Task TestDiscard() - { - await Test_KeywordAsync( -@" -class C -{ - void M() + [Fact] + public async Task TestPreprocessorEndIf2() { - [||]_ = Goo(); + await TestAsync( + """ + #if ANY + #endif[||] + """, "#endif"); } - object Goo() => null; -}", "discard"); - } - - [Fact] - public async Task TestChecked_01() - { - await Test_KeywordAsync( -@"public class C -{ - void goo() + [Fact] + public async Task TestPreprocessorElse() { - chec[||]ked - { - } + await TestAsync( + """ + #if ANY + #el[||]se + #endif + """, "#else"); } -}", "checked"); - } - [Fact] - public async Task TestChecked_02() - { - await Test_KeywordAsync( -@"public class C -{ - int goo() + [Fact] + public async Task TestPreprocessorElse2() { - return chec[||]ked(0); + await TestAsync( + """ + #if ANY + #else[||] + #endif + """, "#else"); } -}", "checked"); - } - [Fact] - public async Task TestChecked_03() - { - await Test_KeywordAsync( -@"public class C -{ - C operator chec[||]ked -(C x) {} -}", "checked"); - } - - [Fact] - public async Task TestChecked_04() - { - await Test_KeywordAsync( -@"public class C -{ - C operator chec[||]ked +(C x, C y) {} -}", "checked"); - } - - [Fact] - public async Task TestChecked_05() - { - await Test_KeywordAsync( -@"public class C -{ - explicit operator chec[||]ked string(C x) {} -}", "checked"); - } - - [Fact] - public async Task TestChecked_06() - { - await Test_KeywordAsync( -@"public class C -{ - C I1.operator chec[||]ked -(C x) {} -}", "checked"); - } - - [Fact] - public async Task TestChecked_07() - { - await Test_KeywordAsync( -@"public class C -{ - C I1.operator chec[||]ked +(C x, C y) {} -}", "checked"); - } - - [Fact] - public async Task TestChecked_08() - { - await Test_KeywordAsync( -@"public class C -{ - explicit I1.operator chec[||]ked string(C x) {} -}", "checked"); - } - - [Fact] - public async Task TestChecked_09() - { - await Test_KeywordAsync( -@"public class C -{ - /// - /// - /// - void goo() + [Fact] + public async Task TestPreprocessorElIf() { + await TestAsync( + """ + #if ANY + #el[||]if SOME + #endif + """, "#elif"); } -}", "checked"); - } - [Fact] - public async Task TestChecked_10() - { - await Test_KeywordAsync( -@"public class C -{ - /// - /// - /// - void goo() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] + public async Task TestPreprocessorElIf2() { + await TestAsync( + """ + #if ANY + #elif S[||]OME + #endif + """, "#elif"); } -}", "checked"); - } - [Fact] - public async Task TestChecked_11() - { - await Test_KeywordAsync( -@"public class C -{ - /// - /// - /// - void goo() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] + public async Task TestPreprocessorPragmaWarning1() { + await TestAsync( + """ + #pragma warning disable CS[||]0312 + """, "#pragma"); } -}", "checked"); - } - - [Fact] - public async Task TestRequired() - { - await Test_KeywordAsync(""" - public class C - { - re[||]quired int Field; - } - """, "required"); - } - - [Fact] - public async Task TestDefaultConstraint() - { - await Test_KeywordAsync(""" - public class Base - { - virtual void M(T? t) { } - } - public class C - { - override void M() where T : def[||]ault { } - } - """, expectedText: "defaultconstraint"); - } - [Fact] - public async Task TestDefaultCase() - { - await Test_KeywordAsync(""" - public class C - { - void M(object o) - { - switch (o) - { - case 1: - goto def[||]ault; - default: - return; - } - } - } - """, expectedText: "defaultcase"); - } - - [Fact] - public async Task TestGotoDefault() - { - await Test_KeywordAsync(""" - public class C - { - void M(object o) - { - switch (o) - { - case 1: - goto default; - def[||]ault: - return; - } - } - } - """, expectedText: "defaultcase"); - } - - [Fact] - public async Task TestLineDefault() - { - await Test_KeywordAsync(""" - #line def[||]ault - """, expectedText: "defaultline"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65311")] - public async Task TestNotnull_OnType() - { - await Test_KeywordAsync(""" - public class C where T : not[||]null - { - } - """, expectedText: "notnull"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65311")] - public async Task TestNotnull_OnMethod() - { - await Test_KeywordAsync(""" - public class C - { - void M() where T : not[||]null - { - } - } - """, expectedText: "notnull"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65311")] - public async Task TestNotnull_FieldName() - { - await TestAsync(""" - public class C - { - int not[||]null = 0; - } - """, expectedText: "C.notnull"); - } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] + public async Task TestPreprocessorPragmaWarning2() + { + await TestAsync( + """ + #pragm[||]a warning disable CS0312 + """, "#pragma"); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65311")] - public async Task TestUnmanaged_OnType() - { - await Test_KeywordAsync(""" - public class C where T : un[||]managed - { - } - """, expectedText: "unmanaged"); - } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] + public async Task TestPreprocessorPragmaWarning3() + { + await TestAsync( + """ + #pragma warni[||]ng disable CS0312 + """, "#warning"); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65311")] - public async Task TestUnmanaged_OnMethod() - { - await Test_KeywordAsync(""" - public class C - { - void M() where T : un[||]managed - { - } - } - """, expectedText: "unmanaged"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65311")] - public async Task TestUnmanaged_LocalName() - { - await TestAsync(""" - int un[||]managed = 0; - """, expectedText: "System.Int32"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65312")] - public async Task TestSwitchStatement() - { - await Test_KeywordAsync(""" - swit[||]ch (1) { default: break; } - """, expectedText: "switch"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65312")] - public async Task TestSwitchExpression() - { - await Test_KeywordAsync(""" - _ = 1 swit[||]ch { _ => 0 }; - """, expectedText: "switch-expression"); - } - - [Fact] - public async Task TestFile() - { - await Test_KeywordAsync(""" - fi[||]le class C { } - """, expectedText: "file"); - } - - [Fact] - public async Task TestRightShift() - { - await Test_KeywordAsync(""" - _ = 1 >[||]> 2; - """, expectedText: ">>"); - } - - [Fact] - public async Task TestUnsignedRightShift() - { - await Test_KeywordAsync(""" - _ = 1 >>[||]> 2; - """, expectedText: ">>>"); - } - - [Fact] - public async Task TestUnsignedRightShiftAssignment() - { - await Test_KeywordAsync(""" - 1 >>[||]>= 2; - """, expectedText: ">>>="); - } - - [Fact] - public async Task TestPreprocessorIf() - { - await TestAsync( -@" -#i[||]f ANY -#endif -", "#if"); - } - - [Fact] - public async Task TestPreprocessorIf2() - { - await TestAsync( -@" -#if ANY[||] -#endif -", "#if"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] - public async Task TestPreprocessorIf3() - { - await TestAsync( -@" -#if ANY[||]THING -#endif -", "#if"); - } - - [Fact] - public async Task TestPreprocessorEndIf() - { - await TestAsync( -@" -#if ANY -#en[||]dif -", "#endif"); - } - - [Fact] - public async Task TestPreprocessorEndIf2() - { - await TestAsync( -@" -#if ANY -#endif[||] -", "#endif"); - } - - [Fact] - public async Task TestPreprocessorElse() - { - await TestAsync( -@" -#if ANY -#el[||]se -#endif -", "#else"); - } - - [Fact] - public async Task TestPreprocessorElse2() - { - await TestAsync( -@" -#if ANY -#else[||] -#endif -", "#else"); - } - - [Fact] - public async Task TestPreprocessorElIf() - { - await TestAsync( -@" -#if ANY -#el[||]if SOME -#endif -", "#elif"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] - public async Task TestPreprocessorElIf2() - { - await TestAsync( -@" -#if ANY -#elif S[||]OME -#endif -", "#elif"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] - public async Task TestPreprocessorPragmaWarning1() - { - await TestAsync( -@" -#pragma warning disable CS[||]0312 -", "#pragma"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] - public async Task TestPreprocessorPragmaWarning2() - { - await TestAsync( -@" -#pragm[||]a warning disable CS0312 -", "#pragma"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] - public async Task TestPreprocessorPragmaWarning3() - { - await TestAsync( -@" -#pragma warni[||]ng disable CS0312 -", "#warning"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] - public async Task TestPreprocessorPragmaWarning4() - { - await TestAsync( -@" -#pragma warning dis[||]able CS0312 -", "#disable"); - } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] + public async Task TestPreprocessorPragmaWarning4() + { + await TestAsync( + """ + #pragma warning dis[||]able CS0312 + """, "#disable"); } } From 88e3f5b8e93830cf778c22cb8f01a3d9b190f7f1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 20:22:50 -0800 Subject: [PATCH 453/508] Update tests --- src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs b/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs index 915d724ce7b1b..fc030c088c745 100644 --- a/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs +++ b/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs @@ -2217,6 +2217,7 @@ await TestAsync( """ #if ANY #endif[||] + """, "#endif"); } From bfbc7ca98ab30ae76b3d69527ac2ea7f1b19b0f0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 20:24:36 -0800 Subject: [PATCH 454/508] inline --- .../Finders/PreprocessingSymbolReferenceFinder.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs index dd6bfbbb4b0df..1ab72fbc61b13 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs @@ -27,12 +27,13 @@ protected override async Task DetermineDocumentsToSearchAsync( FindReferencesSearchOptions options, CancellationToken cancellationToken) { - await FindDocumentsWithPredicateAsync(project, documents, DetermineDocument, processResult, processResultData, cancellationToken).ConfigureAwait(false); - - bool DetermineDocument(SyntaxTreeIndex syntaxTreeIndex) - { - return syntaxTreeIndex.ContainsDirective && syntaxTreeIndex.ProbablyContainsIdentifier(symbol.Name); - } + await FindDocumentsWithPredicateAsync( + project, + documents, + index => index.ContainsDirective && index.ProbablyContainsIdentifier(symbol.Name), + processResult, + processResultData, + cancellationToken).ConfigureAwait(false); } protected override void FindReferencesInDocument( From e5a09a423641c295d43686551a526db0eb641781 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 20:28:27 -0800 Subject: [PATCH 455/508] Simplify --- .../SemanticFacts/CSharpSemanticFacts.cs | 64 ++++++------------- 1 file changed, 19 insertions(+), 45 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs index 74d9bffc6b73a..ebd2ccc4e5b32 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs @@ -229,8 +229,7 @@ private static void FlattenDeconstructionMethods(DeconstructionInfo deconstructi public bool IsPartial(INamedTypeSymbol typeSymbol, CancellationToken cancellationToken) { - var syntaxRefs = typeSymbol.DeclaringSyntaxReferences; - foreach (var syntaxRef in syntaxRefs) + foreach (var syntaxRef in typeSymbol.DeclaringSyntaxReferences) { var node = syntaxRef.GetSyntax(cancellationToken); if (node is BaseTypeDeclarationSyntax { Modifiers: { } modifiers } && @@ -243,23 +242,15 @@ public bool IsPartial(INamedTypeSymbol typeSymbol, CancellationToken cancellatio return false; } - public IEnumerable GetDeclaredSymbols( - SemanticModel semanticModel, SyntaxNode memberDeclaration, CancellationToken cancellationToken) - { - switch (memberDeclaration) + public IEnumerable GetDeclaredSymbols(SemanticModel semanticModel, SyntaxNode memberDeclaration, CancellationToken cancellationToken) + => memberDeclaration switch { - case FieldDeclarationSyntax field: - return field.Declaration.Variables.Select( - v => semanticModel.GetRequiredDeclaredSymbol(v, cancellationToken)); - - case EventFieldDeclarationSyntax eventField: - return eventField.Declaration.Variables.Select( - v => semanticModel.GetRequiredDeclaredSymbol(v, cancellationToken)); - - default: - return [semanticModel.GetRequiredDeclaredSymbol(memberDeclaration, cancellationToken)]; - } - } + FieldDeclarationSyntax field + => field.Declaration.Variables.Select(v => semanticModel.GetRequiredDeclaredSymbol(v, cancellationToken)), + EventFieldDeclarationSyntax eventField + => eventField.Declaration.Variables.Select(v => semanticModel.GetRequiredDeclaredSymbol(v, cancellationToken)), + _ => [semanticModel.GetRequiredDeclaredSymbol(memberDeclaration, cancellationToken)], + }; public IParameterSymbol? FindParameterForArgument(SemanticModel semanticModel, SyntaxNode argument, bool allowUncertainCandidates, bool allowParams, CancellationToken cancellationToken) => ((ArgumentSyntax)argument).DetermineParameter(semanticModel, allowUncertainCandidates, allowParams, cancellationToken); @@ -364,13 +355,13 @@ private ImmutableArray GetSymbolInfo(SemanticModel semanticModel, Synta type.Equals(symbol, SymbolEqualityComparer.Default) && !type.Equals(symbol, SymbolEqualityComparer.IncludeNullability)) { - return ImmutableArray.Create(type); + return [type]; } } var preprocessingSymbol = GetPreprocessingSymbol(semanticModel, node); return preprocessingSymbol != null - ? ImmutableArray.Create(preprocessingSymbol) + ? [preprocessingSymbol] : semanticModel.GetSymbolInfo(node, cancellationToken).GetBestOrAllSymbols(); } @@ -405,40 +396,23 @@ public string GenerateNameForExpression(SemanticModel semanticModel, SyntaxNode => semanticModel.GenerateNameForExpression((ExpressionSyntax)expression, capitalize, cancellationToken); public IPreprocessingSymbol? GetPreprocessingSymbol(SemanticModel semanticModel, SyntaxNode node) - { - return node switch + => node switch { - IdentifierNameSyntax nameSyntax - when IsInPreprocessingSymbolContext(nameSyntax) => CreatePreprocessingSymbol(semanticModel, nameSyntax.Identifier), + IdentifierNameSyntax nameSyntax when IsInPreprocessingSymbolContext(nameSyntax) => CreatePreprocessingSymbol(semanticModel, nameSyntax.Identifier), DefineDirectiveTriviaSyntax defineSyntax => CreatePreprocessingSymbol(semanticModel, defineSyntax.Name), UndefDirectiveTriviaSyntax undefSyntax => CreatePreprocessingSymbol(semanticModel, undefSyntax.Name), _ => null, }; - } private static IPreprocessingSymbol? CreatePreprocessingSymbol(SemanticModel model, SyntaxToken identifier) - { - return model.Compilation.CreatePreprocessingSymbol(identifier.ValueText); - } + => model.Compilation.CreatePreprocessingSymbol(identifier.ValueText); private static bool IsInPreprocessingSymbolContext(SyntaxNode node) - { - if (node.Ancestors().Any(n => IsPreprocessorDirectiveAcceptingPreprocessingSymbols(n.Kind()))) - { - return true; - } - - return false; - - static bool IsPreprocessorDirectiveAcceptingPreprocessingSymbols(SyntaxKind kind) - { - return kind - is SyntaxKind.IfDirectiveTrivia - or SyntaxKind.ElifDirectiveTrivia - or SyntaxKind.DefineDirectiveTrivia - or SyntaxKind.UndefDirectiveTrivia; - } - } + => node.Ancestors().Any(n => n.Kind() is + SyntaxKind.IfDirectiveTrivia or + SyntaxKind.ElifDirectiveTrivia or + SyntaxKind.DefineDirectiveTrivia or + SyntaxKind.UndefDirectiveTrivia); #if !CODE_STYLE From d8975892eff1d64c3ef00775c11beee667800dcb Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 20:30:18 -0800 Subject: [PATCH 456/508] Simplify --- .../Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs index 2044c004fa3df..11137f869770f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs @@ -8,19 +8,15 @@ internal partial struct SymbolKey { private sealed class PreprocessingSymbolKey : AbstractSymbolKey { - public static readonly PreprocessingSymbolKey Instance = new PreprocessingSymbolKey(); + public static readonly PreprocessingSymbolKey Instance = new(); public sealed override void Create(IPreprocessingSymbol symbol, SymbolKeyWriter visitor) - { - visitor.WriteString(symbol.Name); - } + => visitor.WriteString(symbol.Name); protected sealed override SymbolKeyResolution Resolve(SymbolKeyReader reader, IPreprocessingSymbol? contextualSymbol, out string? failureReason) { failureReason = null; - var preprocessingName = reader.ReadRequiredString(); - var preprocessingSymbol = reader.Compilation.CreatePreprocessingSymbol(preprocessingName); - return new SymbolKeyResolution(preprocessingSymbol); + return new SymbolKeyResolution(reader.Compilation.CreatePreprocessingSymbol(reader.ReadRequiredString())); } } } From d12a2e1ce3dd45da2aacbc84169083d0d7325712 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 20:31:25 -0800 Subject: [PATCH 457/508] sort --- .../SymbolKey/SymbolKey.SymbolKeyReader.cs | 24 +++++++++---------- .../SymbolKey/SymbolKey.SymbolKeyWriter.cs | 24 +++++++++---------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyReader.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyReader.cs index 34491f66559db..8e99117ea7fd8 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyReader.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyReader.cs @@ -507,30 +507,30 @@ private SymbolKeyResolution ReadWorker(SymbolKeyType type, out string? failureRe => type switch { SymbolKeyType.Alias => AliasSymbolKey.Instance.Resolve(this, out failureReason), + SymbolKeyType.AnonymousFunctionOrDelegate => AnonymousFunctionOrDelegateSymbolKey.Resolve(this, out failureReason), + SymbolKeyType.AnonymousType => AnonymousTypeSymbolKey.Instance.Resolve(this, out failureReason), + SymbolKeyType.ArrayType => ArrayTypeSymbolKey.Instance.Resolve(this, out failureReason), + SymbolKeyType.Assembly => AssemblySymbolKey.Instance.Resolve(this, out failureReason), SymbolKeyType.BodyLevel => BodyLevelSymbolKey.Resolve(this, out failureReason), + SymbolKeyType.BuiltinOperator => BuiltinOperatorSymbolKey.Instance.Resolve(this, out failureReason), SymbolKeyType.ConstructedMethod => ConstructedMethodSymbolKey.Instance.Resolve(this, out failureReason), - SymbolKeyType.NamedType => NamedTypeSymbolKey.Instance.Resolve(this, out failureReason), + SymbolKeyType.DynamicType => DynamicTypeSymbolKey.Instance.Resolve(this, out failureReason), SymbolKeyType.ErrorType => ErrorTypeSymbolKey.Instance.Resolve(this, out failureReason), + SymbolKeyType.Event => EventSymbolKey.Instance.Resolve(this, out failureReason), SymbolKeyType.Field => FieldSymbolKey.Instance.Resolve(this, out failureReason), SymbolKeyType.FunctionPointer => FunctionPointerTypeSymbolKey.Instance.Resolve(this, out failureReason), - SymbolKeyType.DynamicType => DynamicTypeSymbolKey.Instance.Resolve(this, out failureReason), - SymbolKeyType.BuiltinOperator => BuiltinOperatorSymbolKey.Instance.Resolve(this, out failureReason), SymbolKeyType.Method => MethodSymbolKey.Instance.Resolve(this, out failureReason), + SymbolKeyType.Module => ModuleSymbolKey.Instance.Resolve(this, out failureReason), + SymbolKeyType.NamedType => NamedTypeSymbolKey.Instance.Resolve(this, out failureReason), SymbolKeyType.Namespace => NamespaceSymbolKey.Instance.Resolve(this, out failureReason), - SymbolKeyType.PointerType => PointerTypeSymbolKey.Instance.Resolve(this, out failureReason), SymbolKeyType.Parameter => ParameterSymbolKey.Instance.Resolve(this, out failureReason), + SymbolKeyType.PointerType => PointerTypeSymbolKey.Instance.Resolve(this, out failureReason), + SymbolKeyType.Preprocessing => PreprocessingSymbolKey.Instance.Resolve(this, out failureReason), SymbolKeyType.Property => PropertySymbolKey.Instance.Resolve(this, out failureReason), - SymbolKeyType.ArrayType => ArrayTypeSymbolKey.Instance.Resolve(this, out failureReason), - SymbolKeyType.Assembly => AssemblySymbolKey.Instance.Resolve(this, out failureReason), - SymbolKeyType.TupleType => TupleTypeSymbolKey.Instance.Resolve(this, out failureReason), - SymbolKeyType.Module => ModuleSymbolKey.Instance.Resolve(this, out failureReason), - SymbolKeyType.Event => EventSymbolKey.Instance.Resolve(this, out failureReason), SymbolKeyType.ReducedExtensionMethod => ReducedExtensionMethodSymbolKey.Instance.Resolve(this, out failureReason), + SymbolKeyType.TupleType => TupleTypeSymbolKey.Instance.Resolve(this, out failureReason), SymbolKeyType.TypeParameter => TypeParameterSymbolKey.Instance.Resolve(this, out failureReason), - SymbolKeyType.AnonymousType => AnonymousTypeSymbolKey.Instance.Resolve(this, out failureReason), - SymbolKeyType.AnonymousFunctionOrDelegate => AnonymousFunctionOrDelegateSymbolKey.Resolve(this, out failureReason), SymbolKeyType.TypeParameterOrdinal => TypeParameterOrdinalSymbolKey.Resolve(this, out failureReason), - SymbolKeyType.Preprocessing => PreprocessingSymbolKey.Instance.Resolve(this, out failureReason), _ => throw new NotImplementedException(), }; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs index cf152d3c1752c..9d9735c7b6c17 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs @@ -20,29 +20,29 @@ internal partial struct SymbolKey private enum SymbolKeyType { Alias = 'A', + AnonymousFunctionOrDelegate = 'Z', + AnonymousType = 'W', + ArrayType = 'R', + Assembly = 'S', BodyLevel = 'B', + BuiltinOperator = 'L', ConstructedMethod = 'C', - NamedType = 'D', + DynamicType = 'I', ErrorType = 'E', + Event = 'V', Field = 'F', FunctionPointer = 'G', - DynamicType = 'I', - Preprocessing = 'J', - BuiltinOperator = 'L', Method = 'M', + Module = 'U', + NamedType = 'D', Namespace = 'N', - PointerType = 'O', Parameter = 'P', + PointerType = 'O', + Preprocessing = 'J', Property = 'Q', - ArrayType = 'R', - Assembly = 'S', - TupleType = 'T', - Module = 'U', - Event = 'V', - AnonymousType = 'W', ReducedExtensionMethod = 'X', + TupleType = 'T', TypeParameter = 'Y', - AnonymousFunctionOrDelegate = 'Z', // Not to be confused with ArrayType. This indicates an array of elements in the stream. Array = '%', From a97f73a2f88155b1ed7468a456cce83c773ee8b1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 22:25:26 -0800 Subject: [PATCH 458/508] Remove trailing comma when converting to a tuple --- ...ymousTypeToTupleCodeRefactoringProvider.cs | 18 ++++++++------ .../ConvertAnonymousTypeToTupleTests.cs | 24 +++++++++++++++++++ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/Features/CSharp/Portable/ConvertAnonymousType/CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertAnonymousType/CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider.cs index 884b529a0f0a0..4b68fe67efe48 100644 --- a/src/Features/CSharp/Portable/ConvertAnonymousType/CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertAnonymousType/CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Collections.Generic; using System.Composition; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -29,17 +31,19 @@ protected override int GetInitializerCount(AnonymousObjectCreationExpressionSynt protected override TupleExpressionSyntax ConvertToTuple(AnonymousObjectCreationExpressionSyntax anonCreation) => TupleExpression( - OpenParenToken.WithTriviaFrom(anonCreation.OpenBraceToken), - ConvertInitializers(anonCreation.Initializers), - CloseParenToken.WithTriviaFrom(anonCreation.CloseBraceToken)) - .WithPrependedLeadingTrivia(anonCreation.GetLeadingTrivia()); + OpenParenToken.WithTriviaFrom(anonCreation.OpenBraceToken), + ConvertInitializers(anonCreation.Initializers), + CloseParenToken.WithTriviaFrom(anonCreation.CloseBraceToken)) + .WithPrependedLeadingTrivia(anonCreation.GetLeadingTrivia()); private static SeparatedSyntaxList ConvertInitializers(SeparatedSyntaxList initializers) - => SeparatedList(initializers.Select(ConvertInitializer), initializers.GetSeparators()); + => SeparatedList(initializers.Select(ConvertInitializer), GetSeparators(initializers)); + + private static IEnumerable GetSeparators(SeparatedSyntaxList initializers) + => initializers.Count == 0 ? [] : initializers.GetSeparators().Take(initializers.Count - 1); private static ArgumentSyntax ConvertInitializer(AnonymousObjectMemberDeclaratorSyntax declarator) - => Argument(ConvertName(declarator.NameEquals), default, declarator.Expression) - .WithTriviaFrom(declarator); + => Argument(ConvertName(declarator.NameEquals), refKindKeyword: default, declarator.Expression).WithTriviaFrom(declarator); private static NameColonSyntax? ConvertName(NameEqualsSyntax? nameEquals) => nameEquals == null diff --git a/src/Features/CSharpTest/ConvertAnonymousType/ConvertAnonymousTypeToTupleTests.cs b/src/Features/CSharpTest/ConvertAnonymousType/ConvertAnonymousTypeToTupleTests.cs index dd7360e049644..b01cee51df531 100644 --- a/src/Features/CSharpTest/ConvertAnonymousType/ConvertAnonymousTypeToTupleTests.cs +++ b/src/Features/CSharpTest/ConvertAnonymousType/ConvertAnonymousTypeToTupleTests.cs @@ -501,4 +501,28 @@ static void Main(string[] args) } """); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75950")] + public async Task RemoveTrailingComma() + { + var text = """ + class Test + { + void Method() + { + var t1 = [||]new { a = 1, b = 2, }; + } + } + """; + var expected = """ + class Test + { + void Method() + { + var t1 = (a: 1, b: 2); + } + } + """; + await TestInRegularAndScriptAsync(text, expected); + } } From 273f2a7ad54e91f756e33b2d436d80dd5baaa03d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 19 Nov 2024 22:26:08 -0800 Subject: [PATCH 459/508] Remove trailing comma when converting to a tuple --- .../CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Features/CSharp/Portable/ConvertAnonymousType/CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertAnonymousType/CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider.cs index 4b68fe67efe48..4d16d9b6a335c 100644 --- a/src/Features/CSharp/Portable/ConvertAnonymousType/CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertAnonymousType/CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Composition; using System.Diagnostics.CodeAnalysis; From 9bea39d29a00719039d0d3705a15d384bdc385da Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Wed, 20 Nov 2024 09:33:49 +0100 Subject: [PATCH 460/508] Fix TFM of MSBuild.UnitTests (#75969) * Fix TFM of MSBuild.UnitTests * Fix TFM in path --- eng/pipelines/test-integration-helix.yml | 4 ++-- ...Microsoft.CodeAnalysis.Workspaces.MSBuild.UnitTests.csproj | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/pipelines/test-integration-helix.yml b/eng/pipelines/test-integration-helix.yml index 3ac4f33fcc055..9cb3fefdfaa33 100644 --- a/eng/pipelines/test-integration-helix.yml +++ b/eng/pipelines/test-integration-helix.yml @@ -69,9 +69,9 @@ stages: # This is a temporary step until the actual test run moves to helix (then we would need to rehydrate the tests there instead) - task: BatchScript@1 - displayName: Rehydrate Microsoft.CodeAnalysis.Workspaces.MSBuild.UnitTests (net8.0) + displayName: Rehydrate Microsoft.CodeAnalysis.Workspaces.MSBuild.UnitTests (net9.0) inputs: - filename: ./artifacts\bin\Microsoft.CodeAnalysis.Workspaces.MSBuild.UnitTests/${{ parameters.configuration }}/net8.0/rehydrate.cmd + filename: ./artifacts\bin\Microsoft.CodeAnalysis.Workspaces.MSBuild.UnitTests/${{ parameters.configuration }}/net9.0/rehydrate.cmd env: HELIX_CORRELATION_PAYLOAD: '$(Build.SourcesDirectory)\.duplicate' diff --git a/src/Workspaces/MSBuildTest/Microsoft.CodeAnalysis.Workspaces.MSBuild.UnitTests.csproj b/src/Workspaces/MSBuildTest/Microsoft.CodeAnalysis.Workspaces.MSBuild.UnitTests.csproj index 9c4abc25d359d..fb8224323dc55 100644 --- a/src/Workspaces/MSBuildTest/Microsoft.CodeAnalysis.Workspaces.MSBuild.UnitTests.csproj +++ b/src/Workspaces/MSBuildTest/Microsoft.CodeAnalysis.Workspaces.MSBuild.UnitTests.csproj @@ -4,7 +4,7 @@ Library Microsoft.CodeAnalysis.MSBuild.UnitTests - $(NetVS);net472 + $(NetRoslyn);net472 From 94fff7ad4f11977f903f435b0d97dcf0f2183710 Mon Sep 17 00:00:00 2001 From: Fred Silberberg Date: Wed, 20 Nov 2024 13:53:20 -0800 Subject: [PATCH 461/508] Update language feature status (#75962) --- docs/Language Feature Status.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/Language Feature Status.md b/docs/Language Feature Status.md index c23f966750f1a..6375ddd575789 100644 --- a/docs/Language Feature Status.md +++ b/docs/Language Feature Status.md @@ -16,12 +16,14 @@ efforts behind them. | [`field` keyword in properties](https://github.com/dotnet/csharplang/issues/140) | [field-keyword](https://github.com/dotnet/roslyn/tree/features/field-keyword) | [Merged into 17.12p3](https://github.com/dotnet/roslyn/issues/57012) | [Youssef1313](https://github.com/Youssef1313), [cston](https://github.com/cston) | [333fred](https://github.com/333fred), [RikkiGibson](https://github.com/RikkiGibson) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | | [First-class Span Types](https://github.com/dotnet/csharplang/issues/7905) | [FirstClassSpan](https://github.com/dotnet/roslyn/tree/features/FirstClassSpan) | [Merged into 17.13p1](https://github.com/dotnet/roslyn/issues/73445) | [jjonescz](https://github.com/jjonescz) | [cston](https://github.com/cston), [333fred](https://github.com/333fred) | | [333fred](https://github.com/333fred), [stephentoub](https://github.com/stephentoub) | | [Unbound generic types in `nameof`](https://github.com/dotnet/csharplang/issues/8480) | [PR](https://github.com/dotnet/roslyn/pull/75368) | [In Progress](https://github.com/dotnet/roslyn/pull/75368) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | [jcouv](https://github.com/jcouv) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | +| Runtime Async | [features/runtime-async](https://github.com/dotnet/roslyn/tree/features/runtime-async) | [In Progress](https://github.com/dotnet/roslyn/issues/75960) | [333fred](https://github.com/333fred) | [jcouv](https://github.com/jcouv), [RikkiGibson](https://github.com/RikkiGibson) | | | # Working Set VB | Feature | Branch | State | Developer | Reviewer | IDE Buddy | LDM Champ | | ------- | ------ | ----- | --------- | -------- | --------- | --------- | | Recognizing 'unmanaged' constraint | main | [Merged into 17.13 P2](https://github.com/dotnet/roslyn/pull/75665) | [AlekseyTs](https://github.com/AlekseyTs) | [cston](https://github.com/cston), [333fred](https://github.com/333fred) | (no IDE impact) | [jaredpar](https://github.com/jaredpar) | +| Overload Resolution Priority | [features/VBOverloadResolutionPriority](https://github.com/dotnet/roslyn/tree/features/VBOverloadResolutionPriority) | In Progress | [AlekseyTs](https://github.com/AlekseyTs) | [333fred](https://github.com/333fred), [cston](https://github.com/cston) | (no IDE impact) | [333fred](https://github.com/333fred) | # C# 13.0 From 50a5a2f74c74b5f58f2d1fe776b19a979bdb1e7e Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Thu, 21 Nov 2024 13:06:25 +0100 Subject: [PATCH 462/508] Prefer array to ReadOnlySpan over Span (#75853) * Prefer array to ROS over Span * Update tests * Add more tests * Test ambiguity --- .../OverloadResolution/OverloadResolution.cs | 13 ++ .../CSharp/Test/Emit3/FirstClassSpanTests.cs | 162 +++++++++++++++++- 2 files changed, 169 insertions(+), 6 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs index 1b0f4d68ed4eb..1c8f97db8f811 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs @@ -2980,6 +2980,19 @@ private BetterResult BetterConversionFromExpression(BoundExpression node, TypeSy switch ((conv1.Kind, conv2.Kind)) { case (ConversionKind.ImplicitSpan, ConversionKind.ImplicitSpan): + // If the expression is of an array type, prefer ReadOnlySpan over Span (to avoid ArrayTypeMismatchExceptions). + if (node.Type is ArrayTypeSymbol) + { + if (t1.IsReadOnlySpan() && t2.IsSpan()) + { + return BetterResult.Left; + } + + if (t1.IsSpan() && t2.IsReadOnlySpan()) + { + return BetterResult.Right; + } + } break; case (_, ConversionKind.ImplicitSpan): return BetterResult.Right; diff --git a/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs b/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs index c97ea2eba9aca..0bf1e55bfbc15 100644 --- a/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs @@ -458,6 +458,37 @@ static class C CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); } + [Fact] + public void BreakingChange_TypeInference_SpanVsIEnumerable_02_BothSpanAndReadOnlySpan() + { + var source = """ + using System; + using System.Collections.Generic; + + string[] s = new[] { "a" }; + object[] o = s; + + C.R(o); + + static class C + { + public static void R(IEnumerable e) => Console.Write(1); + public static void R(ReadOnlySpan s) => Console.Write(2); + public static void R(Span s) => Console.Write(3); + } + """; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13); + CompileAndVerify(comp, expectedOutput: "1").VerifyDiagnostics(); + + var expectedOutput = "2"; + + comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateCompilationWithSpanAndMemoryExtensions(source); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + [Fact] public void BreakingChange_Conversion_SpanVsIEnumerable() { @@ -518,6 +549,37 @@ static class E CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); } + [Fact] + public void BreakingChange_Conversion_SpanVsIEnumerable_BothSpanAndReadOnlySpan() + { + var source = """ + using System; + using System.Collections.Generic; + + string[] s = new[] { "a" }; + object[] o = s; + + o.R(); + + static class E + { + public static void R(this IEnumerable e) => Console.Write(1); + public static void R(this ReadOnlySpan s) => Console.Write(2); + public static void R(this Span s) => Console.Write(3); + } + """; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13); + CompileAndVerify(comp, expectedOutput: "1").VerifyDiagnostics(); + + var expectedOutput = "2"; + + comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateCompilationWithSpanAndMemoryExtensions(source); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + [Fact] public void BreakingChange_Conversion_SpanVsArray() { @@ -631,6 +693,36 @@ static class C CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics(expectedDiagnostics); } + [Fact] + public void BreakingChange_OverloadResolution_Betterness_01() + { + var source = """ + using System; + using System.Collections.Generic; + + var x = new int[0]; + C.M(x, x); + + static class C + { + public static void M(IEnumerable a, ReadOnlySpan b) => Console.Write(1); + public static void M(Span a, Span b) => Console.Write(2); + } + """; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13); + CompileAndVerify(comp, expectedOutput: "2").VerifyDiagnostics(); + + var expectedDiagnostics = new[] + { + // (5,3): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(IEnumerable, ReadOnlySpan)' and 'C.M(Span, Span)' + // C.M(x, x); + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M(System.Collections.Generic.IEnumerable, System.ReadOnlySpan)", "C.M(System.Span, System.Span)").WithLocation(5, 3) + }; + + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics(expectedDiagnostics); + } + [Theory, CombinatorialData] public void Conversion_Array_Span_Implicit( [CombinatorialLangVersions] LanguageVersion langVersion, @@ -7857,8 +7949,8 @@ static class C CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); } - [Theory, MemberData(nameof(LangVersions))] - public void OverloadResolution_SpanVsReadOnlySpan_01(LanguageVersion langVersion) + [Fact] + public void OverloadResolution_SpanVsReadOnlySpan_01() { var source = """ using System; @@ -7873,8 +7965,16 @@ static class C public static void M(ReadOnlySpan arg) => Console.Write(2); } """; - var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13); CompileAndVerify(comp, expectedOutput: "112").VerifyDiagnostics(); + + var expectedOutput = "212"; + + comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateCompilationWithSpanAndMemoryExtensions(source); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); } [Fact] @@ -7898,7 +7998,7 @@ static class C var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13); CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsCoreClr ? "1123" : "1121").VerifyDiagnostics(); - var expectedOutput = "1122"; + var expectedOutput = "2122"; comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext); CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); @@ -7939,6 +8039,31 @@ static class C CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); } + [Fact] + public void OverloadResolution_SpanVsReadOnlySpan_04() + { + var source = """ + using System; + + C.M1(new object[0]); + C.M1(new string[0]); + + C.M2(new object[0]); + C.M2(new string[0]); + + static class C + { + public static void M1(Span arg) => Console.Write(1); + public static void M1(ReadOnlySpan arg) => Console.Write(2); + + public static void M2(Span arg) => Console.Write(1); + public static void M2(ReadOnlySpan arg) => Console.Write(2); + } + """; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source); + CompileAndVerify(comp, expectedOutput: "2212").VerifyDiagnostics(); + } + [Fact] public void OverloadResolution_SpanVsReadOnlySpan_ExtensionMethodReceiver_01() { @@ -7958,7 +8083,7 @@ static class C // (new int[0]).E(); Diagnostic(ErrorCode.ERR_BadInstanceArgType, "new int[0]").WithArguments("int[]", "E", "C.E(System.Span)", "System.Span").WithLocation(3, 2)); - var expectedOutput = "1"; + var expectedOutput = "2"; var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext); CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); @@ -8009,7 +8134,7 @@ static class C // (new string[0]).E(); Diagnostic(ErrorCode.ERR_BadInstanceArgType, "new object[0]").WithArguments("object[]", "E", "C.E(System.Span)", "System.Span").WithLocation(4, 2)); - var expectedOutput = "21"; + var expectedOutput = "22"; var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext); CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); @@ -8050,6 +8175,31 @@ static class C CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); } + [Fact] + public void OverloadResolution_SpanVsReadOnlySpan_ExtensionMethodReceiver_05() + { + var source = """ + using System; + + (new object[0]).M1(); + (new string[0]).M1(); + + (new object[0]).M2(); + (new string[0]).M2(); + + static class E + { + public static void M1(this Span arg) => Console.Write(1); + public static void M1(this ReadOnlySpan arg) => Console.Write(2); + + public static void M2(this Span arg) => Console.Write(1); + public static void M2(this ReadOnlySpan arg) => Console.Write(2); + } + """; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source); + CompileAndVerify(comp, expectedOutput: "2212").VerifyDiagnostics(); + } + [Fact] public void OverloadResolution_ReadOnlySpanVsReadOnlySpan() { From 6d7ee383c3cbfddace95bc2da4eeb9582ec632ee Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Fri, 15 Nov 2024 14:17:54 +0100 Subject: [PATCH 463/508] Update .NET 9 runtime for single machine CI job (#75889) * Update .NET 9 runtime for single machine CI job * Use regular pool --- azure-pipelines.yml | 13 ++++++++++++- eng/pipelines/test-windows-job-single-machine.yml | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 39bf2f2c610a1..0f26c4fff3483 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -53,6 +53,12 @@ variables: ${{ else }}: value: windows.vs2022preview.amd64 + - name: Vs2022RegularQueueName + ${{ if eq(variables['System.TeamProject'], 'public') }}: + value: windows.vs2022.amd64.open + ${{ else }}: + value: windows.vs2022.amd64 + - name: UbuntuQueueName ${{ if eq(variables['System.TeamProject'], 'public') }}: value: Build.Ubuntu.2004.Amd64.Open @@ -107,6 +113,11 @@ parameters: default: name: $(PoolName) demands: ImageOverride -equals $(Vs2022PreviewQueueName) + - name: vs2022RegularPool + type: object + default: + name: $(PoolName) + demands: ImageOverride -equals $(Vs2022RegularQueueName) stages: - stage: Windows_Debug_Build @@ -271,7 +282,7 @@ stages: jobName: Test_Windows_CoreClr_Debug_Single_Machine testArtifactName: Transport_Artifacts_Windows_Debug configuration: Debug - poolParameters: ${{ parameters.vs2022PreviewPool }} + poolParameters: ${{ parameters.vs2022RegularPool }} testArguments: -testCoreClr - template: eng/pipelines/test-windows-job.yml diff --git a/eng/pipelines/test-windows-job-single-machine.yml b/eng/pipelines/test-windows-job-single-machine.yml index 75acc4af38da4..975bdb744f2f0 100644 --- a/eng/pipelines/test-windows-job-single-machine.yml +++ b/eng/pipelines/test-windows-job-single-machine.yml @@ -33,7 +33,7 @@ jobs: displayName: 'Install .NET 9 Runtime' inputs: packageType: runtime - version: 9.0.0-preview.3.24172.9 + version: 9.0.0-rc.2.24473.5 includePreviewVersions: true installationPath: '$(Build.SourcesDirectory)/.dotnet' From 5da7e94dd14e7f5338e3be8d27944a01f8822b10 Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Thu, 21 Nov 2024 09:11:23 -0800 Subject: [PATCH 464/508] Add a couple common type sizes to SegmentedArrayHelper's size/shift/offsetmask methods so those values can be inlined. (#75944) This isn't super significant, but about 175ms of CPU are accounted for these methods in the scrolling speedometer test in our OOP. I believe those should go away completely as these are the only common values that weren't already being handled when I manually debugged the product. --- .../Collections/SegmentedArrayHelperTests.cs | 8 ++++++++ .../Collections/Internal/SegmentedArrayHelper.cs | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/src/Compilers/Core/CodeAnalysisTest/Collections/SegmentedArrayHelperTests.cs b/src/Compilers/Core/CodeAnalysisTest/Collections/SegmentedArrayHelperTests.cs index 6580ae148a424..b9796628782d4 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Collections/SegmentedArrayHelperTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Collections/SegmentedArrayHelperTests.cs @@ -17,6 +17,9 @@ namespace Microsoft.CodeAnalysis.UnitTests.Collections { public class SegmentedArrayHelperTests { + [StructLayout(LayoutKind.Sequential, Size = 1)] + private struct Size1 { } + [StructLayout(LayoutKind.Sequential, Size = 2)] private struct Size2 { } @@ -44,10 +47,14 @@ private struct Size32 { } [StructLayout(LayoutKind.Sequential, Size = 40)] private struct Size40 { } + [StructLayout(LayoutKind.Sequential, Size = 64)] + private struct Size64 { } + public static IEnumerable ExplicitSizeTypes { get { + yield return new object[] { typeof(Size1) }; yield return new object[] { typeof(Size2) }; yield return new object[] { typeof(Size4) }; yield return new object[] { typeof(Size8) }; @@ -57,6 +64,7 @@ public static IEnumerable ExplicitSizeTypes yield return new object[] { typeof(Size28) }; yield return new object[] { typeof(Size32) }; yield return new object[] { typeof(Size40) }; + yield return new object[] { typeof(Size64) }; } } diff --git a/src/Dependencies/Collections/Internal/SegmentedArrayHelper.cs b/src/Dependencies/Collections/Internal/SegmentedArrayHelper.cs index c9ca2d2d31f19..87caa4c74b574 100644 --- a/src/Dependencies/Collections/Internal/SegmentedArrayHelper.cs +++ b/src/Dependencies/Collections/Internal/SegmentedArrayHelper.cs @@ -23,6 +23,8 @@ internal static int GetSegmentSize() // Hard code common values since not all versions of the .NET JIT support reducing this computation to a // constant value at runtime. Values are validated against the reference implementation in // CalculateSegmentSize in unit tests. + 1 => 65536, + 2 => 32768, 4 => 16384, 8 => 8192, 12 => 4096, @@ -31,6 +33,7 @@ internal static int GetSegmentSize() 28 => 2048, 32 => 2048, 40 => 2048, + 64 => 1024, #if NETCOREAPP3_0_OR_GREATER _ => InlineCalculateSegmentSize(Unsafe.SizeOf()), #else @@ -47,6 +50,8 @@ internal static int GetSegmentShift() // Hard code common values since not all versions of the .NET JIT support reducing this computation to a // constant value at runtime. Values are validated against the reference implementation in // CalculateSegmentSize in unit tests. + 1 => 16, + 2 => 15, 4 => 14, 8 => 13, 12 => 12, @@ -55,6 +60,7 @@ internal static int GetSegmentShift() 28 => 11, 32 => 11, 40 => 11, + 64 => 10, #if NETCOREAPP3_0_OR_GREATER _ => InlineCalculateSegmentShift(Unsafe.SizeOf()), #else @@ -71,6 +77,8 @@ internal static int GetOffsetMask() // Hard code common values since not all versions of the .NET JIT support reducing this computation to a // constant value at runtime. Values are validated against the reference implementation in // CalculateSegmentSize in unit tests. + 1 => 65535, + 2 => 32767, 4 => 16383, 8 => 8191, 12 => 4095, @@ -79,6 +87,7 @@ internal static int GetOffsetMask() 28 => 2047, 32 => 2047, 40 => 2047, + 64 => 1023, #if NETCOREAPP3_0_OR_GREATER _ => InlineCalculateOffsetMask(Unsafe.SizeOf()), #else From b8bc872e5cf7fec78b56febae19499b6f4447ae9 Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Thu, 21 Nov 2024 09:11:37 -0800 Subject: [PATCH 465/508] Reuse segments during SegmentedDictionary growth (#75943) * Reuse segments during SegmentedDictionary growth Mimics changes done in https://github.com/dotnet/roslyn/pull/75661 to enable segment reuse in SegmentedDictionary. --- .../Collections/SegmentedDictionary`2.cs | 28 +++++- .../Collections/SegmentedHashSet`1.cs | 28 +++++- .../Collections/SegmentedList`1.cs | 35 ++++--- .../SegmentedDictionaryBenchmarks_Add.cs | 94 +++++++++++++++++++ 4 files changed, 164 insertions(+), 21 deletions(-) create mode 100644 src/Tools/IdeCoreBenchmarks/SegmentedDictionaryBenchmarks_Add.cs diff --git a/src/Dependencies/Collections/SegmentedDictionary`2.cs b/src/Dependencies/Collections/SegmentedDictionary`2.cs index 6a46b472a4cc8..75834b2f50781 100644 --- a/src/Dependencies/Collections/SegmentedDictionary`2.cs +++ b/src/Dependencies/Collections/SegmentedDictionary`2.cs @@ -646,10 +646,10 @@ private void Resize(int newSize) Debug.Assert(_entries.Length > 0, "_entries should be non-empty"); Debug.Assert(newSize >= _entries.Length); - var entries = new SegmentedArray(newSize); - var count = _count; - SegmentedArray.Copy(_entries, entries, count); + + // Rather than creating a copy of _entries, instead reuse as much of it's data as possible. + var entries = CreateNewSegmentedArrayReusingOldSegments(_entries, newSize); // Assign member variables after both arrays allocated to guard against corruption from OOM if second fails _buckets = new SegmentedArray(newSize); @@ -667,6 +667,28 @@ private void Resize(int newSize) _entries = entries; } + private static SegmentedArray CreateNewSegmentedArrayReusingOldSegments(SegmentedArray oldArray, int newSize) + { + var segments = SegmentedCollectionsMarshal.AsSegments(oldArray); + + var oldSegmentCount = segments.Length; + var newSegmentCount = (newSize + SegmentedArrayHelper.GetSegmentSize() - 1) >> SegmentedArrayHelper.GetSegmentShift(); + + // Grow the array of segments, if necessary + Array.Resize(ref segments, newSegmentCount); + + // Resize all segments to full segment size from the last old segment to the next to last + // new segment. + for (var i = oldSegmentCount - 1; i < newSegmentCount - 1; i++) + Array.Resize(ref segments[i], SegmentedArrayHelper.GetSegmentSize()); + + // Resize the last segment + var lastSegmentSize = newSize - ((newSegmentCount - 1) << SegmentedArrayHelper.GetSegmentShift()); + Array.Resize(ref segments[newSegmentCount - 1], lastSegmentSize); + + return SegmentedCollectionsMarshal.AsSegmentedArray(newSize, segments); + } + public bool Remove(TKey key) { // The overload Remove(TKey key, out TValue value) is a copy of this method with one additional diff --git a/src/Dependencies/Collections/SegmentedHashSet`1.cs b/src/Dependencies/Collections/SegmentedHashSet`1.cs index c8792b3e9f5ed..080048041e5ba 100644 --- a/src/Dependencies/Collections/SegmentedHashSet`1.cs +++ b/src/Dependencies/Collections/SegmentedHashSet`1.cs @@ -899,10 +899,10 @@ private void Resize(int newSize) Debug.Assert(_entries.Length > 0, "_entries should be non-empty"); Debug.Assert(newSize >= _entries.Length); - var entries = new SegmentedArray(newSize); - var count = _count; - SegmentedArray.Copy(_entries, entries, count); + + // Rather than creating a copy of _entries, instead reuse as much of it's data as possible. + var entries = CreateNewSegmentedArrayReusingOldSegments(_entries, newSize); // Assign member variables after both arrays allocated to guard against corruption from OOM if second fails _buckets = new SegmentedArray(newSize); @@ -921,6 +921,28 @@ private void Resize(int newSize) _entries = entries; } + private static SegmentedArray CreateNewSegmentedArrayReusingOldSegments(SegmentedArray oldArray, int newSize) + { + var segments = SegmentedCollectionsMarshal.AsSegments(oldArray); + + var oldSegmentCount = segments.Length; + var newSegmentCount = (newSize + SegmentedArrayHelper.GetSegmentSize() - 1) >> SegmentedArrayHelper.GetSegmentShift(); + + // Grow the array of segments, if necessary + Array.Resize(ref segments, newSegmentCount); + + // Resize all segments to full segment size from the last old segment to the next to last + // new segment. + for (var i = oldSegmentCount - 1; i < newSegmentCount - 1; i++) + Array.Resize(ref segments[i], SegmentedArrayHelper.GetSegmentSize()); + + // Resize the last segment + var lastSegmentSize = newSize - ((newSegmentCount - 1) << SegmentedArrayHelper.GetSegmentShift()); + Array.Resize(ref segments[newSegmentCount - 1], lastSegmentSize); + + return SegmentedCollectionsMarshal.AsSegmentedArray(newSize, segments); + } + /// /// Sets the capacity of a object to the actual number of elements it contains, /// rounded up to a nearby, implementation-specific value. diff --git a/src/Dependencies/Collections/SegmentedList`1.cs b/src/Dependencies/Collections/SegmentedList`1.cs index 9e6a45a8ede98..35118b06966ad 100644 --- a/src/Dependencies/Collections/SegmentedList`1.cs +++ b/src/Dependencies/Collections/SegmentedList`1.cs @@ -148,26 +148,31 @@ public int Capacity else { // Rather than creating a copy of _items, instead reuse as much of it's data as possible. - var segments = SegmentedCollectionsMarshal.AsSegments(_items); + _items = CreateNewSegmentedArrayReusingOldSegments(_items, value); + } + } + } - var oldSegmentCount = segments.Length; - var newSegmentCount = (value + SegmentedArrayHelper.GetSegmentSize() - 1) >> SegmentedArrayHelper.GetSegmentShift(); + private static SegmentedArray CreateNewSegmentedArrayReusingOldSegments(SegmentedArray oldArray, int newSize) + { + var segments = SegmentedCollectionsMarshal.AsSegments(oldArray); - // Grow the array of segments, if necessary - Array.Resize(ref segments, newSegmentCount); + var oldSegmentCount = segments.Length; + var newSegmentCount = (newSize + SegmentedArrayHelper.GetSegmentSize() - 1) >> SegmentedArrayHelper.GetSegmentShift(); - // Resize all segments to full segment size from the last old segment to the next to last - // new segment. - for (var i = oldSegmentCount - 1; i < newSegmentCount - 1; i++) - Array.Resize(ref segments[i], SegmentedArrayHelper.GetSegmentSize()); + // Grow the array of segments, if necessary + Array.Resize(ref segments, newSegmentCount); - // Resize the last segment - var lastSegmentSize = value - ((newSegmentCount - 1) << SegmentedArrayHelper.GetSegmentShift()); - Array.Resize(ref segments[newSegmentCount - 1], lastSegmentSize); + // Resize all segments to full segment size from the last old segment to the next to last + // new segment. + for (var i = oldSegmentCount - 1; i < newSegmentCount - 1; i++) + Array.Resize(ref segments[i], SegmentedArrayHelper.GetSegmentSize()); - _items = SegmentedCollectionsMarshal.AsSegmentedArray(value, segments); - } - } + // Resize the last segment + var lastSegmentSize = newSize - ((newSegmentCount - 1) << SegmentedArrayHelper.GetSegmentShift()); + Array.Resize(ref segments[newSegmentCount - 1], lastSegmentSize); + + return SegmentedCollectionsMarshal.AsSegmentedArray(newSize, segments); } // Read-only property describing how many elements are in the SegmentedList. diff --git a/src/Tools/IdeCoreBenchmarks/SegmentedDictionaryBenchmarks_Add.cs b/src/Tools/IdeCoreBenchmarks/SegmentedDictionaryBenchmarks_Add.cs new file mode 100644 index 0000000000000..15b1b9984127b --- /dev/null +++ b/src/Tools/IdeCoreBenchmarks/SegmentedDictionaryBenchmarks_Add.cs @@ -0,0 +1,94 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using BenchmarkDotNet.Attributes; +using Microsoft.CodeAnalysis.Collections; + +namespace IdeCoreBenchmarks +{ + [MemoryDiagnoser] + public class SegmentedDictionaryBenchmarks_Add + { + [Params(1_000, 10_000, 100_000, 1_000_000)] + public int Count { get; set; } + + private int[]? _intItems; + private object[]? _objectItems; + private LargeStruct[]? _largeItems; + private EnormousStruct[]? _enormousItems; + + [IterationSetup] + public void IterationSetup() + { + _intItems = new int[Count]; + _objectItems = new object[Count]; + _largeItems = new LargeStruct[Count]; + _enormousItems = new EnormousStruct[Count]; + + for (var i = 0; i < Count; i++) + { + _intItems[i] = i; + _objectItems[i] = new object(); + _largeItems[i] = new LargeStruct() { s1 = new MediumStruct() { i1 = i } }; + _enormousItems[i] = new EnormousStruct() { s1 = _largeItems[i] }; + } + } + + [Benchmark] + public void AddIntToList() + => AddToList(_intItems!); + + [Benchmark] + public void AddObjectToList() + => AddToList(_objectItems!); + + [Benchmark] + public void AddLargeStructToList() + => AddToList(_largeItems!); + + [Benchmark] + public void AddEnormousStructToList() + => AddToList(_enormousItems!); + + private void AddToList(T[] items) where T : notnull + { + var dict = new SegmentedDictionary(); + var iterations = Count; + + for (var i = 0; i < iterations; i++) + dict.Add(items[i], items[i]); + } + + private struct MediumStruct + { + public int i1 { get; set; } + public int i2 { get; set; } + public int i3 { get; set; } + public int i4 { get; set; } + public int i5 { get; set; } + } + + private struct LargeStruct + { + public MediumStruct s1 { get; set; } + public MediumStruct s2 { get; set; } + public MediumStruct s3 { get; set; } + public MediumStruct s4 { get; set; } + } + + private struct EnormousStruct + { + public LargeStruct s1 { get; set; } + public LargeStruct s2 { get; set; } + public LargeStruct s3 { get; set; } + public LargeStruct s4 { get; set; } + public LargeStruct s5 { get; set; } + public LargeStruct s6 { get; set; } + public LargeStruct s7 { get; set; } + public LargeStruct s8 { get; set; } + public LargeStruct s9 { get; set; } + public LargeStruct s10 { get; set; } + } + } +} From 12070440b1031e7b902b6163a424a63ace482b8f Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Thu, 21 Nov 2024 11:05:58 -0800 Subject: [PATCH 466/508] Use VisitRvalue to visit a function pointer (#75996) --- .../Portable/FlowAnalysis/AbstractFlowPass.cs | 2 +- .../Semantics/FunctionPointerTests.cs | 49 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index 746a32bf7c2d6..fb8ad0ddae840 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -3741,7 +3741,7 @@ public override BoundNode VisitReadOnlySpanFromArray(BoundReadOnlySpanFromArray public override BoundNode VisitFunctionPointerInvocation(BoundFunctionPointerInvocation node) { - Visit(node.InvokedExpression); + VisitRvalue(node.InvokedExpression); VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, node.FunctionPointer.Signature); return null; } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs index aee69356d07eb..f6e980d11d3e9 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs @@ -4152,5 +4152,54 @@ public unsafe void M(delegate*? f) { Diagnostic(ErrorCode.ERR_BadTypeArgument, "f").WithArguments("delegate*").WithLocation(5, 43) ); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75933")] + public void BoolLiteralInvocation_01() + { + var source = """ + #nullable enable + + class C + { + static unsafe void M() + { + int i = 0; + ((delegate*)true)(i); + } + } + """; + + var comp = CreateCompilationWithFunctionPointers(source); + comp.VerifyEmitDiagnostics( + // (8,10): error CS0030: Cannot convert type 'bool' to 'delegate*' + // ((delegate*)true)(i); + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(delegate*)true").WithArguments("bool", "delegate*").WithLocation(8, 10) + ); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75933")] + public void BoolLiteralInvocation_02() + { + var source = """ + #nullable enable + + using System; + + class C + { + static unsafe void M() + { + var d = delegate (object? o, IntPtr f) { return ((delegate* managed)false)(o); }; + } + } + """; + + var comp = CreateCompilationWithFunctionPointers(source); + comp.VerifyEmitDiagnostics( + // (9,58): error CS0030: Cannot convert type 'bool' to 'delegate*' + // var d = delegate (object? o, IntPtr f) { return ((delegate* managed)false)(o); }; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(delegate* managed)false").WithArguments("bool", "delegate*").WithLocation(9, 58) + ); + } } } From 9b2cd5d86601d6125718feb6d5736d10b6671b24 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Thu, 21 Nov 2024 11:13:51 -0800 Subject: [PATCH 467/508] Check for BackingField instead of IsAutoProperty in NullableWalker (#75872) --- .../Portable/FlowAnalysis/NullableWalker.cs | 2 +- .../Symbol/Symbols/RequiredMembersTests.cs | 20 +++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 1bb06799f1329..96a50c5f7daf6 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -971,7 +971,7 @@ static IEnumerable getAllMembersToBeDefaulted(Symbol requiredMember) } static Symbol getFieldSymbolToBeInitialized(Symbol requiredMember) - => requiredMember is SourcePropertySymbol { IsAutoProperty: true } prop ? prop.BackingField : requiredMember; + => requiredMember is SourcePropertySymbolBase { BackingField: { } backingField } ? backingField : requiredMember; } } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs index 63df7b20d52b8..4f68a2f3315f3 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs @@ -4517,17 +4517,19 @@ public C(bool unused) : this() ); } - [Fact, CompilerTrait(CompilerFeature.NullableReferenceTypes)] + [Theory, CompilerTrait(CompilerFeature.NullableReferenceTypes)] [WorkItem(6754, "https://github.com/dotnet/csharplang/issues/6754")] - public void RequiredMemberSuppressesNullabilityWarnings_MemberNotNull_ChainedConstructor_03() + [InlineData("public string Property2 { get; set; }")] + [InlineData("public string Property2 { get => field; set => field = value; }")] + public void RequiredMemberSuppressesNullabilityWarnings_MemberNotNull_ChainedConstructor_03(string property2Definition) { - var code = """ + var code = $$""" using System.Diagnostics.CodeAnalysis; #nullable enable class C { public required string Property1 { get => Property2; [MemberNotNull(nameof(Property2))] set => Property2 = value; } - public string Property2 { get; set; } + {{property2Definition}} public C() { } public C(bool unused) : this() @@ -4549,17 +4551,19 @@ public C(bool unused) : this() ); } - [Fact, CompilerTrait(CompilerFeature.NullableReferenceTypes)] + [Theory, CompilerTrait(CompilerFeature.NullableReferenceTypes)] [WorkItem(6754, "https://github.com/dotnet/csharplang/issues/6754")] - public void RequiredMemberSuppressesNullabilityWarnings_MemberNotNull_ChainedConstructor_04() + [InlineData("public string Property2 { get; set; }")] + [InlineData("public string Property2 { get => field; set => field = value; }")] + public void RequiredMemberSuppressesNullabilityWarnings_MemberNotNull_ChainedConstructor_04(string property2Definition) { - var code = """ + var code = $$""" using System.Diagnostics.CodeAnalysis; #nullable enable class C { public required string Property1 { get => Property2; [MemberNotNull(nameof(Property2))] set => Property2 = value; } - public string Property2 { get; set; } + {{property2Definition}} public C() { } [SetsRequiredMembers] From b07c3e4f7b718e4b09c13c3daa72eb5c3a21370c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 21 Nov 2024 12:09:16 -0800 Subject: [PATCH 468/508] Add test --- .../RemoveUnusedMembersTests.cs | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs index d7ab7dbffa2a7..22ae60fc1dbd9 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs @@ -3271,4 +3271,67 @@ static IEnumerator GetEnumerator(this Range range) ReferenceAssemblies = ReferenceAssemblies.Net.Net90, }.RunAsync(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75995")] + public async Task KeepUsedDeconstructMethod() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + void M( + ref object o, + ref object p) + { + (o, p) = this; + } + + void Deconstruct( + out object? o, + out object? p) + { + o = null; + p = null; + } + } + """, + }.RunAsync(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75995")] + public async Task RemoveUnusedDeconstructMethod() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + void M( + ref object o, + ref object p) + { + } + + void [|Deconstruct|]( + out object? o, + out object? p) + { + o = null; + p = null; + } + } + """, + FixedCode = """ + class C + { + void M( + ref object o, + ref object p) + { + } + } + """, + }.RunAsync(); + } } From 0d1847fada4186bac1ead3d1ecf6975598aacbc8 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 21 Nov 2024 12:23:42 -0800 Subject: [PATCH 469/508] Update test --- .../RemoveUnusedMembersTests.cs | 8 ++++++-- ...bstractRemoveUnusedMembersDiagnosticAnalyzer.cs | 14 ++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs index 22ae60fc1dbd9..7587c41b36fd6 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs @@ -3305,9 +3305,11 @@ public async Task RemoveUnusedDeconstructMethod() await new VerifyCS.Test { TestCode = """ + #nullable enable + class C { - void M( + public void M( ref object o, ref object p) { @@ -3323,9 +3325,11 @@ void M( } """, FixedCode = """ + #nullable enable + class C { - void M( + public void M( ref object o, ref object p) { diff --git a/src/Analyzers/Core/Analyzers/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs index e947de09d2524..412f9a548d9d2 100644 --- a/src/Analyzers/Core/Analyzers/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs @@ -198,7 +198,6 @@ private void RegisterActions(CompilationStartAnalysisContext compilationStartCon if (!ShouldAnalyze(symbolStartContext, (INamedTypeSymbol)symbolStartContext.Symbol)) return; - var hasUnsupportedOperation = false; symbolStartContext.RegisterOperationAction(AnalyzeMemberReferenceOperation, OperationKind.FieldReference, OperationKind.MethodReference, OperationKind.PropertyReference, OperationKind.EventReference); symbolStartContext.RegisterOperationAction(AnalyzeFieldInitializer, OperationKind.FieldInitializer); symbolStartContext.RegisterOperationAction(AnalyzeInvocationOperation, OperationKind.Invocation); @@ -207,11 +206,13 @@ private void RegisterActions(CompilationStartAnalysisContext compilationStartCon symbolStartContext.RegisterOperationAction(AnalyzeLoopOperation, OperationKind.Loop); // We bail out reporting diagnostics for named types if it contains following kind of operations: - // 1. Invalid operations, i.e. erroneous code: - // We do so to ensure that we don't report false positives during editing scenarios in the IDE, where the user - // is still editing code and fixing unresolved references to symbols, such as overload resolution errors. + // 1. Invalid operations, i.e. erroneous code: We do so to ensure that we don't report false positives + // during editing scenarios in the IDE, where the user is still editing code and fixing unresolved + // references to symbols, such as overload resolution errors. // 2. Dynamic operations, where we do not know the exact member being referenced at compile time. // 3. Operations with OperationKind.None. + + var hasUnsupportedOperation = false; symbolStartContext.RegisterOperationAction( _ => hasUnsupportedOperation = true, OperationKind.Invalid, OperationKind.None, OperationKind.DynamicIndexerAccess, OperationKind.DynamicInvocation, OperationKind.DynamicMemberReference, OperationKind.DynamicObjectCreation); @@ -255,10 +256,7 @@ private void AnalyzeSymbolDeclaration(SymbolAnalysisContext symbolContext) // Note that we might receive a symbol reference (AnalyzeMemberOperation) callback before // this symbol declaration callback, so even though we cannot receive duplicate callbacks for a symbol, // an entry might already be present of the declared symbol here. - if (!_symbolValueUsageStateMap.ContainsKey(symbol)) - { - _symbolValueUsageStateMap.Add(symbol, ValueUsageInfo.None); - } + _symbolValueUsageStateMap.TryAdd(symbol, ValueUsageInfo.None); } } } From 319b6221b28363fbef70438f746ebe6b4978c87e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 21 Nov 2024 12:42:42 -0800 Subject: [PATCH 470/508] Mark used Deconstruct methods --- .../RemoveUnusedMembers/RemoveUnusedMembersTests.cs | 8 +++++--- ...AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs | 13 +++++++++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs index 7587c41b36fd6..204bc44c63787 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs @@ -3278,11 +3278,13 @@ public async Task KeepUsedDeconstructMethod() await new VerifyCS.Test { TestCode = """ + #nullable enable + class C { - void M( - ref object o, - ref object p) + public void M( + ref object? o, + ref object? p) { (o, p) = this; } diff --git a/src/Analyzers/Core/Analyzers/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs index 412f9a548d9d2..0e719110d813d 100644 --- a/src/Analyzers/Core/Analyzers/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs @@ -198,12 +198,13 @@ private void RegisterActions(CompilationStartAnalysisContext compilationStartCon if (!ShouldAnalyze(symbolStartContext, (INamedTypeSymbol)symbolStartContext.Symbol)) return; - symbolStartContext.RegisterOperationAction(AnalyzeMemberReferenceOperation, OperationKind.FieldReference, OperationKind.MethodReference, OperationKind.PropertyReference, OperationKind.EventReference); + symbolStartContext.RegisterOperationAction(AnalyzeDeconstructionAssignment, OperationKind.DeconstructionAssignment); symbolStartContext.RegisterOperationAction(AnalyzeFieldInitializer, OperationKind.FieldInitializer); symbolStartContext.RegisterOperationAction(AnalyzeInvocationOperation, OperationKind.Invocation); + symbolStartContext.RegisterOperationAction(AnalyzeLoopOperation, OperationKind.Loop); + symbolStartContext.RegisterOperationAction(AnalyzeMemberReferenceOperation, OperationKind.FieldReference, OperationKind.MethodReference, OperationKind.PropertyReference, OperationKind.EventReference); symbolStartContext.RegisterOperationAction(AnalyzeNameOfOperation, OperationKind.NameOf); symbolStartContext.RegisterOperationAction(AnalyzeObjectCreationOperation, OperationKind.ObjectCreation); - symbolStartContext.RegisterOperationAction(AnalyzeLoopOperation, OperationKind.Loop); // We bail out reporting diagnostics for named types if it contains following kind of operations: // 1. Invalid operations, i.e. erroneous code: We do so to ensure that we don't report false positives @@ -261,6 +262,14 @@ private void AnalyzeSymbolDeclaration(SymbolAnalysisContext symbolContext) } } + private void AnalyzeDeconstructionAssignment(OperationAnalysisContext operationContext) + { + var operation = operationContext.Operation; + var methods = _analyzer.SemanticFacts.GetDeconstructionAssignmentMethods(operation.SemanticModel!, operation.Syntax); + foreach (var method in methods) + OnSymbolUsage(method, ValueUsageInfo.Read); + } + private void AnalyzeFieldInitializer(OperationAnalysisContext operationContext) { // Check if the initialized fields are being initialized a non-constant value. From 79811cdb3af01fefe5afb56cca2611d7cdc1d04c Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Thu, 21 Nov 2024 13:52:39 -0800 Subject: [PATCH 471/508] Clear hoisted locals when disposing iterator and async-iterator state machines (#75908) --- ...yncIteratorMethodToStateMachineRewriter.cs | 20 +- .../AsyncMethodToStateMachineRewriter.cs | 83 +- .../AsyncRewriter.AsyncIteratorRewriter.cs | 1 + .../Lowering/AsyncRewriter/AsyncRewriter.cs | 1 + .../IteratorMethodToStateMachineRewriter.cs | 10 +- .../IteratorRewriter/IteratorRewriter.cs | 1 + .../MethodToStateMachineRewriter.cs | 55 +- .../StateMachineRewriter.cs | 8 +- .../Emit/CodeGen/CodeGenAsyncIteratorTests.cs | 858 ++++++++++++- .../CodeGenDisplayClassOptimisationTests.cs | 25 +- .../Test/Emit/CodeGen/CodeGenIterators.cs | 1078 ++++++++++++++--- .../EditAndContinueStateMachineTests.cs | 14 +- .../Semantic/Semantics/UseSiteErrorTests.cs | 2 + 13 files changed, 1888 insertions(+), 268 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncIteratorMethodToStateMachineRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncIteratorMethodToStateMachineRewriter.cs index 51cd9ae093a78..496c04f657904 100644 --- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncIteratorMethodToStateMachineRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncIteratorMethodToStateMachineRewriter.cs @@ -55,13 +55,14 @@ internal AsyncIteratorMethodToStateMachineRewriter( FieldSymbol? instanceIdField, IReadOnlySet hoistedVariables, IReadOnlyDictionary nonReusableLocalProxies, + ImmutableArray nonReusableFieldsForCleanup, SynthesizedLocalOrdinalsDispenser synthesizedLocalOrdinals, ArrayBuilder stateMachineStateDebugInfoBuilder, VariableSlotAllocator? slotAllocatorOpt, int nextFreeHoistedLocalSlot, BindingDiagnosticBag diagnostics) : base(method, methodOrdinal, asyncMethodBuilderMemberCollection, F, - state, builder, instanceIdField, hoistedVariables, nonReusableLocalProxies, synthesizedLocalOrdinals, + state, builder, instanceIdField, hoistedVariables, nonReusableLocalProxies, nonReusableFieldsForCleanup, synthesizedLocalOrdinals, stateMachineStateDebugInfoBuilder, slotAllocatorOpt, nextFreeHoistedLocalSlot, diagnostics) { Debug.Assert(asyncIteratorInfo != null); @@ -88,11 +89,21 @@ internal AsyncIteratorMethodToStateMachineRewriter( return (asyncDispatch != null) ? F.Block(asyncDispatch, iteratorDispatch) : iteratorDispatch; } + + protected override BoundStatement GenerateCleanupForExit(ImmutableArray rootHoistedLocals) + { + // We need to clean nested hoisted local variables too (not just top-level ones) + // as they are not cleaned when exiting a block if we exit using a `yield break` + // or if the caller interrupts the enumeration after we reached a `yield return`. + // So we clean both top-level and nested hoisted local variables + return GenerateAllHoistedLocalsCleanup(); + } #nullable disable protected override BoundStatement GenerateSetResultCall() { // ... _exprReturnLabel: ... // ... this.state = FinishedState; ... + // ... hoisted locals cleanup ... // if (this.combinedTokens != null) { this.combinedTokens.Dispose(); this.combinedTokens = null; } // for enumerables only // _current = default; @@ -319,7 +330,12 @@ public override BoundNode VisitYieldBreakStatement(BoundYieldBreakStatement node protected override BoundStatement MakeAwaitPreamble() { - if (_asyncIteratorInfo.CurrentField.Type.IsManagedTypeNoUseSiteDiagnostics) + var useSiteInfo = new CompoundUseSiteInfo(F.Diagnostics, F.Compilation.Assembly); + var field = _asyncIteratorInfo.CurrentField; + bool isManaged = field.Type.IsManagedType(ref useSiteInfo); + F.Diagnostics.Add(field.GetFirstLocationOrNone(), useSiteInfo); + + if (isManaged) { // _current = default; return GenerateClearCurrent(); diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodToStateMachineRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodToStateMachineRewriter.cs index 43993b7fe05da..585ec65c29724 100644 --- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodToStateMachineRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodToStateMachineRewriter.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -72,12 +73,13 @@ internal AsyncMethodToStateMachineRewriter( FieldSymbol? instanceIdField, IReadOnlySet hoistedVariables, IReadOnlyDictionary nonReusableLocalProxies, + ImmutableArray nonReusableFieldsForCleanup, SynthesizedLocalOrdinalsDispenser synthesizedLocalOrdinals, ArrayBuilder stateMachineStateDebugInfoBuilder, VariableSlotAllocator? slotAllocatorOpt, int nextFreeHoistedLocalSlot, BindingDiagnosticBag diagnostics) - : base(F, method, state, instanceIdField, hoistedVariables, nonReusableLocalProxies, synthesizedLocalOrdinals, stateMachineStateDebugInfoBuilder, slotAllocatorOpt, nextFreeHoistedLocalSlot, diagnostics) + : base(F, method, state, instanceIdField, hoistedVariables, nonReusableLocalProxies, nonReusableFieldsForCleanup, synthesizedLocalOrdinals, stateMachineStateDebugInfoBuilder, slotAllocatorOpt, nextFreeHoistedLocalSlot, diagnostics) { _method = method; _asyncMethodBuilderMemberCollection = asyncMethodBuilderMemberCollection; @@ -155,7 +157,7 @@ internal void GenerateMoveNext(BoundStatement body, MethodSymbol moveNextMethod) // [body] rewrittenBody ), - F.CatchBlocks(GenerateExceptionHandling(exceptionLocal, rootScopeHoistedLocals))) + F.CatchBlocks(generateExceptionHandling(exceptionLocal, rootScopeHoistedLocals))) ); // ReturnLabel (for the rewritten return expressions in the user's method body) @@ -176,7 +178,7 @@ internal void GenerateMoveNext(BoundStatement body, MethodSymbol moveNextMethod) // The remaining code is hidden to hide the fact that it can run concurrently with the task's continuation } - bodyBuilder.Add(GenerateHoistedLocalsCleanup(rootScopeHoistedLocals)); + bodyBuilder.Add(GenerateCleanupForExit(rootScopeHoistedLocals)); bodyBuilder.Add(GenerateSetResultCall()); @@ -206,6 +208,42 @@ internal void GenerateMoveNext(BoundStatement body, MethodSymbol moveNextMethod) newBody = F.Instrument(newBody, instrumentation); F.CloseMethod(newBody); + return; + + BoundCatchBlock generateExceptionHandling(LocalSymbol exceptionLocal, ImmutableArray rootHoistedLocals) + { + // catch (Exception ex) + // { + // _state = finishedState; + // + // for each hoisted local: + // <>x__y = default + // + // builder.SetException(ex); OR if (this.combinedTokens != null) this.combinedTokens.Dispose(); _promiseOfValueOrEnd.SetException(ex); /* for async-iterator method */ + // return; + // } + + // _state = finishedState; + BoundStatement assignFinishedState = + F.ExpressionStatement(F.AssignmentExpression(F.Field(F.This(), stateField), F.Literal(StateMachineState.FinishedState))); + + // builder.SetException(ex); OR if (this.combinedTokens != null) this.combinedTokens.Dispose(); _promiseOfValueOrEnd.SetException(ex); + BoundStatement callSetException = GenerateSetExceptionCall(exceptionLocal); + + return new BoundCatchBlock( + F.Syntax, + ImmutableArray.Create(exceptionLocal), + F.Local(exceptionLocal), + exceptionLocal.Type, + exceptionFilterPrologueOpt: null, + exceptionFilterOpt: null, + body: F.Block( + assignFinishedState, // _state = finishedState; + GenerateCleanupForExit(rootHoistedLocals), + callSetException, // builder.SetException(ex); OR _promiseOfValueOrEnd.SetException(ex); + GenerateReturn(false)), // return; + isSynthesizedAsyncCatchAll: true); + } } protected virtual BoundStatement GenerateTopLevelTry(BoundBlock tryBlock, ImmutableArray catchBlocks) @@ -223,48 +261,13 @@ protected virtual BoundStatement GenerateSetResultCall() : ImmutableArray.Empty)); } - protected BoundCatchBlock GenerateExceptionHandling(LocalSymbol exceptionLocal, ImmutableArray hoistedLocals) - { - // catch (Exception ex) - // { - // _state = finishedState; - // - // for each hoisted local: - // <>x__y = default - // - // builder.SetException(ex); OR if (this.combinedTokens != null) this.combinedTokens.Dispose(); _promiseOfValueOrEnd.SetException(ex); /* for async-iterator method */ - // return; - // } - - // _state = finishedState; - BoundStatement assignFinishedState = - F.ExpressionStatement(F.AssignmentExpression(F.Field(F.This(), stateField), F.Literal(StateMachineState.FinishedState))); - - // builder.SetException(ex); OR if (this.combinedTokens != null) this.combinedTokens.Dispose(); _promiseOfValueOrEnd.SetException(ex); - BoundStatement callSetException = GenerateSetExceptionCall(exceptionLocal); - - return new BoundCatchBlock( - F.Syntax, - ImmutableArray.Create(exceptionLocal), - F.Local(exceptionLocal), - exceptionLocal.Type, - exceptionFilterPrologueOpt: null, - exceptionFilterOpt: null, - body: F.Block( - assignFinishedState, // _state = finishedState; - GenerateHoistedLocalsCleanup(hoistedLocals), - callSetException, // builder.SetException(ex); OR _promiseOfValueOrEnd.SetException(ex); - GenerateReturn(false)), // return; - isSynthesizedAsyncCatchAll: true); - } - - protected BoundStatement GenerateHoistedLocalsCleanup(ImmutableArray hoistedLocals) + protected virtual BoundStatement GenerateCleanupForExit(ImmutableArray rootHoistedLocals) { var builder = ArrayBuilder.GetInstance(); // Cleanup all hoisted local variables // so that they can be collected by GC if needed - foreach (var hoistedLocal in hoistedLocals) + foreach (var hoistedLocal in rootHoistedLocals) { var useSiteInfo = new CompoundUseSiteInfo(F.Diagnostics, F.Compilation.Assembly); var isManagedType = hoistedLocal.Type.IsManagedType(ref useSiteInfo); diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.AsyncIteratorRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.AsyncIteratorRewriter.cs index 6bde60bf03281..82a95a335b015 100644 --- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.AsyncIteratorRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.AsyncIteratorRewriter.cs @@ -703,6 +703,7 @@ protected override void GenerateMoveNext(SynthesizedImplementationMethod moveNex instanceIdField: instanceIdField, hoistedVariables: hoistedVariables, nonReusableLocalProxies: nonReusableLocalProxies, + nonReusableFieldsForCleanup: nonReusableFieldsForCleanup, synthesizedLocalOrdinals: synthesizedLocalOrdinals, stateMachineStateDebugInfoBuilder, slotAllocatorOpt: slotAllocatorOpt, diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.cs index 7e948c47dccab..1d911bc7155d3 100644 --- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.cs @@ -286,6 +286,7 @@ protected virtual void GenerateMoveNext(SynthesizedImplementationMethod moveNext instanceIdField: instanceIdField, hoistedVariables: hoistedVariables, nonReusableLocalProxies: nonReusableLocalProxies, + nonReusableFieldsForCleanup: nonReusableFieldsForCleanup, synthesizedLocalOrdinals: synthesizedLocalOrdinals, stateMachineStateDebugInfoBuilder, slotAllocatorOpt: slotAllocatorOpt, diff --git a/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorMethodToStateMachineRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorMethodToStateMachineRewriter.cs index 606cde97b5066..f022c87dd9e40 100644 --- a/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorMethodToStateMachineRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorMethodToStateMachineRewriter.cs @@ -61,12 +61,13 @@ internal IteratorMethodToStateMachineRewriter( FieldSymbol? instanceIdField, IReadOnlySet hoistedVariables, IReadOnlyDictionary nonReusableLocalProxies, + ImmutableArray nonReusableFieldsForCleanup, SynthesizedLocalOrdinalsDispenser synthesizedLocalOrdinals, ArrayBuilder stateMachineStateDebugInfoBuilder, VariableSlotAllocator slotAllocatorOpt, int nextFreeHoistedLocalSlot, BindingDiagnosticBag diagnostics) - : base(F, originalMethod, state, instanceIdField, hoistedVariables, nonReusableLocalProxies, synthesizedLocalOrdinals, stateMachineStateDebugInfoBuilder, slotAllocatorOpt, nextFreeHoistedLocalSlot, diagnostics) + : base(F, originalMethod, state, instanceIdField, hoistedVariables, nonReusableLocalProxies, nonReusableFieldsForCleanup, synthesizedLocalOrdinals, stateMachineStateDebugInfoBuilder, slotAllocatorOpt, nextFreeHoistedLocalSlot, diagnostics) { _current = current; @@ -160,7 +161,11 @@ internal void GenerateMoveNextAndDispose(BoundStatement body, SynthesizedImpleme if (rootFrame.knownStates == null) { // nothing to finalize - F.CloseMethod(F.Return()); + var disposeBody = F.Block( + GenerateAllHoistedLocalsCleanup(), + F.Return()); + + F.CloseMethod(disposeBody); } else { @@ -171,6 +176,7 @@ internal void GenerateMoveNextAndDispose(BoundStatement body, SynthesizedImpleme ImmutableArray.Create(stateLocal), F.Assignment(F.Local(stateLocal), F.Field(F.This(), stateField)), EmitFinallyFrame(rootFrame, state), + GenerateAllHoistedLocalsCleanup(), F.Return()); F.CloseMethod(disposeBody); diff --git a/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorRewriter.cs index 9d444a3eeeaa6..815f497c82f42 100644 --- a/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorRewriter.cs @@ -335,6 +335,7 @@ private void GenerateMoveNextAndDispose( instanceIdField, hoistedVariables, nonReusableLocalProxies, + nonReusableFieldsForCleanup, synthesizedLocalOrdinals, stateMachineStateDebugInfoBuilder, slotAllocatorOpt, diff --git a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs index 1fe789a5a9a69..4221e9de09565 100644 --- a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs @@ -61,7 +61,7 @@ internal abstract class MethodToStateMachineRewriter : MethodToClassRewriter /// Note that there is a dispatch occurring at every try-finally statement, so this /// variable takes on a new set of values inside each try block. /// - private Dictionary> _dispatches = new(); + private Dictionary> _dispatches = new Dictionary>(); /// /// A pool of fields used to hoist locals. They appear in this set when not in scope, @@ -70,15 +70,15 @@ internal abstract class MethodToStateMachineRewriter : MethodToClassRewriter private Dictionary>? _lazyAvailableReusableHoistedFields; /// - /// Fields allocated for temporary variables are given unique names distinguished by a number at the end. - /// This counter ensures they are unique within a given translated method. + /// We collect all the hoisted fields for locals, so that we can clear them so the GC can collect references. /// - private int _nextHoistedFieldId = 1; + private readonly ArrayBuilder _fieldsForCleanup; /// - /// Used to enumerate the instance fields of a struct. + /// Fields allocated for temporary variables are given unique names distinguished by a number at the end. + /// This counter ensures they are unique within a given translated method. /// - private readonly EmptyStructTypeCache _emptyStructTypeCache = EmptyStructTypeCache.CreateNeverEmpty(); + private int _nextHoistedFieldId = 1; /// /// The set of local variables and parameters that were hoisted and need a proxy. @@ -104,6 +104,7 @@ public MethodToStateMachineRewriter( FieldSymbol? instanceIdField, IReadOnlySet hoistedVariables, IReadOnlyDictionary nonReusableLocalProxies, + ImmutableArray nonReusableFieldsForCleanup, SynthesizedLocalOrdinalsDispenser synthesizedLocalOrdinals, ArrayBuilder stateMachineStateDebugInfoBuilder, VariableSlotAllocator? slotAllocatorOpt, @@ -115,6 +116,7 @@ public MethodToStateMachineRewriter( Debug.Assert(originalMethod != null); Debug.Assert(state != null); Debug.Assert(nonReusableLocalProxies != null); + Debug.Assert(!nonReusableFieldsForCleanup.IsDefault); Debug.Assert(diagnostics != null); Debug.Assert(hoistedVariables != null); Debug.Assert(nextFreeHoistedLocalSlot >= 0); @@ -133,6 +135,9 @@ public MethodToStateMachineRewriter( this.proxies.Add(proxy.Key, proxy.Value); } + _fieldsForCleanup = new ArrayBuilder(nonReusableFieldsForCleanup.Length); + _fieldsForCleanup.AddRange(nonReusableFieldsForCleanup); + // create cache local for reference type "this" in Release var thisParameter = originalMethod.ThisParameter; CapturedSymbolReplacement? thisProxy; @@ -428,15 +433,35 @@ internal static bool TryUnwrapBoundStateMachineScope(ref BoundStatement statemen /// private void AddVariableCleanup(ArrayBuilder cleanup, FieldSymbol field) { - if (field.Type.IsManagedTypeNoUseSiteDiagnostics) + var useSiteInfo = new CompoundUseSiteInfo(F.Diagnostics, F.Compilation.Assembly); + bool isManaged = field.Type.IsManagedType(ref useSiteInfo); + F.Diagnostics.Add(field.GetFirstLocationOrNone(), useSiteInfo); + if (isManaged) { cleanup.Add(F.AssignmentExpression(F.Field(F.This(), field), F.NullOrDefault(field.Type))); } } - private StateMachineFieldSymbol GetOrAllocateReusableHoistedField(TypeSymbol type, out bool reused, LocalSymbol local = null) +#nullable enable + protected BoundBlock GenerateAllHoistedLocalsCleanup() { - ArrayBuilder fields; + var variableCleanup = ArrayBuilder.GetInstance(); + + foreach (FieldSymbol fieldSymbol in _fieldsForCleanup) + { + AddVariableCleanup(variableCleanup, fieldSymbol); + } + + var result = F.Block(variableCleanup.SelectAsArray((e, f) => (BoundStatement)f.ExpressionStatement(e), F)); + + variableCleanup.Free(); + + return result; + } + + private StateMachineFieldSymbol GetOrAllocateReusableHoistedField(TypeSymbol type, out bool reused, LocalSymbol? local = null) + { + ArrayBuilder? fields; if (_lazyAvailableReusableHoistedFields != null && _lazyAvailableReusableHoistedFields.TryGetValue(type, out fields) && fields.Count > 0) { var field = fields.Last(); @@ -448,14 +473,21 @@ private StateMachineFieldSymbol GetOrAllocateReusableHoistedField(TypeSymbol typ reused = false; var slotIndex = _nextHoistedFieldId++; + StateMachineFieldSymbol createdField; if (local?.SynthesizedKind == SynthesizedLocalKind.UserDefined) { string fieldName = GeneratedNames.MakeHoistedLocalFieldName(SynthesizedLocalKind.UserDefined, slotIndex, local.Name); - return F.StateMachineField(type, fieldName, SynthesizedLocalKind.UserDefined, slotIndex); + createdField = F.StateMachineField(type, fieldName, SynthesizedLocalKind.UserDefined, slotIndex); + } + else + { + createdField = F.StateMachineField(type, GeneratedNames.ReusableHoistedLocalFieldName(slotIndex)); } - return F.StateMachineField(type, GeneratedNames.ReusableHoistedLocalFieldName(slotIndex)); + _fieldsForCleanup.Add(createdField); + return createdField; } +#nullable disable private void FreeReusableHoistedField(StateMachineFieldSymbol field) { @@ -671,6 +703,7 @@ private BoundExpression HoistExpression( string fieldName = GeneratedNames.MakeHoistedLocalFieldName(kind, slotIndex); hoistedField = F.StateMachineField(expr.Type, fieldName, new LocalSlotDebugInfo(kind, id), slotIndex); + _fieldsForCleanup.Add(hoistedField); } else { diff --git a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineRewriter.cs index 4720e12ee1dfb..a6911a94549f4 100644 --- a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineRewriter.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.CodeGen; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Symbols; -using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; @@ -29,6 +28,7 @@ internal abstract class StateMachineRewriter protected FieldSymbol? stateField; protected FieldSymbol? instanceIdField; protected IReadOnlyDictionary? nonReusableLocalProxies; + protected ImmutableArray nonReusableFieldsForCleanup; protected int nextFreeHoistedLocalSlot; protected IOrderedReadOnlySet? hoistedVariables; protected Dictionary? initialParameters; @@ -123,7 +123,7 @@ protected BoundStatement Rewrite() return new BoundBadStatement(F.Syntax, ImmutableArray.Empty, hasErrors: true); } - CreateNonReusableLocalProxies(variablesToHoist, out this.nonReusableLocalProxies, out this.nextFreeHoistedLocalSlot); + CreateNonReusableLocalProxies(variablesToHoist, out this.nonReusableLocalProxies, out this.nonReusableFieldsForCleanup, out this.nextFreeHoistedLocalSlot); this.hoistedVariables = variablesToHoist; @@ -136,9 +136,11 @@ protected BoundStatement Rewrite() private void CreateNonReusableLocalProxies( IEnumerable variablesToHoist, out IReadOnlyDictionary proxies, + out ImmutableArray nonReusableFieldsForCleanup, out int nextFreeHoistedLocalSlot) { var proxiesBuilder = new Dictionary(); + var nonReusableFieldsForCleanupBuilder = ArrayBuilder.GetInstance(); var typeMap = stateMachineType.TypeMap; bool isDebugBuild = F.Compilation.Options.OptimizationLevel == OptimizationLevel.Debug; @@ -221,6 +223,7 @@ private void CreateNonReusableLocalProxies( string fieldName = GeneratedNames.MakeHoistedLocalFieldName(synthesizedKind, slotIndex, local.Name); field = F.StateMachineField(fieldType, fieldName, new LocalSlotDebugInfo(synthesizedKind, id), slotIndex); + nonReusableFieldsForCleanupBuilder.Add(field); } if (field != null) @@ -262,6 +265,7 @@ private void CreateNonReusableLocalProxies( } proxies = proxiesBuilder; + nonReusableFieldsForCleanup = nonReusableFieldsForCleanupBuilder.ToImmutableAndFree(); } private bool ShouldPreallocateNonReusableProxy(LocalSymbol local) diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs index 890e20d8e09c2..4459d0c3cc867 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs @@ -182,7 +182,7 @@ public static async Task Main() var v = CompileAndVerify(comp, expectedOutput: "hello world"); v.VerifyIL("C.d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" { - // Code size 254 (0xfe) + // Code size 268 (0x10c) .maxstack 3 .locals init (int V_0, System.Exception V_1) @@ -199,7 +199,7 @@ .locals init (int V_0, IL_0010: ldarg.0 IL_0011: ldfld ""bool C.d__1.<>w__disposeMode"" IL_0016: brfalse.s IL_001d - IL_0018: leave IL_00d4 + IL_0018: leave IL_00db IL_001d: ldarg.0 IL_001e: ldc.i4.m1 IL_001f: dup @@ -267,11 +267,11 @@ .locals init (int V_0, IL_0096: ldarg.0 IL_0097: ldfld ""bool C.d__1.<>w__disposeMode"" IL_009c: brfalse.s IL_00a0 - IL_009e: leave.s IL_00d4 + IL_009e: leave.s IL_00db IL_00a0: ldarg.0 IL_00a1: ldc.i4.1 IL_00a2: stfld ""bool C.d__1.<>w__disposeMode"" - IL_00a7: leave.s IL_00d4 + IL_00a7: leave.s IL_00db } catch System.Exception { @@ -281,35 +281,41 @@ .locals init (int V_0, IL_00ad: stfld ""int C.d__1.<>1__state"" IL_00b2: ldarg.0 IL_00b3: ldnull - IL_00b4: stfld ""string C.d__1.<>2__current"" + IL_00b4: stfld ""object C.d__1.<>s__1"" IL_00b9: ldarg.0 - IL_00ba: ldflda ""System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__1.<>t__builder"" - IL_00bf: call ""void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()"" - IL_00c4: nop - IL_00c5: ldarg.0 - IL_00c6: ldflda ""System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__1.<>v__promiseOfValueOrEnd"" - IL_00cb: ldloc.1 - IL_00cc: call ""void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetException(System.Exception)"" - IL_00d1: nop - IL_00d2: leave.s IL_00fd + IL_00ba: ldnull + IL_00bb: stfld ""string C.d__1.<>2__current"" + IL_00c0: ldarg.0 + IL_00c1: ldflda ""System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__1.<>t__builder"" + IL_00c6: call ""void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()"" + IL_00cb: nop + IL_00cc: ldarg.0 + IL_00cd: ldflda ""System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__1.<>v__promiseOfValueOrEnd"" + IL_00d2: ldloc.1 + IL_00d3: call ""void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetException(System.Exception)"" + IL_00d8: nop + IL_00d9: leave.s IL_010b } - IL_00d4: ldarg.0 - IL_00d5: ldc.i4.s -2 - IL_00d7: stfld ""int C.d__1.<>1__state"" - IL_00dc: ldarg.0 - IL_00dd: ldnull - IL_00de: stfld ""string C.d__1.<>2__current"" + IL_00db: ldarg.0 + IL_00dc: ldc.i4.s -2 + IL_00de: stfld ""int C.d__1.<>1__state"" IL_00e3: ldarg.0 - IL_00e4: ldflda ""System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__1.<>t__builder"" - IL_00e9: call ""void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()"" - IL_00ee: nop - IL_00ef: ldarg.0 - IL_00f0: ldflda ""System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__1.<>v__promiseOfValueOrEnd"" - IL_00f5: ldc.i4.0 - IL_00f6: call ""void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)"" - IL_00fb: nop - IL_00fc: ret - IL_00fd: ret + IL_00e4: ldnull + IL_00e5: stfld ""object C.d__1.<>s__1"" + IL_00ea: ldarg.0 + IL_00eb: ldnull + IL_00ec: stfld ""string C.d__1.<>2__current"" + IL_00f1: ldarg.0 + IL_00f2: ldflda ""System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__1.<>t__builder"" + IL_00f7: call ""void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()"" + IL_00fc: nop + IL_00fd: ldarg.0 + IL_00fe: ldflda ""System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__1.<>v__promiseOfValueOrEnd"" + IL_0103: ldc.i4.0 + IL_0104: call ""void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)"" + IL_0109: nop + IL_010a: ret + IL_010b: ret }"); } @@ -9448,8 +9454,11 @@ public void AddVariableCleanup_StringLocal() using System.Reflection; var values = C.Produce(); -await foreach (int value in values) { } -System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); +await foreach (int value in values) +{ + System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); +} +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); class C { @@ -9462,8 +9471,234 @@ public static async System.Collections.Generic.IAsyncEnumerable Produce() } } """; - // Note: hoisted top-level local does not get cleared when exiting normally - CompileAndVerify(src, expectedOutput: ExpectedOutput("value value"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + var verifier = CompileAndVerify(src, expectedOutput: ExpectedOutput("value value True"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", """ +{ + // Code size 320 (0x140) + .maxstack 3 + .locals init (int V_0, + System.Runtime.CompilerServices.TaskAwaiter V_1, + C.d__0 V_2, + System.Exception V_3) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.d__0.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: ldc.i4.s -4 + IL_000a: sub + IL_000b: switch ( + IL_00b5, + IL_0024, + IL_0024, + IL_0024, + IL_007f) + IL_0024: ldarg.0 + IL_0025: ldfld "bool C.d__0.<>w__disposeMode" + IL_002a: brfalse.s IL_0031 + IL_002c: leave IL_0105 + IL_0031: ldarg.0 + IL_0032: ldc.i4.m1 + IL_0033: dup + IL_0034: stloc.0 + IL_0035: stfld "int C.d__0.<>1__state" + IL_003a: ldarg.0 + IL_003b: ldstr "value " + IL_0040: stfld "string C.d__0.5__2" + IL_0045: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.CompletedTask.get" + IL_004a: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_004f: stloc.1 + IL_0050: ldloca.s V_1 + IL_0052: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_0057: brtrue.s IL_009b + IL_0059: ldarg.0 + IL_005a: ldc.i4.0 + IL_005b: dup + IL_005c: stloc.0 + IL_005d: stfld "int C.d__0.<>1__state" + IL_0062: ldarg.0 + IL_0063: ldloc.1 + IL_0064: stfld "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_0069: ldarg.0 + IL_006a: stloc.2 + IL_006b: ldarg.0 + IL_006c: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__0.<>t__builder" + IL_0071: ldloca.s V_1 + IL_0073: ldloca.s V_2 + IL_0075: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__0)" + IL_007a: leave IL_013f + IL_007f: ldarg.0 + IL_0080: ldfld "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_0085: stloc.1 + IL_0086: ldarg.0 + IL_0087: ldflda "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_008c: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_0092: ldarg.0 + IL_0093: ldc.i4.m1 + IL_0094: dup + IL_0095: stloc.0 + IL_0096: stfld "int C.d__0.<>1__state" + IL_009b: ldloca.s V_1 + IL_009d: call "void System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_00a2: ldarg.0 + IL_00a3: ldc.i4.1 + IL_00a4: stfld "int C.d__0.<>2__current" + IL_00a9: ldarg.0 + IL_00aa: ldc.i4.s -4 + IL_00ac: dup + IL_00ad: stloc.0 + IL_00ae: stfld "int C.d__0.<>1__state" + IL_00b3: leave.s IL_0133 + IL_00b5: ldarg.0 + IL_00b6: ldc.i4.m1 + IL_00b7: dup + IL_00b8: stloc.0 + IL_00b9: stfld "int C.d__0.<>1__state" + IL_00be: ldarg.0 + IL_00bf: ldfld "bool C.d__0.<>w__disposeMode" + IL_00c4: brfalse.s IL_00c8 + IL_00c6: leave.s IL_0105 + IL_00c8: ldarg.0 + IL_00c9: ldfld "string C.d__0.5__2" + IL_00ce: call "void System.Console.Write(string)" + IL_00d3: leave.s IL_0105 + } + catch System.Exception + { + IL_00d5: stloc.3 + IL_00d6: ldarg.0 + IL_00d7: ldc.i4.s -2 + IL_00d9: stfld "int C.d__0.<>1__state" + IL_00de: ldarg.0 + IL_00df: ldnull + IL_00e0: stfld "string C.d__0.5__2" + IL_00e5: ldarg.0 + IL_00e6: ldc.i4.0 + IL_00e7: stfld "int C.d__0.<>2__current" + IL_00ec: ldarg.0 + IL_00ed: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__0.<>t__builder" + IL_00f2: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_00f7: ldarg.0 + IL_00f8: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__0.<>v__promiseOfValueOrEnd" + IL_00fd: ldloc.3 + IL_00fe: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetException(System.Exception)" + IL_0103: leave.s IL_013f + } + IL_0105: ldarg.0 + IL_0106: ldc.i4.s -2 + IL_0108: stfld "int C.d__0.<>1__state" + IL_010d: ldarg.0 + IL_010e: ldnull + IL_010f: stfld "string C.d__0.5__2" + IL_0114: ldarg.0 + IL_0115: ldc.i4.0 + IL_0116: stfld "int C.d__0.<>2__current" + IL_011b: ldarg.0 + IL_011c: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__0.<>t__builder" + IL_0121: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_0126: ldarg.0 + IL_0127: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__0.<>v__promiseOfValueOrEnd" + IL_012c: ldc.i4.0 + IL_012d: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_0132: ret + IL_0133: ldarg.0 + IL_0134: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__0.<>v__promiseOfValueOrEnd" + IL_0139: ldc.i4.1 + IL_013a: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_013f: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_StringLocal_YieldBreak() + { + string src = """ +using System.Reflection; + +var values = C.Produce(true); +await foreach (int value in values) +{ + System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); +} +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce(bool b) + { + string s = "value "; + await System.Threading.Tasks.Task.CompletedTask; + yield return 42; + System.Console.Write(s); + if (b) yield break; + throw null; + } +} +"""; + CompileAndVerify(src, expectedOutput: ExpectedOutput("value value True"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_StringLocal_ThrownException() + { + string src = """ +using System.Reflection; + +var values = C.Produce(true); +try +{ + await foreach (int value in values) { } +} +catch (System.Exception e) +{ + System.Console.Write(e.Message); +} + +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce(bool b) + { + string s = "value "; + await System.Threading.Tasks.Task.CompletedTask; + System.Console.Write(s); + if (b) throw new System.Exception("exception "); + yield break; + } +} +"""; + CompileAndVerify(src, expectedOutput: ExpectedOutput("value exception True"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_StringLocal_EarlyIterationExit() + { + string src = """ +using System.Reflection; + +var values = C.Produce(true); +await foreach (var value in values) +{ + System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); + break; +} +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce(bool b) + { + string s = "value "; + await System.Threading.Tasks.Task.CompletedTask; + yield return 42; + _ = s.ToString(); + } +} +"""; + CompileAndVerify(src, expectedOutput: ExpectedOutput("value True"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] @@ -9477,6 +9712,8 @@ public void AddVariableCleanup_NestedStringLocal() var enumerator = values.GetAsyncEnumerator(); assert(await enumerator.MoveNextAsync()); assert(enumerator.Current == 1); +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); + assert(await enumerator.MoveNextAsync()); assert(enumerator.Current == 2); _ = enumerator.MoveNextAsync(); @@ -9505,7 +9742,124 @@ public static async System.Collections.Generic.IAsyncEnumerable Produce(boo } } """; - // Note: hoisted nested local gets cleared when exiting nested scope normally + CompileAndVerify(src, expectedOutput: ExpectedOutput("value value True"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedStringLocal_YieldBreak() + { + string src = """ +using System.Reflection; + +var values = C.Produce(true); +var enumerator = values.GetAsyncEnumerator(); +assert(await enumerator.MoveNextAsync()); +assert(enumerator.Current == 1); +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); +assert(!(await enumerator.MoveNextAsync())); + +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +void assert(bool b) +{ + if (!b) throw new System.Exception(); +} + +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce(bool b) + { + while (b) + { + string s = "value "; + yield return 1; + System.Console.Write(s); + await System.Threading.Tasks.Task.CompletedTask; + if (b) yield break; + } + throw null; + } +} +"""; + CompileAndVerify(src, expectedOutput: ExpectedOutput("value value True"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedStringLocal_ThrownException() + { + string src = """ +using System.Reflection; + +var values = C.Produce(true); +var enumerator = values.GetAsyncEnumerator(); +assert(await enumerator.MoveNextAsync()); +assert(enumerator.Current == 1); +try +{ + assert(!(await enumerator.MoveNextAsync())); +} +catch (System.Exception e) +{ + System.Console.Write(e.Message); +} +await enumerator.DisposeAsync(); + +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +void assert(bool b) +{ + if (!b) throw new System.Exception(); +} + +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce(bool b) + { + while (b) + { + string s = "value "; + yield return 1; + System.Console.Write(s); + await System.Threading.Tasks.Task.CompletedTask; + if (b) throw new System.Exception("exception "); + } + throw null; + } +} +"""; + CompileAndVerify(src, expectedOutput: ExpectedOutput("value exception True"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedStringLocal_EarlyIterationExit() + { + string src = """ +using System.Reflection; + +var values = C.Produce(true); +await foreach (var value in values) +{ + System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); + break; +} +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce(bool b) + { + while (b) + { + string s = "value "; + yield return 1; + System.Console.Write(s); + await System.Threading.Tasks.Task.CompletedTask; + throw null; + } + throw null; + } +} +"""; CompileAndVerify(src, expectedOutput: ExpectedOutput("value True"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); } @@ -9743,5 +10097,439 @@ .locals init (int V_0, } """); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedStringLocal_InTryFinally_WithThrow_EarlyIterationExit() + { + string src = """ +using System.Reflection; + +var values = C.Produce(true); +try +{ + await foreach (var value in values) { break; } // we interrupt the iteration early +} +catch (System.Exception e) +{ + System.Console.Write(e.Message); +} + +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce(bool b) + { + try + { + string s = "value "; + await System.Threading.Tasks.Task.CompletedTask; + yield return 42; + s.ToString(); + throw null; + } + finally + { + throw new System.Exception("exception "); + } + } +} +"""; + CompileAndVerify(src, expectedOutput: ExpectedOutput("exception True"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_StringLocal_IAsyncEnumerator() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); +assert(await values.MoveNextAsync()); +assert(values.Current == 1); +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); +assert(!(await values.MoveNextAsync())); +await values.DisposeAsync(); + +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +static void assert(bool b) { if (!b) throw new System.Exception(); } + +class C +{ + public static async System.Collections.Generic.IAsyncEnumerator Produce() + { + string s = "value "; + await System.Threading.Tasks.Task.CompletedTask; + yield return 1; + System.Console.Write(s); + } +} +"""; + CompileAndVerify(src, expectedOutput: ExpectedOutput("value value True"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NotCleanedTooSoon() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); +await foreach (int i in values) +{ + System.Console.Write((values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); + break; +} +System.Console.Write((values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce() + { + try + { + string s = "value "; + try + { + await System.Threading.Tasks.Task.CompletedTask; + yield return 42; + } + finally + { + System.Console.Write(s); + } + } + finally + { + System.Console.Write("outer "); + } + } +} +"""; + CompileAndVerify(src, expectedOutput: ExpectedOutput("value value outer True"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_HoistedFromRefExpression() + { + string src = """ +using System.Reflection; + +var c = new C(); +var values = Program.Produce(c); +await foreach (int i in values) +{ + System.Console.Write((values.GetType().GetField("<>7__wrap3", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + System.Console.Write($" {i} "); +} +System.Console.Write((values.GetType().GetField("<>7__wrap3", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +partial class Program +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce(C x) + { + int i = 0; + foreach (var y in x.F) + { + await System.Threading.Tasks.Task.CompletedTask; + yield return i++; + } + } +} + +class C +{ + public Buffer2 F = default; +} + +[System.Runtime.CompilerServices.InlineArray(2)] +public struct Buffer2 +{ + private T _element0; +} +"""; + CompileAndVerify(src, expectedOutput: ExpectedOutput("False 0 False 1 True"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_HoistedFromRefExpression_Debug() + { + string src = """ +using System.Reflection; + +var c = new C(); +var values = Program.Produce(c); +await foreach (int i in values) +{ + System.Console.Write((values.GetType().GetField("<>s__4", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + System.Console.Write($" {i} "); +} +System.Console.Write((values.GetType().GetField("<>s__4", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +partial class Program +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce(C x) + { + int i = 0; + foreach (var y in x.F) + { + await System.Threading.Tasks.Task.CompletedTask; + yield return i++; + } + } +} + +class C +{ + public Buffer2 F = default; +} + +[System.Runtime.CompilerServices.InlineArray(2)] +public struct Buffer2 +{ + private T _element0; +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: ExpectedOutput("False 0 False 1 True"), + verify: Verification.Skipped, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", """ +{ + // Code size 449 (0x1c1) + .maxstack 4 + .locals init (int V_0, + System.Runtime.CompilerServices.TaskAwaiter V_1, + Program.d__1 V_2, + int V_3, + System.Exception V_4) + IL_0000: ldarg.0 + IL_0001: ldfld "int Program.d__1.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: ldc.i4.s -4 + IL_000a: sub + IL_000b: switch ( + IL_0026, + IL_002b, + IL_0032, + IL_0032, + IL_002d) + IL_0024: br.s IL_0032 + IL_0026: br IL_0118 + IL_002b: br.s IL_0032 + IL_002d: br IL_00ce + IL_0032: ldarg.0 + IL_0033: ldfld "bool Program.d__1.<>w__disposeMode" + IL_0038: brfalse.s IL_003f + IL_003a: leave IL_0183 + IL_003f: ldarg.0 + IL_0040: ldc.i4.m1 + IL_0041: dup + IL_0042: stloc.0 + IL_0043: stfld "int Program.d__1.<>1__state" + IL_0048: nop + IL_0049: ldarg.0 + IL_004a: ldc.i4.0 + IL_004b: stfld "int Program.d__1.5__1" + IL_0050: nop + IL_0051: ldarg.0 + IL_0052: ldarg.0 + IL_0053: ldfld "C Program.d__1.x" + IL_0058: stfld "C Program.d__1.<>s__4" + IL_005d: ldarg.0 + IL_005e: ldfld "C Program.d__1.<>s__4" + IL_0063: ldfld "Buffer2 C.F" + IL_0068: pop + IL_0069: ldarg.0 + IL_006a: ldc.i4.0 + IL_006b: stfld "int Program.d__1.<>s__2" + IL_0070: br IL_013a + IL_0075: ldarg.0 + IL_0076: ldarg.0 + IL_0077: ldfld "C Program.d__1.<>s__4" + IL_007c: ldflda "Buffer2 C.F" + IL_0081: ldarg.0 + IL_0082: ldfld "int Program.d__1.<>s__2" + IL_0087: call "ref int .InlineArrayElementRef, int>(ref Buffer2, int)" + IL_008c: ldind.i4 + IL_008d: stfld "int Program.d__1.5__3" + IL_0092: nop + IL_0093: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.CompletedTask.get" + IL_0098: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_009d: stloc.1 + IL_009e: ldloca.s V_1 + IL_00a0: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_00a5: brtrue.s IL_00ea + IL_00a7: ldarg.0 + IL_00a8: ldc.i4.0 + IL_00a9: dup + IL_00aa: stloc.0 + IL_00ab: stfld "int Program.d__1.<>1__state" + IL_00b0: ldarg.0 + IL_00b1: ldloc.1 + IL_00b2: stfld "System.Runtime.CompilerServices.TaskAwaiter Program.d__1.<>u__1" + IL_00b7: ldarg.0 + IL_00b8: stloc.2 + IL_00b9: ldarg.0 + IL_00ba: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder Program.d__1.<>t__builder" + IL_00bf: ldloca.s V_1 + IL_00c1: ldloca.s V_2 + IL_00c3: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.AwaitUnsafeOnCompletedd__1>(ref System.Runtime.CompilerServices.TaskAwaiter, ref Program.d__1)" + IL_00c8: nop + IL_00c9: leave IL_01c0 + IL_00ce: ldarg.0 + IL_00cf: ldfld "System.Runtime.CompilerServices.TaskAwaiter Program.d__1.<>u__1" + IL_00d4: stloc.1 + IL_00d5: ldarg.0 + IL_00d6: ldflda "System.Runtime.CompilerServices.TaskAwaiter Program.d__1.<>u__1" + IL_00db: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_00e1: ldarg.0 + IL_00e2: ldc.i4.m1 + IL_00e3: dup + IL_00e4: stloc.0 + IL_00e5: stfld "int Program.d__1.<>1__state" + IL_00ea: ldloca.s V_1 + IL_00ec: call "void System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_00f1: nop + IL_00f2: ldarg.0 + IL_00f3: ldarg.0 + IL_00f4: ldfld "int Program.d__1.5__1" + IL_00f9: stloc.3 + IL_00fa: ldarg.0 + IL_00fb: ldloc.3 + IL_00fc: ldc.i4.1 + IL_00fd: add + IL_00fe: stfld "int Program.d__1.5__1" + IL_0103: ldloc.3 + IL_0104: stfld "int Program.d__1.<>2__current" + IL_0109: ldarg.0 + IL_010a: ldc.i4.s -4 + IL_010c: dup + IL_010d: stloc.0 + IL_010e: stfld "int Program.d__1.<>1__state" + IL_0113: leave IL_01b3 + IL_0118: ldarg.0 + IL_0119: ldc.i4.m1 + IL_011a: dup + IL_011b: stloc.0 + IL_011c: stfld "int Program.d__1.<>1__state" + IL_0121: ldarg.0 + IL_0122: ldfld "bool Program.d__1.<>w__disposeMode" + IL_0127: brfalse.s IL_012b + IL_0129: leave.s IL_0183 + IL_012b: nop + IL_012c: ldarg.0 + IL_012d: ldarg.0 + IL_012e: ldfld "int Program.d__1.<>s__2" + IL_0133: ldc.i4.1 + IL_0134: add + IL_0135: stfld "int Program.d__1.<>s__2" + IL_013a: ldarg.0 + IL_013b: ldfld "int Program.d__1.<>s__2" + IL_0140: ldc.i4.2 + IL_0141: blt IL_0075 + IL_0146: ldarg.0 + IL_0147: ldnull + IL_0148: stfld "C Program.d__1.<>s__4" + IL_014d: leave.s IL_0183 + } + catch System.Exception + { + IL_014f: stloc.s V_4 + IL_0151: ldarg.0 + IL_0152: ldc.i4.s -2 + IL_0154: stfld "int Program.d__1.<>1__state" + IL_0159: ldarg.0 + IL_015a: ldnull + IL_015b: stfld "C Program.d__1.<>s__4" + IL_0160: ldarg.0 + IL_0161: ldc.i4.0 + IL_0162: stfld "int Program.d__1.<>2__current" + IL_0167: ldarg.0 + IL_0168: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder Program.d__1.<>t__builder" + IL_016d: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_0172: nop + IL_0173: ldarg.0 + IL_0174: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore Program.d__1.<>v__promiseOfValueOrEnd" + IL_0179: ldloc.s V_4 + IL_017b: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetException(System.Exception)" + IL_0180: nop + IL_0181: leave.s IL_01c0 + } + IL_0183: ldarg.0 + IL_0184: ldc.i4.s -2 + IL_0186: stfld "int Program.d__1.<>1__state" + IL_018b: ldarg.0 + IL_018c: ldnull + IL_018d: stfld "C Program.d__1.<>s__4" + IL_0192: ldarg.0 + IL_0193: ldc.i4.0 + IL_0194: stfld "int Program.d__1.<>2__current" + IL_0199: ldarg.0 + IL_019a: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder Program.d__1.<>t__builder" + IL_019f: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_01a4: nop + IL_01a5: ldarg.0 + IL_01a6: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore Program.d__1.<>v__promiseOfValueOrEnd" + IL_01ab: ldc.i4.0 + IL_01ac: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_01b1: nop + IL_01b2: ret + IL_01b3: ldarg.0 + IL_01b4: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore Program.d__1.<>v__promiseOfValueOrEnd" + IL_01b9: ldc.i4.1 + IL_01ba: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_01bf: nop + IL_01c0: ret +} +"""); + } + + [Fact] + public void AddVariableCleanup_Unmanaged_UseSiteError() + { + var missingLibS1 = CreateCompilation(@" +public struct S1 +{ + public int i; +} +", assemblyName: "libS1", targetFramework: TargetFramework.Net80).ToMetadataReference(); + + var libS2 = CreateCompilation(@" +public struct S2 +{ + public S1 s1; +} +", references: [missingLibS1], assemblyName: "libS2", targetFramework: TargetFramework.Net80).ToMetadataReference(); + + var source = @" +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable M(S2 p) + { + S2 local = p; + await System.Threading.Tasks.Task.CompletedTask; + yield return local; + System.Console.Write(local); + } +} +"; + var comp = CreateCompilation(source, references: [libS2], targetFramework: TargetFramework.Net80); + comp.VerifyEmitDiagnostics( + // error CS0012: The type 'S1' is defined in an assembly that is not referenced. You must add a reference to assembly 'libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + Diagnostic(ErrorCode.ERR_NoTypeDef).WithArguments("S1", "libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 1), + // error CS0012: The type 'S1' is defined in an assembly that is not referenced. You must add a reference to assembly 'libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + Diagnostic(ErrorCode.ERR_NoTypeDef).WithArguments("S1", "libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 1), + // error CS0012: The type 'S1' is defined in an assembly that is not referenced. You must add a reference to assembly 'libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + Diagnostic(ErrorCode.ERR_NoTypeDef).WithArguments("S1", "libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 1), + // error CS0012: The type 'S1' is defined in an assembly that is not referenced. You must add a reference to assembly 'libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + Diagnostic(ErrorCode.ERR_NoTypeDef).WithArguments("S1", "libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 1)); + + comp = CreateCompilation(source, options: TestOptions.UnsafeDebugDll, references: [libS2, missingLibS1], targetFramework: TargetFramework.Net80); + comp.VerifyEmitDiagnostics(); + } } } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDisplayClassOptimisationTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDisplayClassOptimisationTests.cs index 03941adc0d1c2..9de4494e5794c 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDisplayClassOptimisationTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDisplayClassOptimisationTests.cs @@ -7742,15 +7742,24 @@ 01 00 00 00 ) .override method instance void [mscorlib]System.IDisposable::Dispose() // Method begins at RVA 0x20b1 - // Code size 1 (0x1) + // Code size 22 (0x16) .maxstack 8 - IL_0000: ret + IL_0000: ldarg.0 + IL_0001: ldnull + IL_0002: stfld class C/'<>c__DisplayClass0_0' C/'d__0'::'<>8__1' + IL_0007: ldarg.0 + IL_0008: ldnull + IL_0009: stfld class C/'<>c__DisplayClass0_1' C/'d__0'::'<>8__2' + IL_000e: ldarg.0 + IL_000f: ldnull + IL_0010: stfld class C/'<>c__DisplayClass0_2' C/'d__0'::'<>8__3' + IL_0015: ret } // end of method 'd__0'::System.IDisposable.Dispose .method private final hidebysig newslot virtual instance bool MoveNext () cil managed { .override method instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() - // Method begins at RVA 0x20b4 + // Method begins at RVA 0x20c8 // Code size 342 (0x156) .maxstack 2 .locals init ( @@ -7889,7 +7898,7 @@ .method private final hidebysig specialname newslot virtual 01 00 00 00 ) .override method instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1::get_Current() - // Method begins at RVA 0x2216 + // Method begins at RVA 0x222a // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 @@ -7903,7 +7912,7 @@ instance void System.Collections.IEnumerator.Reset () cil managed 01 00 00 00 ) .override method instance void [mscorlib]System.Collections.IEnumerator::Reset() - // Method begins at RVA 0x221e + // Method begins at RVA 0x2232 // Code size 6 (0x6) .maxstack 8 IL_0000: newobj instance void [mscorlib]System.NotSupportedException::.ctor() @@ -7916,7 +7925,7 @@ instance object System.Collections.IEnumerator.get_Current () cil managed 01 00 00 00 ) .override method instance object [mscorlib]System.Collections.IEnumerator::get_Current() - // Method begins at RVA 0x2225 + // Method begins at RVA 0x2239 // Code size 12 (0xc) .maxstack 8 IL_0000: ldarg.0 @@ -7931,7 +7940,7 @@ .method private final hidebysig newslot virtual 01 00 00 00 ) .override method instance class [mscorlib]System.Collections.Generic.IEnumerator`1 class [mscorlib]System.Collections.Generic.IEnumerable`1::GetEnumerator() - // Method begins at RVA 0x2234 + // Method begins at RVA 0x2248 // Code size 43 (0x2b) .maxstack 2 .locals init ( @@ -7964,7 +7973,7 @@ instance class [mscorlib]System.Collections.IEnumerator System.Collections.IEnum 01 00 00 00 ) .override method instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.IEnumerable::GetEnumerator() - // Method begins at RVA 0x226b + // Method begins at RVA 0x227f // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenIterators.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenIterators.cs index f4ad723ea8864..5ab7b180a4923 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenIterators.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenIterators.cs @@ -3022,7 +3022,14 @@ public static System.Collections.Generic.IEnumerable Produce() } } """; - CompileAndVerify(src, expectedOutput: "42 42").VerifyDiagnostics(); + var verifier = CompileAndVerify(src, expectedOutput: "42 42").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 1 (0x1) + .maxstack 0 + IL_0000: ret +} +"""); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] @@ -3032,33 +3039,85 @@ public void AddVariableCleanup_StringLocal() using System.Reflection; var values = C.Produce(); -foreach (int value in values) { } -System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); +foreach (int value in values) +{ + System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); +} +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); class C { public static System.Collections.Generic.IEnumerable Produce() { - string values2 = "ran"; + string values2 = "ran "; yield return 42; - System.Console.Write($"{values2} "); + System.Console.Write(values2); + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "ran ran True").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldnull + IL_0002: stfld "string C.d__0.5__2" + IL_0007: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_StringLocal_YieldBreak() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); +foreach (int value in values) +{ + System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); +} +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static System.Collections.Generic.IEnumerable Produce() + { + string s = "ran "; + yield return 42; + System.Console.Write(s); + yield break; } } """; - // Note: top-level hoisted local does not get cleared when exiting normally - CompileAndVerify(src, expectedOutput: "ran ran").VerifyDiagnostics(); + var verifier = CompileAndVerify(src, expectedOutput: "ran ran True").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldnull + IL_0002: stfld "string C.d__0.5__2" + IL_0007: ret +} +"""); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] - public void AddVariableCleanup_IntArrayLocalAndForeachLocals() + public void AddVariableCleanup_IntArrayLocal() { string source = """ using System.Linq; using System.Reflection; var values = C.Produce(); -foreach (int value in values) { } -System.Console.Write(((int[])values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)).Length); +foreach (int value in values) +{ + System.Console.Write(((int[])values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)).Length); +} +System.Console.Write(((int[])values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); class C { @@ -3066,13 +3125,22 @@ public static System.Collections.Generic.IEnumerable Produce() { int[] values2 = Enumerable.Range(0, 100).ToArray(); yield return 42; - System.Console.Write($"{values2.Length} "); + System.Console.Write($" {values2.Length} "); } } """; - // Note: top-level hoisted local does not get cleared when exiting normally var comp = CreateCompilation(source); - CompileAndVerify(comp, expectedOutput: "100 100").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "100 100 True").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldnull + IL_0002: stfld "int[] C.d__0.5__2" + IL_0007: ret +} +"""); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] @@ -3087,6 +3155,7 @@ public void AddVariableCleanup_NestedStringLocal() { assert(enumerator.MoveNext()); System.Console.Write($"{enumerator.Current} "); + System.Console.Write(((string)enumerator.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator))); assert(!enumerator.MoveNext()); System.Console.Write(((string)enumerator.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator)) is null); } @@ -3114,30 +3183,39 @@ public static System.Collections.Generic.IEnumerable M(bool b) } } """; - // Note: nested hoisted local gets cleared when exiting normally - CompileAndVerify(src, expectedOutput: "42 value True").VerifyDiagnostics(); + var verifier = CompileAndVerify(src, expectedOutput: "42 value value True").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldnull + IL_0002: stfld "string C.d__0.5__2" + IL_0007: ret +} +"""); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] - public void AddVariableCleanup_NestedUnmanagedTypeParameterLocal() + public void AddVariableCleanup_NestedStringLocal_YieldBreak() { var src = """ using System.Reflection; -var enumerable = C.M(true, 42); +var enumerable = C.M(true); var enumerator = enumerable.GetEnumerator(); try { assert(enumerator.MoveNext()); System.Console.Write($"{enumerator.Current} "); assert(!enumerator.MoveNext()); - System.Console.Write(" "); - System.Console.Write(((int)enumerator.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator))); + System.Console.Write(((string)enumerator.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator))); } finally { enumerator.Dispose(); } +System.Console.Write(((string)enumerator.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator)) is null); void assert(bool b) { @@ -3146,104 +3224,50 @@ void assert(bool b) public class C { - public static System.Collections.Generic.IEnumerable M(bool b, T t) where T : unmanaged + public static System.Collections.Generic.IEnumerable M(bool b) { while (b) { - T local = t; - yield return 10; - b = false; - System.Console.Write(local); + string s = "value "; + yield return 42; + System.Console.Write(s); + yield break; } } } """; - var verifier = CompileAndVerify(src, expectedOutput: "10 42 42").VerifyDiagnostics(); - verifier.VerifyIL("C.d__0.System.Collections.IEnumerator.MoveNext()", """ + var verifier = CompileAndVerify(src, expectedOutput: "42 value value True").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ { - // Code size 94 (0x5e) + // Code size 8 (0x8) .maxstack 2 - .locals init (int V_0) IL_0000: ldarg.0 - IL_0001: ldfld "int C.d__0.<>1__state" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: brfalse.s IL_0010 - IL_000a: ldloc.0 - IL_000b: ldc.i4.1 - IL_000c: beq.s IL_0036 - IL_000e: ldc.i4.0 - IL_000f: ret - IL_0010: ldarg.0 - IL_0011: ldc.i4.m1 - IL_0012: stfld "int C.d__0.<>1__state" - IL_0017: br.s IL_0054 - IL_0019: ldarg.0 - IL_001a: ldarg.0 - IL_001b: ldfld "T C.d__0.t" - IL_0020: stfld "T C.d__0.5__2" - IL_0025: ldarg.0 - IL_0026: ldc.i4.s 10 - IL_0028: stfld "int C.d__0.<>2__current" - IL_002d: ldarg.0 - IL_002e: ldc.i4.1 - IL_002f: stfld "int C.d__0.<>1__state" - IL_0034: ldc.i4.1 - IL_0035: ret - IL_0036: ldarg.0 - IL_0037: ldc.i4.m1 - IL_0038: stfld "int C.d__0.<>1__state" - IL_003d: ldarg.0 - IL_003e: ldc.i4.0 - IL_003f: stfld "bool C.d__0.b" - IL_0044: ldarg.0 - IL_0045: ldfld "T C.d__0.5__2" - IL_004a: box "T" - IL_004f: call "void System.Console.Write(object)" - IL_0054: ldarg.0 - IL_0055: ldfld "bool C.d__0.b" - IL_005a: brtrue.s IL_0019 - IL_005c: ldc.i4.0 - IL_005d: ret -} -"""); - verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ -{ - // Code size 1 (0x1) - .maxstack 0 - IL_0000: ret + IL_0001: ldnull + IL_0002: stfld "string C.d__0.5__2" + IL_0007: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] - public void AddVariableCleanup_NestedLocalWithStructFromAnotherCompilation() + public void AddVariableCleanup_NestedStringLocal_ThrownException() { - var libSrc = """ -public struct S -{ - public int field; - public override string ToString() => field.ToString(); -} -"""; - var libComp = CreateCompilation(libSrc); var src = """ using System.Reflection; -var enumerable = C.M(true, new S { field = 42 }); +var enumerable = C.M(true); var enumerator = enumerable.GetEnumerator(); try { assert(enumerator.MoveNext()); System.Console.Write($"{enumerator.Current} "); - assert(!enumerator.MoveNext()); - System.Console.Write(" "); - System.Console.Write(((S)enumerator.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator))); } finally { + System.Console.Write(((string)enumerable.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerable))); enumerator.Dispose(); } +System.Console.Write(((string)enumerable.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerable)) is null); void assert(bool b) { @@ -3252,70 +3276,73 @@ void assert(bool b) public class C { - public static System.Collections.Generic.IEnumerable M(bool b, S s) + public static System.Collections.Generic.IEnumerable M(bool b) { while (b) { - S local = s; - yield return 10; - b = false; - System.Console.Write(local.ToString()); + string s = "value "; + yield return 42; + System.Console.Write(s); + throw new System.Exception(); } } } """; + CompileAndVerify(src, expectedOutput: "42 value True").VerifyDiagnostics(); + } - var verifier = CompileAndVerify(src, expectedOutput: "10 42 42", references: [libComp.EmitToImageReference()]).VerifyDiagnostics(); - verifier.VerifyIL("C.d__0.System.Collections.IEnumerator.MoveNext()", """ + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_IntParameter() + { + string src = """ +using System.Reflection; + +var values = C.Produce(42); +foreach (int value in values) { } +System.Console.Write(((int)values.GetType().GetField("s", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); +System.Console.Write(((int)values.GetType().GetField("<>3__s", BindingFlags.Public | BindingFlags.Instance).GetValue(values))); + +class C { - // Code size 100 (0x64) - .maxstack 2 - .locals init (int V_0) - IL_0000: ldarg.0 - IL_0001: ldfld "int C.d__0.<>1__state" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: brfalse.s IL_0010 - IL_000a: ldloc.0 - IL_000b: ldc.i4.1 - IL_000c: beq.s IL_0036 - IL_000e: ldc.i4.0 - IL_000f: ret - IL_0010: ldarg.0 - IL_0011: ldc.i4.m1 - IL_0012: stfld "int C.d__0.<>1__state" - IL_0017: br.s IL_005a - IL_0019: ldarg.0 - IL_001a: ldarg.0 - IL_001b: ldfld "S C.d__0.s" - IL_0020: stfld "S C.d__0.5__2" - IL_0025: ldarg.0 - IL_0026: ldc.i4.s 10 - IL_0028: stfld "int C.d__0.<>2__current" - IL_002d: ldarg.0 - IL_002e: ldc.i4.1 - IL_002f: stfld "int C.d__0.<>1__state" - IL_0034: ldc.i4.1 - IL_0035: ret - IL_0036: ldarg.0 - IL_0037: ldc.i4.m1 - IL_0038: stfld "int C.d__0.<>1__state" - IL_003d: ldarg.0 - IL_003e: ldc.i4.0 - IL_003f: stfld "bool C.d__0.b" - IL_0044: ldarg.0 - IL_0045: ldflda "S C.d__0.5__2" - IL_004a: constrained. "S" - IL_0050: callvirt "string object.ToString()" - IL_0055: call "void System.Console.Write(string)" - IL_005a: ldarg.0 - IL_005b: ldfld "bool C.d__0.b" - IL_0060: brtrue.s IL_0019 - IL_0062: ldc.i4.0 - IL_0063: ret + public static System.Collections.Generic.IEnumerable Produce(int s) + { + yield return 0; + System.Console.Write($"{s} "); + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "42 4242").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 1 (0x1) + .maxstack 0 + IL_0000: ret } """); - verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_StringParameter() + { + string src = """ +using System.Reflection; + +var values = C.Produce("value "); +foreach (int value in values) { } +System.Console.Write(((string)values.GetType().GetField("s", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); +System.Console.Write(((string)values.GetType().GetField("<>3__s", BindingFlags.Public | BindingFlags.Instance).GetValue(values))); + +class C +{ + public static System.Collections.Generic.IEnumerable Produce(string s) + { + yield return 0; + System.Console.Write(s); + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "value value value").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ { // Code size 1 (0x1) .maxstack 0 @@ -3325,50 +3352,773 @@ .maxstack 0 } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] - public void AddVariableCleanup_NestedUnmanagedWithGenericsLocal() + public void AddVariableCleanup_ClosureOverLocal() { - var src = """ + string src = """ using System.Reflection; -var enumerable = C.M(true, 42); -var enumerator = enumerable.GetEnumerator(); -try +var values = C.Produce(); +foreach (int value in values) { } +var closure = values.GetType().GetField("<>8__1", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values); +System.Console.Write((int)(closure.GetType().GetField("s", BindingFlags.Public | BindingFlags.Instance).GetValue(closure))); + +class C { - assert(enumerator.MoveNext()); - System.Console.Write($"{enumerator.Current} "); - assert(!enumerator.MoveNext()); - System.Console.Write(((S)enumerator.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator)).field); + public static System.Collections.Generic.IEnumerable Produce() + { + int s = 41; + local(); + yield return 0; + System.Console.Write($"{s} "); + + void local() + { + s++; + } + } } -finally +"""; + var verifier = CompileAndVerify(src, expectedOutput: "42 42").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ { - enumerator.Dispose(); + // Code size 1 (0x1) + .maxstack 0 + IL_0000: ret } +"""); + } -void assert(bool b) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_ClosureOverThis() + { + string src = """ +using System.Reflection; + +class C { - if (!b) throw new System.Exception(); + int field = 41; + public static void Main() + { + var values = new C().Produce(); + foreach (int value in values) { } + System.Console.Write(((C)values.GetType().GetField("<>4__this", BindingFlags.Public | BindingFlags.Instance).GetValue(values)).field); + } + + private System.Collections.Generic.IEnumerable Produce() + { + local(); + yield return this.field; + + void local() + { + this.field++; + } + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "42").VerifyDiagnostics(); + verifier.VerifyIL("C.d__2.System.IDisposable.Dispose()", """ +{ + // Code size 1 (0x1) + .maxstack 0 + IL_0000: ret } +"""); + } -public struct S where T : unmanaged // UnmanagedWithGenerics + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedStringLocal_InTryFinally() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); +foreach (int value in values) { - public T field; + System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); } +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); -public class C +class C { - public static System.Collections.Generic.IEnumerable M(bool b, T t) where T : unmanaged + public static System.Collections.Generic.IEnumerable Produce() { - while (b) + try { - S s = new S { field = t }; - yield return 42; - b = false; - System.Console.Write(s.field); + var s = "value "; + yield return 0; + System.Console.Write(s); + } + finally + { + System.Console.Write("ran "); } } } """; - CompileAndVerify(src, expectedOutput: "42 4242").VerifyDiagnostics(); + var verifier = CompileAndVerify(src, expectedOutput: "value value ran True").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 34 (0x22) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.d__0.<>1__state" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldc.i4.s -3 + IL_000a: beq.s IL_0010 + IL_000c: ldloc.0 + IL_000d: ldc.i4.1 + IL_000e: bne.un.s IL_001a + IL_0010: nop + .try + { + IL_0011: leave.s IL_001a + } + finally + { + IL_0013: ldarg.0 + IL_0014: call "void C.d__0.<>m__Finally1()" + IL_0019: endfinally + } + IL_001a: ldarg.0 + IL_001b: ldnull + IL_001c: stfld "string C.d__0.5__2" + IL_0021: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedStringLocal_InTryFinally_WithThrow() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); + +try +{ + foreach (int value in values) + { + System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); + } +} +catch (System.Exception e) +{ + System.Console.Write(e.Message); +} + +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static System.Collections.Generic.IEnumerable Produce() + { + try + { + string s = "value "; + yield return 0; + System.Console.Write(s); + } + finally + { + throw new System.Exception("exception "); + } + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "value value exception True").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 34 (0x22) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.d__0.<>1__state" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldc.i4.s -3 + IL_000a: beq.s IL_0010 + IL_000c: ldloc.0 + IL_000d: ldc.i4.1 + IL_000e: bne.un.s IL_001a + IL_0010: nop + .try + { + IL_0011: leave.s IL_001a + } + finally + { + IL_0013: ldarg.0 + IL_0014: call "void C.d__0.<>m__Finally1()" + IL_0019: endfinally + } + IL_001a: ldarg.0 + IL_001b: ldnull + IL_001c: stfld "string C.d__0.5__2" + IL_0021: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedStringLocal_InTryFinally_WithThrow_EarlyIterationExit() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); + +try +{ + foreach (int value in values) { break; } // we interrupt the iteration early +} +catch (System.Exception e) +{ + System.Console.Write(e.Message); +} + +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); + +class C +{ + public static System.Collections.Generic.IEnumerable Produce() + { + try + { + string s = "value"; + yield return 0; + s.ToString(); + throw null; + } + finally + { + throw new System.Exception("exception "); + } + } +} +"""; + // Note: nested hoisted local does not get cleared when an exception is thrown during disposal + CompileAndVerify(src, expectedOutput: "exception value").VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_StringLocal_WithThrownException() + { + string src = """ +using System.Reflection; + +var values = C.Produce(true); + +try +{ + foreach (int value in values) { } +} +catch (System.Exception e) +{ + System.Console.Write(e.Message); +} + +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static System.Collections.Generic.IEnumerable Produce(bool b) + { + string s = "value "; + if (b) throw new System.Exception("exception "); + yield return 0; + System.Console.Write(s); + } +} +"""; + CompileAndVerify(src, expectedOutput: "exception True").VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedUnmanagedTypeParameterLocal() + { + var src = """ +using System.Reflection; + +var enumerable = C.M(true, 42); +var enumerator = enumerable.GetEnumerator(); +try +{ + assert(enumerator.MoveNext()); + System.Console.Write($"{enumerator.Current} "); + assert(!enumerator.MoveNext()); + System.Console.Write(" "); + System.Console.Write(((int)enumerator.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator))); +} +finally +{ + enumerator.Dispose(); +} + +void assert(bool b) +{ + if (!b) throw new System.Exception(); +} + +public class C +{ + public static System.Collections.Generic.IEnumerable M(bool b, T t) where T : unmanaged + { + while (b) + { + T local = t; + yield return 10; + b = false; + System.Console.Write(local); + } + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "10 42 42").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 1 (0x1) + .maxstack 0 + IL_0000: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedLocalWithStructFromAnotherCompilation() + { + var libSrc = """ +public struct S +{ + public int field; + public override string ToString() => field.ToString(); +} +"""; + var libComp = CreateCompilation(libSrc); + var src = """ +using System.Reflection; + +var enumerable = C.M(true, new S { field = 42 }); +var enumerator = enumerable.GetEnumerator(); +try +{ + assert(enumerator.MoveNext()); + System.Console.Write($"{enumerator.Current} "); + assert(!enumerator.MoveNext()); + System.Console.Write(" "); + System.Console.Write(((S)enumerator.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator))); +} +finally +{ + enumerator.Dispose(); +} + +void assert(bool b) +{ + if (!b) throw new System.Exception(); +} + +public class C +{ + public static System.Collections.Generic.IEnumerable M(bool b, S s) + { + while (b) + { + S local = s; + yield return 10; + b = false; + System.Console.Write(local.ToString()); + } + } +} +"""; + + var verifier = CompileAndVerify(src, expectedOutput: "10 42 42", references: [libComp.EmitToImageReference()]).VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 1 (0x1) + .maxstack 0 + IL_0000: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedUnmanagedWithGenericsLocal() + { + var src = """ +using System.Reflection; + +var enumerable = C.M(true, 42); +var enumerator = enumerable.GetEnumerator(); +try +{ + assert(enumerator.MoveNext()); + System.Console.Write($"{enumerator.Current} "); + assert(!enumerator.MoveNext()); + System.Console.Write(((S)enumerator.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator)).field); +} +finally +{ + enumerator.Dispose(); +} + +void assert(bool b) +{ + if (!b) throw new System.Exception(); +} + +public struct S where T : unmanaged // UnmanagedWithGenerics +{ + public T field; +} + +public class C +{ + public static System.Collections.Generic.IEnumerable M(bool b, T t) where T : unmanaged + { + while (b) + { + S s = new S { field = t }; + yield return 42; + b = false; + System.Console.Write(s.field); + } + } +} +"""; + CompileAndVerify(src, expectedOutput: "42 4242").VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_StringLocal_EarlyIterationExit() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); + +foreach (int value in values) +{ + System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); + break; +} + +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static System.Collections.Generic.IEnumerable Produce() + { + string s = "value "; + yield return 0; + s.ToString(); + throw null; + } +} +"""; + CompileAndVerify(src, expectedOutput: "value True").VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedStringLocal_Reused() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); +foreach (int value in values) +{ + System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); +} +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static System.Collections.Generic.IEnumerable Produce() + { + { + string values2 = "values2 "; + yield return 42; + System.Console.Write(values2); + } + { + string values3 = "values3 "; + yield return 43; + System.Console.Write(values3); + } + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "values2 values2 values3 values3 True").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldnull + IL_0002: stfld "string C.d__0.5__2" + IL_0007: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_Parameters() + { + string src = """ +string s = "ran "; +var values = C.Produce(s); +foreach (int value in values) { } +foreach (int value in values) { } + +class C +{ + public static System.Collections.Generic.IEnumerable Produce(string s) + { + yield return 42; + System.Console.Write(s); + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "ran ran").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 1 (0x1) + .maxstack 0 + IL_0000: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NotCleanedTooSoon() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); +foreach (int value in values) +{ + System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); + break; +} +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static System.Collections.Generic.IEnumerable Produce() + { + try + { + string s = "value "; + try + { + yield return 42; + } + finally + { + System.Console.Write(s); + } + } + finally + { + System.Console.Write("outer "); + } + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "value value outer True").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 55 (0x37) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.d__0.<>1__state" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldc.i4.s -4 + IL_000a: sub + IL_000b: ldc.i4.1 + IL_000c: ble.un.s IL_0012 + IL_000e: ldloc.0 + IL_000f: ldc.i4.1 + IL_0010: bne.un.s IL_002f + IL_0012: nop + .try + { + IL_0013: ldloc.0 + IL_0014: ldc.i4.s -4 + IL_0016: beq.s IL_001e + IL_0018: ldloc.0 + IL_0019: ldc.i4.1 + IL_001a: beq.s IL_001e + IL_001c: leave.s IL_002f + IL_001e: nop + .try + { + IL_001f: leave.s IL_002f + } + finally + { + IL_0021: ldarg.0 + IL_0022: call "void C.d__0.<>m__Finally2()" + IL_0027: endfinally + } + } + finally + { + IL_0028: ldarg.0 + IL_0029: call "void C.d__0.<>m__Finally1()" + IL_002e: endfinally + } + IL_002f: ldarg.0 + IL_0030: ldnull + IL_0031: stfld "string C.d__0.5__2" + IL_0036: ret +} +"""); + } + + [ConditionalFact(typeof(CoreClrOnly))] + public void AddVariableCleanup_HoistedFromRefExpression() + { + var src = """ +using System.Reflection; + +C c = new C(); +var coll = Test(c); +var enumerator = coll.GetEnumerator(); +try +{ + enumerator.MoveNext(); + System.Console.Write(((C)coll.GetType().GetField("<>7__wrap2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(coll)) is null); +} +finally +{ + enumerator.Dispose(); +} + +System.Console.Write(((C)coll.GetType().GetField("<>7__wrap2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(coll)) is null); + +class C +{ + public Buffer4 F = default; +} + +partial class Program +{ + static System.Collections.Generic.IEnumerable Test(C x) + { + foreach (var y in x.F) + { + yield return -1; + } + } +} + +[System.Runtime.CompilerServices.InlineArray(4)] +public struct Buffer4 +{ + private T _element0; +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); + var verifier = CompileAndVerify(comp, expectedOutput: "FalseTrue", verify: Verification.Skipped).VerifyDiagnostics(); + verifier.VerifyIL("Program.d__1.System.IDisposable.Dispose()", """ +{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldnull + IL_0002: stfld "C Program.d__1.<>7__wrap2" + IL_0007: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_StringLocal_IEnumerator() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); +assert(values.MoveNext()); +assert(values.Current == 42); +assert(!values.MoveNext()); +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); +values.Dispose(); +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +static void assert(bool b) { if (!b) throw new System.Exception(); } + +class C +{ + public static System.Collections.Generic.IEnumerator Produce() + { + string s = "ran "; + yield return 42; + System.Console.Write(s); + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "ran ran True").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldnull + IL_0002: stfld "string C.d__0.5__2" + IL_0007: ret +} +"""); + } + + [Fact] + public void AddVariableCleanup_Unmanaged_UseSiteError() + { + var missingLibS1 = CreateCompilation(@" +public struct S1 +{ + public int i; +} +", assemblyName: "libS1").ToMetadataReference(); + + var libS2 = CreateCompilation(@" +public struct S2 +{ + public S1 s1; +} +", references: [missingLibS1], assemblyName: "libS2").ToMetadataReference(); + + var source = @" +class C +{ + System.Collections.Generic.IEnumerable M1() + { + S2 s2 = M2(); + yield return 42; + System.Console.Write(s2); + } + + S2 M2() => default; +} +"; + var comp = CreateCompilation(source, references: [libS2]); + comp.VerifyEmitDiagnostics( + // error CS0012: The type 'S1' is defined in an assembly that is not referenced. You must add a reference to assembly 'libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + Diagnostic(ErrorCode.ERR_NoTypeDef).WithArguments("S1", "libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 1), + // error CS0012: The type 'S1' is defined in an assembly that is not referenced. You must add a reference to assembly 'libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + Diagnostic(ErrorCode.ERR_NoTypeDef).WithArguments("S1", "libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 1)); + + comp = CreateCompilation(source, options: TestOptions.UnsafeDebugDll, references: [libS2, missingLibS1]); + comp.VerifyEmitDiagnostics(); } } } diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs index f814e6204c9b2..41169ac72a8ff 100644 --- a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs @@ -5176,7 +5176,7 @@ .maxstack 2 v0.VerifyIL("C.d__0.System.IDisposable.Dispose", @" { - // Code size 33 (0x21) + // Code size 40 (0x28) .maxstack 2 .locals init (int V_0) IL_0000: ldarg.0 @@ -5202,12 +5202,15 @@ .locals init (int V_0) IL_001d: endfinally } IL_001e: br.s IL_0020 - IL_0020: ret + IL_0020: ldarg.0 + IL_0021: ldnull + IL_0022: stfld ""System.IDisposable C.d__0.5__1"" + IL_0027: ret } "); diff1.VerifyIL("C.d__0.System.IDisposable.Dispose", @" { - // Code size 35 (0x23) + // Code size 42 (0x2a) .maxstack 2 .locals init (int V_0) IL_0000: ldarg.0 @@ -5235,7 +5238,10 @@ .locals init (int V_0) IL_001f: endfinally } IL_0020: br.s IL_0022 - IL_0022: ret + IL_0022: ldarg.0 + IL_0023: ldnull + IL_0024: stfld ""System.IDisposable C.d__0.5__1"" + IL_0029: ret } "); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/UseSiteErrorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/UseSiteErrorTests.cs index 172b3255fee4b..5bb1106bc6dc9 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/UseSiteErrorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/UseSiteErrorTests.cs @@ -2728,6 +2728,8 @@ async Task M1() "; var comp = CreateCompilation(source, references: new[] { UnmanagedUseSiteError_Ref2 }); comp.VerifyEmitDiagnostics( + // error CS0012: The type 'S1' is defined in an assembly that is not referenced. You must add a reference to assembly 'libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + Diagnostic(ErrorCode.ERR_NoTypeDef).WithArguments("S1", "libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 1), // error CS0012: The type 'S1' is defined in an assembly that is not referenced. You must add a reference to assembly 'libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. Diagnostic(ErrorCode.ERR_NoTypeDef).WithArguments("S1", "libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 1), // error CS0012: The type 'S1' is defined in an assembly that is not referenced. You must add a reference to assembly 'libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. From 8f973187666d58d3b4dc9b1f7ff1bad71ee7688c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 21 Nov 2024 16:31:24 -0800 Subject: [PATCH 472/508] Keep comments when removing unnecessary lambda expressions --- ...ssaryLambdaExpressionDiagnosticAnalyzer.cs | 22 ++++----- ...ecessaryLambdaExpressionCodeFixProvider.cs | 32 +++++++++++- .../RemoveUnnecessaryLambdaExpressionTests.cs | 49 +++++++++++++++++-- .../QuickInfo/SemanticQuickInfoSourceTests.cs | 11 ++++- 4 files changed, 94 insertions(+), 20 deletions(-) diff --git a/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryLambdaExpression/CSharpRemoveUnnecessaryLambdaExpressionDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryLambdaExpression/CSharpRemoveUnnecessaryLambdaExpressionDiagnosticAnalyzer.cs index 089f5c0521045..cd7c2b26446f1 100644 --- a/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryLambdaExpression/CSharpRemoveUnnecessaryLambdaExpressionDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryLambdaExpression/CSharpRemoveUnnecessaryLambdaExpressionDiagnosticAnalyzer.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; @@ -26,18 +25,15 @@ namespace Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryLambdaExpression; /// time. /// [DiagnosticAnalyzer(LanguageNames.CSharp)] -internal sealed class CSharpRemoveUnnecessaryLambdaExpressionDiagnosticAnalyzer : AbstractBuiltInUnnecessaryCodeStyleDiagnosticAnalyzer +internal sealed class CSharpRemoveUnnecessaryLambdaExpressionDiagnosticAnalyzer() + : AbstractBuiltInUnnecessaryCodeStyleDiagnosticAnalyzer( + IDEDiagnosticIds.RemoveUnnecessaryLambdaExpressionDiagnosticId, + EnforceOnBuildValues.RemoveUnnecessaryLambdaExpression, + CSharpCodeStyleOptions.PreferMethodGroupConversion, + fadingOption: null, + new LocalizableResourceString(nameof(CSharpAnalyzersResources.Remove_unnecessary_lambda_expression), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources)), + new LocalizableResourceString(nameof(CSharpAnalyzersResources.Lambda_expression_can_be_removed), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources))) { - public CSharpRemoveUnnecessaryLambdaExpressionDiagnosticAnalyzer() - : base(IDEDiagnosticIds.RemoveUnnecessaryLambdaExpressionDiagnosticId, - EnforceOnBuildValues.RemoveUnnecessaryLambdaExpression, - CSharpCodeStyleOptions.PreferMethodGroupConversion, - fadingOption: null, - new LocalizableResourceString(nameof(CSharpAnalyzersResources.Remove_unnecessary_lambda_expression), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources)), - new LocalizableResourceString(nameof(CSharpAnalyzersResources.Lambda_expression_can_be_removed), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources))) - { - } - public override DiagnosticAnalyzerCategory GetAnalyzerCategory() => DiagnosticAnalyzerCategory.SemanticSpanAnalysis; @@ -51,7 +47,7 @@ protected override void InitializeWorker(AnalysisContext context) var conditionalAttributeType = context.Compilation.ConditionalAttribute(); context.RegisterSyntaxNodeAction( - c => AnalyzeSyntax(c, expressionType, conditionalAttributeType), + context => AnalyzeSyntax(context, expressionType, conditionalAttributeType), SyntaxKind.SimpleLambdaExpression, SyntaxKind.ParenthesizedLambdaExpression, SyntaxKind.AnonymousMethodExpression); } }); diff --git a/src/Analyzers/CSharp/CodeFixes/RemoveUnnecessaryLambdaExpression/CSharpRemoveUnnecessaryLambdaExpressionCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/RemoveUnnecessaryLambdaExpression/CSharpRemoveUnnecessaryLambdaExpressionCodeFixProvider.cs index d219c3bd03a0b..cb003a2520721 100644 --- a/src/Analyzers/CSharp/CodeFixes/RemoveUnnecessaryLambdaExpression/CSharpRemoveUnnecessaryLambdaExpressionCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/RemoveUnnecessaryLambdaExpression/CSharpRemoveUnnecessaryLambdaExpressionCodeFixProvider.cs @@ -3,8 +3,10 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; @@ -12,8 +14,10 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Utilities; namespace Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryLambdaExpression; @@ -39,7 +43,9 @@ protected override Task FixAllAsync( { foreach (var diagnostic in diagnostics) { - var anonymousFunction = diagnostic.AdditionalLocations[0].FindNode(getInnermostNodeForTie: true, cancellationToken); + var anonymousFunction = diagnostic.AdditionalLocations[0].FindNode(getInnermostNodeForTie: true, cancellationToken) as AnonymousFunctionExpressionSyntax; + if (anonymousFunction is null) + continue; editor.ReplaceNode(anonymousFunction, (current, generator) => @@ -52,8 +58,32 @@ protected override Task FixAllAsync( return current; }); + + // If the inner invocation has important trivia on it, move it to the container of the anonymous function. + if (TryGetAnonymousFunctionInvocation(anonymousFunction, out var invocation, out _) && + invocation.GetLeadingTrivia().Any(t => t.IsSingleOrMultiLineComment())) + { + var containingStatement = anonymousFunction.AncestorsAndSelf().OfType().FirstOrDefault(); + if (containingStatement != null) + { + editor.ReplaceNode(containingStatement, + (current, generator) => current + .WithPrependedLeadingTrivia(TakeComments(invocation.GetLeadingTrivia())) + .WithAdditionalAnnotations(Formatter.Annotation)); + } + } } return Task.CompletedTask; } + + private static IEnumerable TakeComments(SyntaxTriviaList triviaList) + { + var lastComment = triviaList.Last(t => t.IsSingleOrMultiLineComment()); + var lastIndex = triviaList.IndexOf(lastComment) + 1; + if (lastIndex < triviaList.Count && triviaList[lastIndex].IsEndOfLine()) + lastIndex++; + + return triviaList.Take(lastIndex); + } } diff --git a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryLambdaExpression/RemoveUnnecessaryLambdaExpressionTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryLambdaExpression/RemoveUnnecessaryLambdaExpressionTests.cs index 7bdc80f506142..233c01c113d65 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryLambdaExpression/RemoveUnnecessaryLambdaExpressionTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryLambdaExpression/RemoveUnnecessaryLambdaExpressionTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp; @@ -19,11 +20,11 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.RemoveUnnecessaryLambda CSharpRemoveUnnecessaryLambdaExpressionCodeFixProvider>; [Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessaryLambdaExpression)] -public class RemoveUnnecessaryLambdaExpressionTests +public sealed class RemoveUnnecessaryLambdaExpressionTests { private static async Task TestInRegularAndScriptAsync( - string testCode, - string fixedCode, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string testCode, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string fixedCode, LanguageVersion version = LanguageVersion.CSharp12, OutputKind? outputKind = null) { @@ -40,7 +41,7 @@ private static async Task TestInRegularAndScriptAsync( } private static Task TestMissingInRegularAndScriptAsync( - string testCode, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string testCode, LanguageVersion version = LanguageVersion.CSharp12, OutputKind? outputKind = null) => TestInRegularAndScriptAsync(testCode, testCode, version, outputKind); @@ -2035,4 +2036,44 @@ public string ToStr(int x) } """); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71300")] + public async Task PreserveComment() + { + await TestInRegularAndScriptAsync(""" + using System; + + class C + { + void M1() + { + M2([|() => + { + // I hope M2 doesn't call M1! + |]M1(); + }); + } + + void M2(Action a) + { + } + } + """, + """ + using System; + + class C + { + void M1() + { + // I hope M2 doesn't call M1! + M2(M1); + } + + void M2(Action a) + { + } + } + """); + } } diff --git a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs index 8b0dc9a91d517..c9f6dfc8ee444 100644 --- a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs +++ b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs @@ -516,7 +516,9 @@ void M() // SingleLine doc comment with '\r' line separators await TestAsync(""" - ///Hello! /// class C { void M() { $$C obj; } } + ///Hello! + /// + class C { void M() { $$C obj; } } """, MainDescription("class C"), Documentation("Hello!")); @@ -632,7 +634,12 @@ void M() // Multiline doc comment with '\r' line separators await TestAsync(""" - /** * * Hello! * */ class C { void M() { $$C obj; } } + /** + * + * Hello! + * + */ + class C { void M() { $$C obj; } } """, MainDescription("class C"), Documentation("Hello!")); From 62625c45b5d92e88d0b9b496a64607517fa4d1f5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 21 Nov 2024 17:19:52 -0800 Subject: [PATCH 473/508] Only run Code Cleanup on documents from the VS workspace --- .../CodeCleanup/AbstractCodeCleanUpFixer.cs | 35 +++++++------------ 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/src/VisualStudio/Core/Def/CodeCleanup/AbstractCodeCleanUpFixer.cs b/src/VisualStudio/Core/Def/CodeCleanup/AbstractCodeCleanUpFixer.cs index 407b0d6dffce1..099c274c0f812 100644 --- a/src/VisualStudio/Core/Def/CodeCleanup/AbstractCodeCleanUpFixer.cs +++ b/src/VisualStudio/Core/Def/CodeCleanup/AbstractCodeCleanUpFixer.cs @@ -9,12 +9,10 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Progress; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; @@ -34,21 +32,14 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeCleanup; /// be implementing the interface, this abstract base class allows Roslyn to operate /// on MEF instances of fixers known to be relevant in the context of Roslyn languages. /// -internal abstract partial class AbstractCodeCleanUpFixer : ICodeCleanUpFixer +internal abstract partial class AbstractCodeCleanUpFixer( + IThreadingContext threadingContext, + VisualStudioWorkspaceImpl workspace, + IVsHierarchyItemManager vsHierarchyItemManager) : ICodeCleanUpFixer { - private readonly IThreadingContext _threadingContext; - private readonly VisualStudioWorkspaceImpl _workspace; - private readonly IVsHierarchyItemManager _vsHierarchyItemManager; - - protected AbstractCodeCleanUpFixer( - IThreadingContext threadingContext, - VisualStudioWorkspaceImpl workspace, - IVsHierarchyItemManager vsHierarchyItemManager) - { - _threadingContext = threadingContext; - _workspace = workspace; - _vsHierarchyItemManager = vsHierarchyItemManager; - } + private readonly IThreadingContext _threadingContext = threadingContext; + private readonly VisualStudioWorkspaceImpl _workspace = workspace; + private readonly IVsHierarchyItemManager _vsHierarchyItemManager = vsHierarchyItemManager; public Task FixAsync(ICodeCleanUpScope scope, ICodeCleanUpExecutionContext context) => scope switch @@ -151,19 +142,17 @@ private Task FixTextBufferAsync(TextBufferCodeCleanUpScope textBufferScope // Let LSP handle code cleanup in the cloud scenario if (buffer.IsInLspEditorContext()) - { return SpecializedTasks.False; - } var document = buffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); if (document == null) - { return SpecializedTasks.False; - } var workspace = buffer.GetWorkspace(); - Contract.ThrowIfNull(workspace); - return FixAsync(workspace, ApplyFixAsync, context); + if (workspace is not VisualStudioWorkspace visualStudioWorkspace) + return SpecializedTasks.False; + + return FixAsync(visualStudioWorkspace, ApplyFixAsync, context); // Local function async Task ApplyFixAsync(IProgress progress, CancellationToken cancellationToken) @@ -177,7 +166,7 @@ async Task ApplyFixAsync(IProgress progress, Can } private async Task FixAsync( - Workspace workspace, + VisualStudioWorkspace workspace, Func, CancellationToken, Task> applyFixAsync, ICodeCleanUpExecutionContext context) { From ee588362c453eab3fc1d0c2b9b0f194f0e529e3c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 21 Nov 2024 17:21:03 -0800 Subject: [PATCH 474/508] Cleanup --- .../LanguageService/CSharpCodeCleanupFixer.cs | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixer.cs b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixer.cs index 95869ad79ed84..b9d7c7e036479 100644 --- a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixer.cs +++ b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixer.cs @@ -7,23 +7,19 @@ using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; using Microsoft.VisualStudio.LanguageServices.Implementation.CodeCleanup; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Utilities; -namespace Microsoft.VisualStudio.LanguageServices.CSharp.LanguageService -{ - [Export(typeof(AbstractCodeCleanUpFixer))] - [ContentType(ContentTypeNames.CSharpContentType)] - internal sealed class CSharpCodeCleanUpFixer : AbstractCodeCleanUpFixer - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpCodeCleanUpFixer(IThreadingContext threadingContext, VisualStudioWorkspaceImpl workspace, IVsHierarchyItemManager vsHierarchyItemManager) - : base(threadingContext, workspace, vsHierarchyItemManager) - { - } - } -} +namespace Microsoft.VisualStudio.LanguageServices.CSharp.LanguageService; + +[Export(typeof(AbstractCodeCleanUpFixer))] +[ContentType(ContentTypeNames.CSharpContentType)] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CSharpCodeCleanUpFixer( + IThreadingContext threadingContext, + VisualStudioWorkspaceImpl workspace, + IVsHierarchyItemManager vsHierarchyItemManager) + : AbstractCodeCleanUpFixer(threadingContext, workspace, vsHierarchyItemManager); From c024fc636eb85884f686819981339285cebf0f79 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 21 Nov 2024 17:56:40 -0800 Subject: [PATCH 475/508] Disable 'use interpolated string' with raw strings --- ...ToInterpolatedStringRefactoringProvider.cs | 10 +++------ ...tConcatenationToInterpolatedStringTests.cs | 22 +++++++++++++++++++ ...ToInterpolatedStringRefactoringProvider.cs | 19 +++++++++++++--- .../Services/SyntaxFacts/CSharpSyntaxFacts.cs | 7 ++++++ .../Core/Services/SyntaxFacts/ISyntaxFacts.cs | 1 + .../SyntaxFacts/VisualBasicSyntaxFacts.vb | 5 +++++ 6 files changed, 54 insertions(+), 10 deletions(-) diff --git a/src/Features/CSharp/Portable/ConvertToInterpolatedString/CSharpConvertConcatenationToInterpolatedStringRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertToInterpolatedString/CSharpConvertConcatenationToInterpolatedStringRefactoringProvider.cs index 9a7cf2e5ebc2b..7c6d6a1f69ee5 100644 --- a/src/Features/CSharp/Portable/ConvertToInterpolatedString/CSharpConvertConcatenationToInterpolatedStringRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertToInterpolatedString/CSharpConvertConcatenationToInterpolatedStringRefactoringProvider.cs @@ -12,15 +12,11 @@ namespace Microsoft.CodeAnalysis.CSharp.ConvertToInterpolatedString; [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.ConvertConcatenationToInterpolatedString), Shared] -internal sealed class CSharpConvertConcatenationToInterpolatedStringRefactoringProvider : +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CSharpConvertConcatenationToInterpolatedStringRefactoringProvider() : AbstractConvertConcatenationToInterpolatedStringRefactoringProvider { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpConvertConcatenationToInterpolatedStringRefactoringProvider() - { - } - protected override bool SupportsInterpolatedStringHandler(Compilation compilation) => compilation.GetTypeByMetadataName("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler") != null; diff --git a/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.cs b/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.cs index 93af30d6414fe..c4a59e07811fc 100644 --- a/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.cs +++ b/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.cs @@ -1388,4 +1388,26 @@ public override string ToString() ReferenceAssemblies = ReferenceAssemblies.Net.Net60, }.RunAsync(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69721")] + public async Task TestWithRawString() + { + await new VerifyCS.Test + { + TestCode = """" + struct ValueTuple + { + public void Goo() + { + var someVariable = "Some text"; + + var fullText = someVariable [||]+ """ + Appended line + """; + } + } + """", + LanguageVersion = LanguageVersion.CSharp11, + }.RunAsync(); + } } diff --git a/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertConcatenationToInterpolatedStringRefactoringProvider.cs b/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertConcatenationToInterpolatedStringRefactoringProvider.cs index 0b6cb035c8e7c..6da34202c6444 100644 --- a/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertConcatenationToInterpolatedStringRefactoringProvider.cs +++ b/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertConcatenationToInterpolatedStringRefactoringProvider.cs @@ -80,6 +80,7 @@ public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContex return; var isVerbatimStringLiteral = false; + if (stringLiterals.Length > 0) { @@ -89,10 +90,18 @@ public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContex // escape sequences in these strings, so we only support combining the string // tokens if they're all the same type. var firstStringToken = stringLiterals[0].GetFirstToken(); + isVerbatimStringLiteral = syntaxFacts.IsVerbatimStringLiteral(firstStringToken); - for (int i = 1, n = stringLiterals.Length; i < n; i++) + + foreach (var literal in stringLiterals) { - if (isVerbatimStringLiteral != syntaxFacts.IsVerbatimStringLiteral(stringLiterals[i].GetFirstToken())) + var firstToken = literal.GetFirstToken(); + if (isVerbatimStringLiteral != syntaxFacts.IsVerbatimStringLiteral(firstToken)) + return; + + // Not currently supported with raw string literals due to the complexity of merging them and forming + // the final interpolated raw string. + if (syntaxFacts.IsRawStringLiteral(firstToken)) return; } } @@ -117,7 +126,10 @@ private async Task UpdateDocumentAsync( } protected async Task CreateInterpolatedStringAsync( - Document document, bool isVerbatimStringLiteral, ImmutableArray pieces, CancellationToken cancellationToken) + Document document, + bool isVerbatimStringLiteral, + ImmutableArray pieces, + CancellationToken cancellationToken) { var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var supportsInterpolatedStringHandler = this.SupportsInterpolatedStringHandler(semanticModel.Compilation); @@ -133,6 +145,7 @@ protected async Task CreateInterpolatedStringAsync( using var _ = ArrayBuilder.GetInstance(pieces.Length, out var content); var previousContentWasStringLiteralExpression = false; + foreach (var piece in pieces) { var isCharacterLiteral = syntaxFacts.IsCharacterLiteralExpression(piece); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs index 74652e0c0418f..b22d533aaec0d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs @@ -1196,6 +1196,13 @@ public SyntaxList GetContentsOfInterpolatedString(SyntaxNode interpo public bool IsVerbatimStringLiteral(SyntaxToken token) => token.IsVerbatimStringLiteral(); + public bool IsRawStringLiteral(SyntaxToken token) + => token.Kind() is + SyntaxKind.SingleLineRawStringLiteralToken or + SyntaxKind.MultiLineRawStringLiteralToken or + SyntaxKind.Utf8SingleLineRawStringLiteralToken or + SyntaxKind.Utf8MultiLineRawStringLiteralToken; + public bool IsNumericLiteral(SyntaxToken token) => token.Kind() == SyntaxKind.NumericLiteralToken; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs index cd199f1d8fabd..d4b01afb7e636 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs @@ -162,6 +162,7 @@ internal interface ISyntaxFacts bool IsNumericLiteral(SyntaxToken token); bool IsVerbatimStringLiteral(SyntaxToken token); + bool IsRawStringLiteral(SyntaxToken token); bool IsUsingOrExternOrImport([NotNullWhen(true)] SyntaxNode? node); bool IsGlobalAssemblyAttribute([NotNullWhen(true)] SyntaxNode? node); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb index bf8f406ff26f6..f763bf800fc50 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb @@ -1252,6 +1252,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Return False End Function + Public Function IsRawStringLiteral(token As SyntaxToken) As Boolean Implements ISyntaxFacts.IsRawStringLiteral + ' VB does not have raw strings + Return False + End Function + Public Function GetArgumentsOfObjectCreationExpression(node As SyntaxNode) As SeparatedSyntaxList(Of SyntaxNode) Implements ISyntaxFacts.GetArgumentsOfObjectCreationExpression Dim argumentList = DirectCast(node, ObjectCreationExpressionSyntax).ArgumentList Return If(argumentList Is Nothing, Nothing, GetArgumentsOfArgumentList(argumentList)) From 5a9a16ada2a676ddf88c366a349fb166ad18880a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 21 Nov 2024 17:57:02 -0800 Subject: [PATCH 476/508] Fix --- .../ConvertConcatenationToInterpolatedStringTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.cs b/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.cs index c4a59e07811fc..7756bb368d640 100644 --- a/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.cs +++ b/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.cs @@ -1389,7 +1389,7 @@ public override string ToString() }.RunAsync(); } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69721")] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/61256")] public async Task TestWithRawString() { await new VerifyCS.Test From 91f96d00139e2e7f9a74d9fbdbc47ce0047a83d8 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 21 Nov 2024 18:34:16 -0800 Subject: [PATCH 477/508] Fix explicit generation of static conversion methods in interfaces --- ...CSharpImplementInterfaceCodeFixProvider.cs | 3 +- .../ImplementInterfaceTests.cs | 47 ++++++++++++++++++- .../CodeGeneration/ConversionGenerator.cs | 34 ++++++++------ 3 files changed, 67 insertions(+), 17 deletions(-) diff --git a/src/Analyzers/CSharp/CodeFixes/ImplementInterface/CSharpImplementInterfaceCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/ImplementInterface/CSharpImplementInterfaceCodeFixProvider.cs index 87ff9b07f4752..0e57a94e02e1f 100644 --- a/src/Analyzers/CSharp/CodeFixes/ImplementInterface/CSharpImplementInterfaceCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/ImplementInterface/CSharpImplementInterfaceCodeFixProvider.cs @@ -15,7 +15,8 @@ namespace Microsoft.CodeAnalysis.CSharp.ImplementInterface; [ExtensionOrder(After = PredefinedCodeFixProviderNames.ImplementAbstractClass)] [method: ImportingConstructor] [method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] -internal sealed class CSharpImplementInterfaceCodeFixProvider() : AbstractImplementInterfaceCodeFixProvider +internal sealed class CSharpImplementInterfaceCodeFixProvider() + : AbstractImplementInterfaceCodeFixProvider { private const string CS0535 = nameof(CS0535); // 'Program' does not implement interface member 'System.Collections.IEnumerable.GetEnumerator()' private const string CS0737 = nameof(CS0737); // 'Class' does not implement interface member 'IInterface.M()'. 'Class.M()' cannot implement an interface member because it is not public. diff --git a/src/Analyzers/CSharp/Tests/ImplementInterface/ImplementInterfaceTests.cs b/src/Analyzers/CSharp/Tests/ImplementInterface/ImplementInterfaceTests.cs index ef616c40e1044..d1bcb162d4d50 100644 --- a/src/Analyzers/CSharp/Tests/ImplementInterface/ImplementInterfaceTests.cs +++ b/src/Analyzers/CSharp/Tests/ImplementInterface/ImplementInterfaceTests.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.ImplementInterface; @@ -377,7 +376,7 @@ class Class : IInterface } } """, -codeAction: ("True;False;False:global::IInterface;Microsoft.CodeAnalysis.ImplementInterface.AbstractImplementInterfaceService+ImplementInterfaceCodeAction;", 1)); + codeAction: ("True;False;False:global::IInterface;Microsoft.CodeAnalysis.ImplementInterface.AbstractImplementInterfaceService+ImplementInterfaceCodeAction;", 1)); } [Fact, CompilerTrait(CompilerFeature.Tuples)] @@ -11933,4 +11932,48 @@ event System.EventHandler I.Click } }.RunAsync(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/61263")] + public async Task ImplementStaticConversionsExplicitly() + { + await new VerifyCS.Test + { + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + LanguageVersion = LanguageVersion.CSharp12, + TestCode = """ + interface I11 where T11 : I11 + { + static abstract implicit operator long(T11 x); + static abstract explicit operator int(T11 x); + } + + class C11 : {|CS0535:{|CS0535:I11|}|} + { + } + """, + FixedCode = """ + interface I11 where T11 : I11 + { + static abstract implicit operator long(T11 x); + static abstract explicit operator int(T11 x); + } + + class C11 : I11 + { + static implicit I11.operator long(C11 x) + { + throw new System.NotImplementedException(); + } + + static explicit I11.operator int(C11 x) + { + throw new System.NotImplementedException(); + } + } + """, + CodeActionIndex = 1, + CodeActionEquivalenceKey = "True;False;False:global::I11;Microsoft.CodeAnalysis.ImplementInterface.AbstractImplementInterfaceService+ImplementInterfaceCodeAction;", + CodeActionVerifier = (codeAction, verifier) => verifier.Equal(CodeFixesResources.Implement_all_members_explicitly, codeAction.Title), + }.RunAsync(); + } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ConversionGenerator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ConversionGenerator.cs index 62452baed9146..2ff50184f0e60 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ConversionGenerator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ConversionGenerator.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.CSharp.Extensions; @@ -48,34 +49,31 @@ private static ConversionOperatorDeclarationSyntax GenerateConversionDeclaration CSharpCodeGenerationContextInfo info, CancellationToken cancellationToken) { - var hasNoBody = !info.Context.GenerateMethodBodies || method.IsExtern; - var reusableSyntax = GetReuseableSyntaxNodeForSymbol(method, info); if (reusableSyntax != null) - { return reusableSyntax; - } var keyword = method.MetadataName == WellKnownMemberNames.ImplicitConversionName ? ImplicitKeyword : ExplicitKeyword; - var checkedToken = SyntaxFacts.IsCheckedOperator(method.MetadataName) + var checkedKeyword = SyntaxFacts.IsCheckedOperator(method.MetadataName) ? CheckedKeyword : default; + var hasNoBody = !info.Context.GenerateMethodBodies || method.IsExtern; var declaration = ConversionOperatorDeclaration( attributeLists: AttributeGenerator.GenerateAttributeLists(method.GetAttributes(), info), - modifiers: GenerateModifiers(destination), + modifiers: GenerateModifiers(destination, method), implicitOrExplicitKeyword: keyword, - explicitInterfaceSpecifier: null, + explicitInterfaceSpecifier: GenerateExplicitInterfaceSpecifier(method.ExplicitInterfaceImplementations), operatorKeyword: OperatorKeyword, - checkedKeyword: checkedToken, + checkedKeyword: checkedKeyword, type: method.ReturnType.GenerateTypeSyntax(), parameterList: ParameterGenerator.GenerateParameterList(method.Parameters, isExplicit: false, info: info), body: hasNoBody ? null : StatementGenerator.GenerateBlock(method), expressionBody: null, - semicolonToken: hasNoBody ? SemicolonToken : new SyntaxToken()); + semicolonToken: hasNoBody ? SemicolonToken : default); declaration = UseExpressionBodyIfDesired(info, declaration, cancellationToken); @@ -100,11 +98,19 @@ private static ConversionOperatorDeclarationSyntax UseExpressionBodyIfDesired( return declaration; } - private static SyntaxTokenList GenerateModifiers(CodeGenerationDestination destination) + private static SyntaxTokenList GenerateModifiers(CodeGenerationDestination destination, IMethodSymbol method) { - // If these appear in interfaces they must be static abstract - return destination is CodeGenerationDestination.InterfaceType - ? ([StaticKeyword, AbstractKeyword]) - : ([PublicKeyword, StaticKeyword]); + // Only "static" allowed if we're an explicit impl. + if (method.ExplicitInterfaceImplementations.Any()) + { + return method.IsStatic ? [StaticKeyword] : []; + } + else + { + // If these appear in interfaces they must be static abstract + return destination is CodeGenerationDestination.InterfaceType + ? [StaticKeyword, AbstractKeyword] + : [PublicKeyword, StaticKeyword]; + } } } From fa63ad41b3bc3142f8827444ef0a0eeb5bc4ff08 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 21 Nov 2024 18:43:31 -0800 Subject: [PATCH 478/508] Remove unnecessary parentheses around collection expressions --- ...veUnnecessaryExpressionParenthesesTests.cs | 38 +++++++++++++++---- .../UseConditionalExpressionForReturnTests.cs | 31 +++++++++++++++ ...ParenthesizedExpressionSyntaxExtensions.cs | 4 ++ 3 files changed, 66 insertions(+), 7 deletions(-) diff --git a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryParentheses/RemoveUnnecessaryExpressionParenthesesTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryParentheses/RemoveUnnecessaryExpressionParenthesesTests.cs index a8d90e34f4f95..f7b5eca75f4ad 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryParentheses/RemoveUnnecessaryExpressionParenthesesTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryParentheses/RemoveUnnecessaryExpressionParenthesesTests.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; @@ -20,17 +21,16 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.RemoveUnnecessaryParentheses; [Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessaryParentheses)] -public partial class RemoveUnnecessaryExpressionParenthesesTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_NoEditor +public sealed class RemoveUnnecessaryExpressionParenthesesTests(ITestOutputHelper logger) + : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_NoEditor(logger) { - public RemoveUnnecessaryExpressionParenthesesTests(ITestOutputHelper logger) - : base(logger) - { - } - internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) => (new CSharpRemoveUnnecessaryExpressionParenthesesDiagnosticAnalyzer(), new CSharpRemoveUnnecessaryParenthesesCodeFixProvider()); - private async Task TestAsync(string initial, string expected, bool offeredWhenRequireForClarityIsEnabled, int index = 0) + private async Task TestAsync( + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string initial, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string expected, + bool offeredWhenRequireForClarityIsEnabled, int index = 0) { await TestInRegularAndScriptAsync(initial, expected, options: RemoveAllUnnecessaryParentheses, index: index); @@ -3458,4 +3458,28 @@ public void M() } """); } + + [Fact] + public async Task TestRemoveAroundCollectionExpression() + { + await TestInRegularAndScriptAsync( + """ + class C + { + void M(bool b) + { + int[] a = b ? $$([1]) : []; + } + } + """, + """ + class C + { + void M(bool b) + { + int[] a = b ? [1] : []; + } + } + """); + } } diff --git a/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForReturnTests.cs b/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForReturnTests.cs index e942959c3fbab..667934aeeba06 100644 --- a/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForReturnTests.cs +++ b/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForReturnTests.cs @@ -2308,4 +2308,35 @@ public static string Method(bool empty) } """); } + + [Fact] + public async Task TestWithCollectionExpressions() + { + await TestInRegularAndScript1Async( + """ + class C + { + int[] M() + { + [||]if (true) + { + return [0]; + } + else + { + return [1]; + } + } + } + """, + """ + class C + { + int[] M() + { + return true ? [0] : [1]; + } + } + """); + } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs index 02042426e6d6c..4c3dbcd9d9ef9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs @@ -111,6 +111,10 @@ public static bool CanRemoveParentheses( if (expression.IsKind(SyntaxKind.TupleExpression)) return true; + // ([...]) -> [...] + if (expression.IsKind(SyntaxKind.CollectionExpression)) + return true; + // int Prop => (x); -> int Prop => x; if (nodeParent is ArrowExpressionClauseSyntax arrowExpressionClause && arrowExpressionClause.Expression == node) { From 9ce3c820c604b27dd84ac5268dfae0f35fd69edc Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 21 Nov 2024 18:47:23 -0800 Subject: [PATCH 479/508] Update test --- .../Tests/ImplementInterface/ImplementInterfaceTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Analyzers/CSharp/Tests/ImplementInterface/ImplementInterfaceTests.cs b/src/Analyzers/CSharp/Tests/ImplementInterface/ImplementInterfaceTests.cs index d1bcb162d4d50..eaa059b5972bd 100644 --- a/src/Analyzers/CSharp/Tests/ImplementInterface/ImplementInterfaceTests.cs +++ b/src/Analyzers/CSharp/Tests/ImplementInterface/ImplementInterfaceTests.cs @@ -11537,12 +11537,12 @@ class C3 : I1 throw new System.NotImplementedException(); } - public static explicit operator checked string(C3 x) + static explicit I1.operator checked string(C3 x) { throw new System.NotImplementedException(); } - public static explicit operator string(C3 x) + static explicit I1.operator string(C3 x) { throw new System.NotImplementedException(); } From 56b2cdefa1aa874c68762819feeb5f583dbeeb55 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 21 Nov 2024 19:00:45 -0800 Subject: [PATCH 480/508] Apply suggestions from code review --- .../Workspace/CSharp/CodeGeneration/ConversionGenerator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ConversionGenerator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ConversionGenerator.cs index 2ff50184f0e60..d8fa523840519 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ConversionGenerator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ConversionGenerator.cs @@ -109,8 +109,8 @@ private static SyntaxTokenList GenerateModifiers(CodeGenerationDestination desti { // If these appear in interfaces they must be static abstract return destination is CodeGenerationDestination.InterfaceType - ? [StaticKeyword, AbstractKeyword] - : [PublicKeyword, StaticKeyword]; + ? ([StaticKeyword, AbstractKeyword]) + : ([PublicKeyword, StaticKeyword]); } } } From 12beae15101f95871bba23803521471bbb1fe4fc Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 21 Nov 2024 19:15:59 -0800 Subject: [PATCH 481/508] Fix location where inheritdoc doc comment is placed --- .../AddInheritdocCodeFixProvider.cs | 56 +++++------- .../Tests/AddInheritdoc/AddInheritdocTests.cs | 91 +++++++++++++++---- 2 files changed, 98 insertions(+), 49 deletions(-) diff --git a/src/Analyzers/CSharp/CodeFixes/AddInheritdoc/AddInheritdocCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/AddInheritdoc/AddInheritdocCodeFixProvider.cs index eab69a511fc47..465f33a6f4f84 100644 --- a/src/Analyzers/CSharp/CodeFixes/AddInheritdoc/AddInheritdocCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/AddInheritdoc/AddInheritdocCodeFixProvider.cs @@ -8,18 +8,18 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Text; -using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; +using Microsoft.CodeAnalysis.CSharp.Extensions; namespace Microsoft.CodeAnalysis.CSharp.CodeFixes.AddInheritdoc; using static CSharpSyntaxTokens; +using static SyntaxFactory; [ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.AddInheritdoc), Shared] [method: ImportingConstructor] @@ -37,33 +37,21 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var document = context.Document; var cancellationToken = context.CancellationToken; - var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - if (root is null) - { - return; - } + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - SemanticModel? semanticModel = null; + var semanticModel = await context.Document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); foreach (var diagnostic in context.Diagnostics) { var node = root.FindNode(diagnostic.Location.SourceSpan); if (node.Kind() is not SyntaxKind.MethodDeclaration and not SyntaxKind.PropertyDeclaration and not SyntaxKind.VariableDeclarator) - { continue; - } - if (node.IsKind(SyntaxKind.VariableDeclarator) && node.Parent?.Parent?.IsKind(SyntaxKind.EventFieldDeclaration) == false) - { + if (node.IsKind(SyntaxKind.VariableDeclarator) && node is not { Parent.Parent: EventFieldDeclarationSyntax }) continue; - } - - semanticModel ??= await context.Document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var symbol = semanticModel.GetDeclaredSymbol(node, cancellationToken); if (symbol is null) - { continue; - } if (symbol.Kind is SymbolKind.Method or SymbolKind.Property or SymbolKind.Event) { @@ -78,21 +66,15 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) protected override async Task FixAllAsync(Document document, ImmutableArray diagnostics, SyntaxEditor editor, CancellationToken cancellationToken) { - string? newLine = null; - SourceText? sourceText = null; + var options = await document.GetLineFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); + var newLine = options.NewLine; + var sourceText = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); + foreach (var diagnostic in diagnostics) { var node = editor.OriginalRoot.FindNode(diagnostic.Location.SourceSpan); - if (node.IsKind(SyntaxKind.VariableDeclarator) && !(node = node.Parent?.Parent).IsKind(SyntaxKind.EventFieldDeclaration)) - { - continue; - } - - if (newLine == null) - { - var options = await document.GetLineFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); - newLine = options.NewLine; - } + if (node is VariableDeclaratorSyntax { Parent.Parent: EventFieldDeclarationSyntax eventField }) + node = eventField; // We can safely assume, that there is no leading doc comment, because that is what CS1591 is telling us. // So we create a new /// comment. @@ -112,13 +94,21 @@ protected override async Task FixAllAsync(Document document, ImmutableArray; [Trait(Traits.Feature, Traits.Features.CodeActionsAddInheritdoc)] -public class AddInheritdocTests +public sealed class AddInheritdocTests { - private static async Task TestAsync(string initialMarkup, string expectedMarkup) - { - var test = new VerifyCS.Test + private static Task TestAsync(string initialMarkup, string expectedMarkup) + => new VerifyCS.Test { TestCode = initialMarkup, FixedCode = expectedMarkup, CodeActionValidationMode = CodeActionValidationMode.Full, - }; - await test.RunAsync(); - } + }.RunAsync(); - private static async Task TestMissingAsync(string initialMarkup) - => await VerifyCS.VerifyCodeFixAsync(initialMarkup, initialMarkup); + private static Task TestMissingAsync(string initialMarkup) + => VerifyCS.VerifyCodeFixAsync(initialMarkup, initialMarkup); [Fact] - public async Task AddMissingInheritdocOnOverridenMethod() + public async Task AddMissingInheritdocOnOverriddenMethod() { await TestAsync( """ @@ -69,7 +67,7 @@ public override void M() { } [InlineData("public void {|CS1591:OtherMethod|}() { }")] [InlineData("public void {|CS1591:M|}() { }")] [InlineData("public new void {|CS1591:M|}() { }")] - public async Task DoNotOfferOnNotOverridenMethod(string methodDefintion) + public async Task DoNotOfferOnNotOverriddenMethod(string methodDefinition) { await TestMissingAsync( $$""" @@ -82,7 +80,7 @@ public virtual void M() { } /// Some doc. public class Derived: BaseClass { - {{methodDefintion}} + {{methodDefinition}} } """); } @@ -139,7 +137,7 @@ void IInterface.M() { } """); } [Fact] - public async Task AddMissingInheritdocOnOverridenProperty() + public async Task AddMissingInheritdocOnOverriddenProperty() { await TestAsync( """ @@ -269,8 +267,8 @@ public virtual void M() { } /// Some doc. public class Derived: BaseClass { - /// // Comment + /// public override void M() { } } """); @@ -304,9 +302,9 @@ public virtual void M() { } /// Some doc. public class Derived: BaseClass { - /// // Comment 1 - /* Comment 2 */ public /* Comment 3 */ override void M /* Comment 4 */ () /* Comment 5 */ { } /* Comment 6 */ + /* Comment 2 */ /// + public /* Comment 3 */ override void M /* Comment 4 */ () /* Comment 5 */ { } /* Comment 6 */ } """); } @@ -397,4 +395,65 @@ public override void M() { } } """); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/61562")] + public async Task TestOverrideBelowMember() + { + await TestAsync( + """ + using System; + + /// + /// Hello C1 + /// + public abstract class Class1 + { + /// + /// Hello C1.DoStuff + /// + public abstract void DoStuff(); + } + + /// + /// Hello C2 + /// + public class Class2 : Class1 + { + private const int Number = 1; + + public override void {|CS1591:DoStuff|}() + { + throw new NotImplementedException(); + } + } + """, + """ + using System; + + /// + /// Hello C1 + /// + public abstract class Class1 + { + /// + /// Hello C1.DoStuff + /// + public abstract void DoStuff(); + } + + /// + /// Hello C2 + /// + public class Class2 : Class1 + { + private const int Number = 1; + + /// + public override void {|CS1591:DoStuff|}() + { + throw new NotImplementedException(); + } + } + """); + } } From ec42cb151dd0e53215a95bb780db74a9562011bb Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 21 Nov 2024 19:32:51 -0800 Subject: [PATCH 482/508] Add test demonstrating issue doesn't arise --- .../CSharpUseNotPatternDiagnosticAnalyzer.cs | 3 +- .../CSharpUseNotPatternTests.cs | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/Analyzers/CSharp/Analyzers/UsePatternMatching/CSharpUseNotPatternDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UsePatternMatching/CSharpUseNotPatternDiagnosticAnalyzer.cs index da2601fd3a508..9638b15efc86f 100644 --- a/src/Analyzers/CSharp/Analyzers/UsePatternMatching/CSharpUseNotPatternDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UsePatternMatching/CSharpUseNotPatternDiagnosticAnalyzer.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; @@ -98,7 +97,7 @@ private void SyntaxNodeAction( isKeyword.GetLocation(), styleOption.Notification, context.Options, - ImmutableArray.Create(node.GetLocation()), + [node.GetLocation()], properties: null)); } } diff --git a/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpUseNotPatternTests.cs b/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpUseNotPatternTests.cs index 97c6ac0dd572c..98f97bbd10ddd 100644 --- a/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpUseNotPatternTests.cs +++ b/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpUseNotPatternTests.cs @@ -467,4 +467,44 @@ void M() LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/62078")] + public async Task TestTypeVersusMemberAmbiguity() + { + await new VerifyCS.Test + { + TestCode = """ + class Color { } + + class C + { + public const int Color = 0; + + void M(object x) + { + if (!(x [|is|] Color)) + { + } + } + } + """, + FixedCode = """ + class Color { } + + class C + { + public const int Color = 0; + + void M(object x) + { + if (x is not global::Color) + { + } + } + } + """, + LanguageVersion = LanguageVersion.CSharp9, + CodeActionValidationMode = Testing.CodeActionValidationMode.None, + }.RunAsync(); + } } From f09bab150a069ad7e2f090f6a67e2d159e15adfc Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 21 Nov 2024 20:02:38 -0800 Subject: [PATCH 483/508] Disable symbol search on generated documents --- .../IntroduceVariableTests.cs | 43 +++++++++++++++++++ .../RemoteMissingImportDiscoveryService.cs | 36 ++++++++-------- 2 files changed, 60 insertions(+), 19 deletions(-) diff --git a/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs b/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs index abe706c762c20..4cd431a92f0c9 100644 --- a/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs +++ b/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs @@ -8984,4 +8984,47 @@ public class C } """); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71042")] + public async Task TestWithGenericsInLocalFunction() + { + await TestInRegularAndScriptAsync( + """ + using System.Collections.Generic; + + record struct ID(T Key); + + class C + { + void M(int id) + { + void N(T id, Dictionary, string> d) + { + if (d.TryGetValue([|new(id)|], out string? s)) + { + } + } + } + } + """, + """ + using System.Collections.Generic; + + record struct ID(T Key); + + class C + { + void M(int id) + { + void N(T id, Dictionary, string> d) + { + ID {|Rename:key|} = new(id); + if (d.TryGetValue(key, out string? s)) + { + } + } + } + } + """); + } } diff --git a/src/Workspaces/Remote/ServiceHub/Services/MissingImportDiscovery/RemoteMissingImportDiscoveryService.cs b/src/Workspaces/Remote/ServiceHub/Services/MissingImportDiscovery/RemoteMissingImportDiscoveryService.cs index ce5eefcae2312..8261f14ec5cd5 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/MissingImportDiscovery/RemoteMissingImportDiscoveryService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/MissingImportDiscovery/RemoteMissingImportDiscoveryService.cs @@ -13,7 +13,10 @@ namespace Microsoft.CodeAnalysis.Remote; -internal sealed class RemoteMissingImportDiscoveryService : BrokeredServiceBase, IRemoteMissingImportDiscoveryService +internal sealed class RemoteMissingImportDiscoveryService( + in BrokeredServiceBase.ServiceConstructionArguments arguments, + RemoteCallback callback) + : BrokeredServiceBase(arguments), IRemoteMissingImportDiscoveryService { internal sealed class Factory : FactoryBase { @@ -21,13 +24,7 @@ protected override IRemoteMissingImportDiscoveryService CreateService(in Service => new RemoteMissingImportDiscoveryService(arguments, callback); } - private readonly RemoteCallback _callback; - - public RemoteMissingImportDiscoveryService(in ServiceConstructionArguments arguments, RemoteCallback callback) - : base(arguments) - { - _callback = callback; - } + private readonly RemoteCallback _callback = callback; public ValueTask> GetFixesAsync( Checksum solutionChecksum, @@ -42,7 +39,9 @@ public ValueTask> GetFixesAsync( { return RunServiceAsync(solutionChecksum, async solution => { - var document = solution.GetRequiredDocument(documentId); + var document = solution.GetDocument(documentId); + if (document is null) + return []; var service = document.GetRequiredLanguageService(); @@ -69,7 +68,9 @@ public ValueTask> GetUniqueFixesAsync( { return RunServiceAsync(solutionChecksum, async solution => { - var document = solution.GetRequiredDocument(documentId); + var document = solution.GetDocument(documentId); + if (document is null) + return []; var service = document.GetRequiredLanguageService(); @@ -94,16 +95,13 @@ public ValueTask> GetUniqueFixesAsync( /// /// Ideally we would not need to bounce back to the host for this. /// - private sealed class SymbolSearchService : ISymbolSearchService + private sealed class SymbolSearchService( + RemoteCallback callback, + RemoteServiceCallbackId callbackId) + : ISymbolSearchService { - private readonly RemoteCallback _callback; - private readonly RemoteServiceCallbackId _callbackId; - - public SymbolSearchService(RemoteCallback callback, RemoteServiceCallbackId callbackId) - { - _callback = callback; - _callbackId = callbackId; - } + private readonly RemoteCallback _callback = callback; + private readonly RemoteServiceCallbackId _callbackId = callbackId; public ValueTask> FindPackagesWithTypeAsync(string source, string name, int arity, CancellationToken cancellationToken) => _callback.InvokeAsync((callback, cancellationToken) => callback.FindPackagesWithTypeAsync(_callbackId, source, name, arity, cancellationToken), cancellationToken); From 0eb7a3e90b4765c8a4b8bdca86070f020eb83059 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 21 Nov 2024 20:03:32 -0800 Subject: [PATCH 484/508] revert --- .../IntroduceVariableTests.cs | 43 ------------------- 1 file changed, 43 deletions(-) diff --git a/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs b/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs index 4cd431a92f0c9..abe706c762c20 100644 --- a/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs +++ b/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs @@ -8984,47 +8984,4 @@ public class C } """); } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71042")] - public async Task TestWithGenericsInLocalFunction() - { - await TestInRegularAndScriptAsync( - """ - using System.Collections.Generic; - - record struct ID(T Key); - - class C - { - void M(int id) - { - void N(T id, Dictionary, string> d) - { - if (d.TryGetValue([|new(id)|], out string? s)) - { - } - } - } - } - """, - """ - using System.Collections.Generic; - - record struct ID(T Key); - - class C - { - void M(int id) - { - void N(T id, Dictionary, string> d) - { - ID {|Rename:key|} = new(id); - if (d.TryGetValue(key, out string? s)) - { - } - } - } - } - """); - } } From 0d9bcf1e7010a56bbf2af4fd66993278a4259708 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 21 Nov 2024 21:44:33 -0800 Subject: [PATCH 485/508] Add the 'scoped' modifier in metadata-as-source --- ...FunctionToMethodCodeRefactoringProvider.cs | 6 +-- .../CodeGeneration/CSharpSyntaxGenerator.cs | 4 +- .../Core/Portable/Editing/SyntaxGenerator.cs | 3 +- .../CodeGeneration/ParameterGenerator.cs | 3 +- ...olExtensions.TypeSyntaxGeneratorVisitor.cs | 4 +- .../CSharpSyntaxGeneratorInternal.cs | 49 +++++++++++++++---- .../SyntaxGeneratorInternal.cs | 4 ++ 7 files changed, 51 insertions(+), 22 deletions(-) diff --git a/src/Features/CSharp/Portable/CodeRefactorings/ConvertLocalFunctionToMethod/CSharpConvertLocalFunctionToMethodCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/CodeRefactorings/ConvertLocalFunctionToMethod/CSharpConvertLocalFunctionToMethodCodeRefactoringProvider.cs index da17567e32e99..4b12e0084164c 100644 --- a/src/Features/CSharp/Portable/CodeRefactorings/ConvertLocalFunctionToMethod/CSharpConvertLocalFunctionToMethodCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/CodeRefactorings/ConvertLocalFunctionToMethod/CSharpConvertLocalFunctionToMethodCodeRefactoringProvider.cs @@ -264,11 +264,11 @@ private static List GenerateUniqueParameterNames(ImmutableArray GetReservedNames(SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken) => semanticModel.GetAllDeclaredSymbols(node.GetAncestor(), cancellationToken).Select(s => s.Name).ToList(); - private static ParameterSyntax GenerateParameter(IParameterSymbol p, string name) + private static ParameterSyntax GenerateParameter(IParameterSymbol parameter, string name) { return SyntaxFactory.Parameter(name.ToIdentifierToken()) - .WithModifiers(CSharpSyntaxGeneratorInternal.GetParameterModifiers(p.RefKind)) - .WithType(p.Type.GenerateTypeSyntax()); + .WithModifiers(CSharpSyntaxGeneratorInternal.GetParameterModifiers(parameter)) + .WithType(parameter.Type.GenerateTypeSyntax()); } private static MethodDeclarationSyntax WithBodyFrom( diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs index 95dc4d1a23c5d..f398bf6985f7e 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs @@ -190,9 +190,7 @@ public override SyntaxNode FieldDeclaration( private protected override SyntaxNode ParameterDeclaration( string name, SyntaxNode? type, SyntaxNode? initializer, RefKind refKind, bool isExtension, bool isParams, bool isScoped) { - var modifiers = CSharpSyntaxGeneratorInternal.GetParameterModifiers(refKind); - if (isScoped) - modifiers = modifiers.Insert(0, ScopedKeyword); + var modifiers = CSharpSyntaxGeneratorInternal.GetParameterModifiers(isScoped, refKind); if (isExtension) modifiers = modifiers.Insert(0, ThisKeyword); diff --git a/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs b/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs index 70da68cf4cf59..e1dcfdf45f301 100644 --- a/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs +++ b/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs @@ -328,8 +328,7 @@ public SyntaxNode ParameterDeclaration(IParameterSymbol symbol, SyntaxNode? init symbol.RefKind, isExtension: symbol is { Ordinal: 0, ContainingSymbol: IMethodSymbol { IsExtensionMethod: true } }, symbol.IsParams, - isScoped: symbol is { RefKind: RefKind.Ref or RefKind.In or RefKind.RefReadOnlyParameter, ScopedKind: ScopedKind.ScopedRef } - or { RefKind: RefKind.None, Type.IsRefLikeType: true, ScopedKind: ScopedKind.ScopedValue }); + isScoped: SyntaxGeneratorInternal.ParameterIsScoped(symbol)); } private protected abstract SyntaxNode TypeParameter(ITypeParameterSymbol typeParameter); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ParameterGenerator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ParameterGenerator.cs index dde1cec02d278..4335961372e21 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ParameterGenerator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ParameterGenerator.cs @@ -7,7 +7,6 @@ using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.CSharp.CodeGeneration; @@ -76,7 +75,7 @@ internal static ParameterSyntax GetParameter(IParameterSymbol parameter, CSharpC private static SyntaxTokenList GenerateModifiers( IParameterSymbol parameter, bool isFirstParam) { - var list = CSharpSyntaxGeneratorInternal.GetParameterModifiers(parameter.RefKind); + var list = CSharpSyntaxGeneratorInternal.GetParameterModifiers(parameter); if (isFirstParam && parameter.ContainingSymbol is IMethodSymbol methodSymbol && diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.TypeSyntaxGeneratorVisitor.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.TypeSyntaxGeneratorVisitor.cs index c7d6581c6c6a4..22b6c9ab6bdce 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.TypeSyntaxGeneratorVisitor.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.TypeSyntaxGeneratorVisitor.cs @@ -161,10 +161,10 @@ static FunctionPointerUnmanagedCallingConventionSyntax GetConventionForString(st => FunctionPointerUnmanagedCallingConvention(Identifier(identifier)); } - var parameters = symbol.Signature.Parameters.Select(p => (p.Type, RefKindModifiers: CSharpSyntaxGeneratorInternal.GetParameterModifiers(p.RefKind))) + var parameters = symbol.Signature.Parameters.Select(p => (p.Type, RefKindModifiers: CSharpSyntaxGeneratorInternal.GetParameterModifiers(p))) .Concat([( Type: symbol.Signature.ReturnType, - RefKindModifiers: CSharpSyntaxGeneratorInternal.GetParameterModifiers(symbol.Signature.RefKind, forFunctionPointerReturnParameter: true))]) + RefKindModifiers: CSharpSyntaxGeneratorInternal.GetParameterModifiers(isScoped: false, symbol.Signature.RefKind, forFunctionPointerReturnParameter: true))]) .SelectAsArray(t => FunctionPointerParameter(t.Type.GenerateTypeSyntax()).WithModifiers(t.RefKindModifiers)); return AddInformationTo( diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxGeneratorInternal.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxGeneratorInternal.cs index 3d1893fcdf224..993aaf2805553 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxGeneratorInternal.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxGeneratorInternal.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.CodeGeneration; @@ -132,20 +133,48 @@ public override SyntaxNode InterpolationFormatClause(string format) public override SyntaxNode TypeParameterList(IEnumerable typeParameterNames) => SyntaxFactory.TypeParameterList([.. typeParameterNames.Select(SyntaxFactory.TypeParameter)]); - internal static SyntaxTokenList GetParameterModifiers(RefKind refKind, bool forFunctionPointerReturnParameter = false) - => refKind switch + internal static SyntaxTokenList GetParameterModifiers( + IParameterSymbol parameter, bool forFunctionPointerReturnParameter = false) + => GetParameterModifiers(ParameterIsScoped(parameter), parameter.RefKind, forFunctionPointerReturnParameter); + + internal static SyntaxTokenList GetParameterModifiers( + bool isScoped, RefKind refKind, bool forFunctionPointerReturnParameter = false) + { + using var _ = ArrayBuilder.GetInstance(out var result); + + if (isScoped) + result.Add(ScopedKeyword); + + switch (refKind) { - RefKind.None => [], - RefKind.Out => [OutKeyword], - RefKind.Ref => [RefKeyword], + case RefKind.Out: + result.Add(OutKeyword); + break; + + case RefKind.Ref: + result.Add(RefKeyword); + break; + // Note: RefKind.RefReadonly == RefKind.In. Function Pointers must use the correct // ref kind syntax when generating for the return parameter vs other parameters. // The return parameter must use ref readonly, like regular methods. - RefKind.In when !forFunctionPointerReturnParameter => [InKeyword], - RefKind.RefReadOnly when forFunctionPointerReturnParameter => [RefKeyword, ReadOnlyKeyword], - RefKind.RefReadOnlyParameter => [RefKeyword, ReadOnlyKeyword], - _ => throw ExceptionUtilities.UnexpectedValue(refKind), - }; + case RefKind.In when !forFunctionPointerReturnParameter: + result.Add(InKeyword); + break; + + case RefKind.RefReadOnly when forFunctionPointerReturnParameter: + result.Add(RefKeyword); + result.Add(ReadOnlyKeyword); + break; + + case RefKind.RefReadOnlyParameter: + result.Add(RefKeyword); + result.Add(ReadOnlyKeyword); + break; + } + + return SyntaxFactory.TokenList(result); + } public override SyntaxNode Type(ITypeSymbol typeSymbol, bool typeContext) => typeContext ? typeSymbol.GenerateTypeSyntax() : typeSymbol.GenerateExpressionSyntax(); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SyntaxGeneratorInternalExtensions/SyntaxGeneratorInternal.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SyntaxGeneratorInternalExtensions/SyntaxGeneratorInternal.cs index b87a79832bdf4..5ab8c6e63bc72 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SyntaxGeneratorInternalExtensions/SyntaxGeneratorInternal.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SyntaxGeneratorInternalExtensions/SyntaxGeneratorInternal.cs @@ -102,6 +102,10 @@ public SyntaxNode LocalDeclarationStatement(SyntaxToken name, SyntaxNode initial public abstract SyntaxNode IsNotTypeExpression(SyntaxNode expression, SyntaxNode type); + internal static bool ParameterIsScoped(IParameterSymbol symbol) + => symbol is { RefKind: RefKind.Ref or RefKind.In or RefKind.RefReadOnlyParameter, ScopedKind: ScopedKind.ScopedRef } + or { RefKind: RefKind.None, Type.IsRefLikeType: true, ScopedKind: ScopedKind.ScopedValue }; + #region Patterns public abstract bool SupportsPatterns(ParseOptions options); From 1a7204c26b31c3ebab71026bb9509aa2ff5937b0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 21 Nov 2024 21:48:05 -0800 Subject: [PATCH 486/508] Tests --- .../MetadataAsSourceTests.CSharp.cs | 1142 +++++++++-------- 1 file changed, 594 insertions(+), 548 deletions(-) diff --git a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.CSharp.cs b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.CSharp.cs index 29e81ac37afbf..dc5c07138302f 100644 --- a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.CSharp.cs +++ b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.CSharp.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Threading.Tasks; using Microsoft.CodeAnalysis.MetadataAsSource; using Microsoft.CodeAnalysis.Test.Utilities; @@ -20,13 +18,17 @@ public class CSharp : AbstractMetadataAsSourceTests [Fact] public void ExtractXMLFromDocComment() { - var docCommentText = @"/// -/// I am the very model of a modern major general. -/// "; + var docCommentText = """ + /// + /// I am the very model of a modern major general. + /// + """; - var expectedXMLFragment = @" - I am the very model of a modern major general. - "; + var expectedXMLFragment = """ + + I am the very model of a modern major general. + + """; var extractedXMLFragment = DocumentationCommentUtilities.ExtractXMLFragment(docCommentText, "///"); @@ -42,35 +44,39 @@ public async Task TestNativeInteger(bool signaturesOnly) var expected = signaturesOnly switch { - true => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class [|C|] -{{ - public nint i; - public nuint i2; - - public C(); -}}", - false => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|C|] -{{ - public nint i; - - public nuint i2; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + true => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class [|C|] + { + public nint i; + public nuint i2; + + public C(); + } + """, + false => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|C|] + { + public nint i; + + public nuint i2; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, languageVersion: "Preview", metadataLanguageVersion: "Preview", expected: expected, signaturesOnly: signaturesOnly); @@ -79,42 +85,48 @@ public class [|C|] [Theory, CombinatorialData] public async Task TestInitOnlyProperty(bool signaturesOnly) { - var metadataSource = @"public class C { public int Property { get; init; } } -namespace System.Runtime.CompilerServices -{ - public sealed class IsExternalInit { } -} -"; + var metadataSource = """ + public class C { public int Property { get; init; } } + namespace System.Runtime.CompilerServices + { + public sealed class IsExternalInit { } + } + + """; var symbolName = "C"; var expected = signaturesOnly switch { - true => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class [|C|] -{{ - public C(); - - public int Property {{ get; init; }} -}}", - false => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|C|] -{{ - public int Property {{ get; init; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + true => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class [|C|] + { + public C(); + + public int Property { get; init; } + } + """, + false => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|C|] + { + public int Property { get; init; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, languageVersion: "Preview", metadataLanguageVersion: "Preview", expected: expected, signaturesOnly: signaturesOnly); @@ -128,39 +140,43 @@ public async Task TestTupleWithNames(bool signaturesOnly) var expected = signaturesOnly switch { - true => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System.Runtime.CompilerServices; - -public class [|C|] -{{ - [TupleElementNames(new[] {{ ""a"", ""b"" }})] - public (int a, int b) t; - - public C(); -}}", - false => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|C|] -{{ - public (int a, int b) t; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} ------------------- -{string.Format(FeaturesResources.Resolve_0, "System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51")} -{string.Format(FeaturesResources.Found_single_assembly_0, "System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51")} -{string.Format(FeaturesResources.Load_from_0, "System.ValueTuple (net461)")} -#endif", + true => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System.Runtime.CompilerServices; + + public class [|C|] + { + [TupleElementNames(new[] { "a", "b" })] + public (int a, int b) t; + + public C(); + } + """, + false => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|C|] + { + public (int a, int b) t; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51")}} + {{string.Format(FeaturesResources.Load_from_0, "System.ValueTuple (net461)")}} + #endif + """, }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, expected: expected, signaturesOnly: signaturesOnly); @@ -173,214 +189,218 @@ public async Task TestValueTuple(bool signaturesOnly) var expected = signaturesOnly switch { - true => $@"#region {FeaturesResources.Assembly} System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51 -// System.ValueTuple (net461) -#endregion - -using System.Collections; - -namespace System -{{ - public struct [|ValueTuple|] : IEquatable, IStructuralEquatable, IStructuralComparable, IComparable, IComparable, ITupleInternal - {{ - public static ValueTuple Create(); - public static ValueTuple Create(T1 item1); - public static (T1, T2) Create(T1 item1, T2 item2); - public static (T1, T2, T3) Create(T1 item1, T2 item2, T3 item3); - public static (T1, T2, T3, T4) Create(T1 item1, T2 item2, T3 item3, T4 item4); - public static (T1, T2, T3, T4, T5) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5); - public static (T1, T2, T3, T4, T5, T6) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6); - public static (T1, T2, T3, T4, T5, T6, T7) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7); - public static (T1, T2, T3, T4, T5, T6, T7, T8) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8); - public int CompareTo(ValueTuple other); - public override bool Equals(object obj); - public bool Equals(ValueTuple other); - public override int GetHashCode(); - public override string ToString(); - }} -}}", - false => $@"#region {FeaturesResources.Assembly} System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51 -// System.ValueTuple (net461) -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Collections; -using System.Numerics.Hashing; -using System.Runtime.InteropServices; - -namespace System; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct [|ValueTuple|] : IEquatable, IStructuralEquatable, IStructuralComparable, IComparable, IComparable, ITupleInternal -{{ - int ITupleInternal.Size => 0; - - public override bool Equals(object obj) - {{ - return obj is ValueTuple; - }} - - public bool Equals(ValueTuple other) - {{ - return true; - }} - - bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) - {{ - return other is ValueTuple; - }} - - int IComparable.CompareTo(object other) - {{ - if (other == null) - {{ - return 1; - }} - - if (!(other is ValueTuple)) - {{ - throw new ArgumentException(System.SR.ArgumentException_ValueTupleIncorrectType, ""other""); - }} - - return 0; - }} - - public int CompareTo(ValueTuple other) - {{ - return 0; - }} - - int IStructuralComparable.CompareTo(object other, IComparer comparer) - {{ - if (other == null) - {{ - return 1; - }} - - if (!(other is ValueTuple)) - {{ - throw new ArgumentException(System.SR.ArgumentException_ValueTupleIncorrectType, ""other""); - }} - - return 0; - }} - - public override int GetHashCode() - {{ - return 0; - }} - - int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) - {{ - return 0; - }} - - int ITupleInternal.GetHashCode(IEqualityComparer comparer) - {{ - return 0; - }} - - public override string ToString() - {{ - return ""()""; - }} - - string ITupleInternal.ToStringEnd() - {{ - return "")""; - }} - - public static ValueTuple Create() - {{ - return default(ValueTuple); - }} - - public static ValueTuple Create(T1 item1) - {{ - return new ValueTuple(item1); - }} - - public static (T1, T2) Create(T1 item1, T2 item2) - {{ - return (item1, item2); - }} - - public static (T1, T2, T3) Create(T1 item1, T2 item2, T3 item3) - {{ - return (item1, item2, item3); - }} - - public static (T1, T2, T3, T4) Create(T1 item1, T2 item2, T3 item3, T4 item4) - {{ - return (item1, item2, item3, item4); - }} - - public static (T1, T2, T3, T4, T5) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) - {{ - return (item1, item2, item3, item4, item5); - }} - - public static (T1, T2, T3, T4, T5, T6) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) - {{ - return (item1, item2, item3, item4, item5, item6); - }} - - public static (T1, T2, T3, T4, T5, T6, T7) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) - {{ - return (item1, item2, item3, item4, item5, item6, item7); - }} - - public static (T1, T2, T3, T4, T5, T6, T7, T8) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8) - {{ - return new ValueTuple>(item1, item2, item3, item4, item5, item6, item7, Create(item8)); - }} - - internal static int CombineHashCodes(int h1, int h2) - {{ - return HashHelpers.Combine(HashHelpers.Combine(HashHelpers.RandomSeed, h1), h2); - }} - - internal static int CombineHashCodes(int h1, int h2, int h3) - {{ - return HashHelpers.Combine(CombineHashCodes(h1, h2), h3); - }} - - internal static int CombineHashCodes(int h1, int h2, int h3, int h4) - {{ - return HashHelpers.Combine(CombineHashCodes(h1, h2, h3), h4); - }} - - internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5) - {{ - return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4), h5); - }} - - internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6) - {{ - return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5), h6); - }} - - internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7) - {{ - return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5, h6), h7); - }} - - internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7, int h8) - {{ - return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5, h6, h7), h8); - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} ------------------- -{string.Format(FeaturesResources.Resolve_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "System.v4_6_1038_0.dll")} -#endif", + true => $$""" + #region {{FeaturesResources.Assembly}} System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51 + // System.ValueTuple (net461) + #endregion + + using System.Collections; + + namespace System + { + public struct [|ValueTuple|] : IEquatable, IStructuralEquatable, IStructuralComparable, IComparable, IComparable, ITupleInternal + { + public static ValueTuple Create(); + public static ValueTuple Create(T1 item1); + public static (T1, T2) Create(T1 item1, T2 item2); + public static (T1, T2, T3) Create(T1 item1, T2 item2, T3 item3); + public static (T1, T2, T3, T4) Create(T1 item1, T2 item2, T3 item3, T4 item4); + public static (T1, T2, T3, T4, T5) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5); + public static (T1, T2, T3, T4, T5, T6) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6); + public static (T1, T2, T3, T4, T5, T6, T7) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7); + public static (T1, T2, T3, T4, T5, T6, T7, T8) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8); + public int CompareTo(ValueTuple other); + public override bool Equals(object obj); + public bool Equals(ValueTuple other); + public override int GetHashCode(); + public override string ToString(); + } + } + """, + false => $$""" + #region {{FeaturesResources.Assembly}} System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51 + // System.ValueTuple (net461) + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Collections; + using System.Numerics.Hashing; + using System.Runtime.InteropServices; + + namespace System; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct [|ValueTuple|] : IEquatable, IStructuralEquatable, IStructuralComparable, IComparable, IComparable, ITupleInternal + { + int ITupleInternal.Size => 0; + + public override bool Equals(object obj) + { + return obj is ValueTuple; + } + + public bool Equals(ValueTuple other) + { + return true; + } + + bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) + { + return other is ValueTuple; + } + + int IComparable.CompareTo(object other) + { + if (other == null) + { + return 1; + } + + if (!(other is ValueTuple)) + { + throw new ArgumentException(System.SR.ArgumentException_ValueTupleIncorrectType, "other"); + } + + return 0; + } + + public int CompareTo(ValueTuple other) + { + return 0; + } + + int IStructuralComparable.CompareTo(object other, IComparer comparer) + { + if (other == null) + { + return 1; + } + + if (!(other is ValueTuple)) + { + throw new ArgumentException(System.SR.ArgumentException_ValueTupleIncorrectType, "other"); + } + + return 0; + } + + public override int GetHashCode() + { + return 0; + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return 0; + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return 0; + } + + public override string ToString() + { + return "()"; + } + + string ITupleInternal.ToStringEnd() + { + return ")"; + } + + public static ValueTuple Create() + { + return default(ValueTuple); + } + + public static ValueTuple Create(T1 item1) + { + return new ValueTuple(item1); + } + + public static (T1, T2) Create(T1 item1, T2 item2) + { + return (item1, item2); + } + + public static (T1, T2, T3) Create(T1 item1, T2 item2, T3 item3) + { + return (item1, item2, item3); + } + + public static (T1, T2, T3, T4) Create(T1 item1, T2 item2, T3 item3, T4 item4) + { + return (item1, item2, item3, item4); + } + + public static (T1, T2, T3, T4, T5) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) + { + return (item1, item2, item3, item4, item5); + } + + public static (T1, T2, T3, T4, T5, T6) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) + { + return (item1, item2, item3, item4, item5, item6); + } + + public static (T1, T2, T3, T4, T5, T6, T7) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) + { + return (item1, item2, item3, item4, item5, item6, item7); + } + + public static (T1, T2, T3, T4, T5, T6, T7, T8) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8) + { + return new ValueTuple>(item1, item2, item3, item4, item5, item6, item7, Create(item8)); + } + + internal static int CombineHashCodes(int h1, int h2) + { + return HashHelpers.Combine(HashHelpers.Combine(HashHelpers.RandomSeed, h1), h2); + } + + internal static int CombineHashCodes(int h1, int h2, int h3) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2), h3); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3), h4); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4), h5); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5), h6); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5, h6), h7); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7, int h8) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5, h6, h7), h8); + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "System.v4_6_1038_0.dll")}} + #endif + """, }; await context.GenerateAndVerifySourceAsync("System.ValueTuple", expected, signaturesOnly: signaturesOnly); @@ -394,34 +414,38 @@ public async Task TestExtendedPartialMethod1(bool signaturesOnly) var expected = signaturesOnly switch { - true => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class [|C|] -{{ - public C(); - - public void F(); -}}", - false => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|C|] -{{ - public void F() - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + true => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class [|C|] + { + public C(); + + public void F(); + } + """, + false => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|C|] + { + public void F() + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, languageVersion: "Preview", metadataLanguageVersion: "Preview", expected: expected, signaturesOnly: signaturesOnly); @@ -436,56 +460,60 @@ public async Task TestRecordType(bool signaturesOnly) var expected = signaturesOnly switch { - true => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -#nullable enable - -using System; -using System.Runtime.CompilerServices; -using System.Text; - -public record [|R|] : IEquatable -{{ - public R(); - [CompilerGenerated] - protected R(R original); - - [CompilerGenerated] - protected virtual Type EqualityContract {{ get; }} - - [CompilerGenerated] - public virtual R $(); - [CompilerGenerated] - public override bool Equals(object? obj); - [CompilerGenerated] - public virtual bool Equals(R? other); - [CompilerGenerated] - public override int GetHashCode(); - [CompilerGenerated] - public override string ToString(); - [CompilerGenerated] - protected virtual bool PrintMembers(StringBuilder builder); - - [CompilerGenerated] - public static bool operator ==(R? left, R? right); - [CompilerGenerated] - public static bool operator !=(R? left, R? right); -}}", - false => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public record [|R|]; -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + true => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + #nullable enable + + using System; + using System.Runtime.CompilerServices; + using System.Text; + + public record [|R|] : IEquatable + { + public R(); + [CompilerGenerated] + protected R(R original); + + [CompilerGenerated] + protected virtual Type EqualityContract { get; } + + [CompilerGenerated] + public virtual R $(); + [CompilerGenerated] + public override bool Equals(object? obj); + [CompilerGenerated] + public virtual bool Equals(R? other); + [CompilerGenerated] + public override int GetHashCode(); + [CompilerGenerated] + public override string ToString(); + [CompilerGenerated] + protected virtual bool PrintMembers(StringBuilder builder); + + [CompilerGenerated] + public static bool operator ==(R? left, R? right); + [CompilerGenerated] + public static bool operator !=(R? left, R? right); + } + """, + false => $""" + #region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {CodeAnalysisResources.InMemoryAssembly} + // Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} + #endregion + + public record [|R|]; + #if false // {FeaturesResources.Decompilation_log} + {string.Format(FeaturesResources._0_items_in_cache, 6)} + ------------------ + {string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} + {string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} + {string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} + #endif + """, }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, expected: expected, signaturesOnly: signaturesOnly); @@ -498,87 +526,93 @@ public record [|R|]; [WorkItem("https://github.com/dotnet/roslyn/issues/42986")] public async Task TestCheckedOperators(bool signaturesOnly) { - var metadataSource = @" -public class C -{ - public static explicit operator string(C x) => throw new System.Exception(); + var metadataSource = """ - public static explicit operator checked string(C x) => throw new System.Exception(); + public class C + { + public static explicit operator string(C x) => throw new System.Exception(); + + public static explicit operator checked string(C x) => throw new System.Exception(); - public static C operator -(C x) => throw new System.Exception(); + public static C operator -(C x) => throw new System.Exception(); - public static C operator checked -(C x) => throw new System.Exception(); + public static C operator checked -(C x) => throw new System.Exception(); - public static C operator +(C x, C y) => throw new System.Exception(); + public static C operator +(C x, C y) => throw new System.Exception(); - public static C operator checked +(C x, C y) => throw new System.Exception(); -}"; + public static C operator checked +(C x, C y) => throw new System.Exception(); + } + """; var symbolName = "C"; var expected = signaturesOnly switch { - true => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class [|C|] -{{ - public C(); - - public static C operator +(C x, C y); - public static C operator checked +(C x, C y); - public static C operator -(C x); - public static C operator checked -(C x); - - public static explicit operator string(C x); - public static explicit operator checked string(C x); -}}", - false => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; - -public class [|C|] -{{ - public static explicit operator string(C x) - {{ - throw new Exception(); - }} - - public static explicit operator checked string(C x) - {{ - throw new Exception(); - }} - - public static C operator -(C x) - {{ - throw new Exception(); - }} - - public static C operator checked -(C x) - {{ - throw new Exception(); - }} - - public static C operator +(C x, C y) - {{ - throw new Exception(); - }} - - public static C operator checked +(C x, C y) - {{ - throw new Exception(); - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + true => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class [|C|] + { + public C(); + + public static C operator +(C x, C y); + public static C operator checked +(C x, C y); + public static C operator -(C x); + public static C operator checked -(C x); + + public static explicit operator string(C x); + public static explicit operator checked string(C x); + } + """, + false => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + + public class [|C|] + { + public static explicit operator string(C x) + { + throw new Exception(); + } + + public static explicit operator checked string(C x) + { + throw new Exception(); + } + + public static C operator -(C x) + { + throw new Exception(); + } + + public static C operator checked -(C x) + { + throw new Exception(); + } + + public static C operator +(C x, C y) + { + throw new Exception(); + } + + public static C operator checked +(C x, C y) + { + throw new Exception(); + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, languageVersion: "Preview", metadataLanguageVersion: "Preview", expected: expected, signaturesOnly: signaturesOnly); @@ -587,43 +621,47 @@ public static explicit operator checked string(C x) [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/60567")] public async Task TestStaticInterfaceMembers() { - var metadataSource = @" -interface I where T : I -{ - static abstract T P { get; set; } - static abstract event System.Action E; - static abstract void M(); - static void NonAbstract() { } - static abstract T operator +(T l, T r); - static abstract bool operator ==(T l, T r); - static abstract bool operator !=(T l, T r); - static abstract implicit operator T(string s); - static abstract explicit operator string(T t); -}"; + var metadataSource = """ + + interface I where T : I + { + static abstract T P { get; set; } + static abstract event System.Action E; + static abstract void M(); + static void NonAbstract() { } + static abstract T operator +(T l, T r); + static abstract bool operator ==(T l, T r); + static abstract bool operator !=(T l, T r); + static abstract implicit operator T(string s); + static abstract explicit operator string(T t); + } + """; var symbolName = "I`1.M"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion -using System; + using System; -internal interface I where T : I -{{ - static abstract T P {{ get; set; }} + internal interface I where T : I + { + static abstract T P { get; set; } - static abstract event Action E; + static abstract event Action E; - static abstract void [|M|](); - static void NonAbstract(); + static abstract void [|M|](); + static void NonAbstract(); - static abstract T operator +(T l, T r); - static abstract bool operator ==(T l, T r); - static abstract bool operator !=(T l, T r); + static abstract T operator +(T l, T r); + static abstract bool operator ==(T l, T r); + static abstract bool operator !=(T l, T r); - static abstract implicit operator T(string s); - static abstract explicit operator string(T t); -}}"; + static abstract implicit operator T(string s); + static abstract explicit operator string(T t); + } + """; await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, languageVersion: "Preview", metadataLanguageVersion: "Preview", expected: expected, signaturesOnly: true, metadataCommonReferences: "CommonReferencesNet6"); } @@ -636,35 +674,39 @@ public async Task UnsignedRightShift(bool signaturesOnly) var expected = signaturesOnly switch { - true => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class C -{{ - public C(); - - public static C operator [|>>>|](C x, int y); -}}", - false => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class C -{{ - public static C operator [|>>>|](C x, int y) - {{ - return x; - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + true => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class C + { + public C(); + + public static C operator [|>>>|](C x, int y); + } + """, + false => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class C + { + public static C operator [|>>>|](C x, int y) + { + return x; + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, expected: expected, signaturesOnly: signaturesOnly, languageVersion: "Preview", metadataLanguageVersion: "Preview"); @@ -699,36 +741,40 @@ public CompilerFeatureRequiredAttribute(string featureName) // ICSharpDecompiler does not yet support decoding required members nicely var expected = signaturesOnly switch { - true => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class [|C|] -{{ - public required int Field; - - public C(); - - public required int Property {{ get; set; }} -}}", - false => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|C|] -{{ - public required int Field; - - public required int Property {{ get; set; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + true => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class [|C|] + { + public required int Field; + + public C(); + + public required int Property { get; set; } + } + """, + false => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|C|] + { + public required int Field; + + public required int Property { get; set; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, languageVersion: "Preview", metadataLanguageVersion: "Preview", expected: expected, signaturesOnly: signaturesOnly); From bd8a064417bb1816b178aad6c96a31220ce8157a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 21 Nov 2024 21:50:37 -0800 Subject: [PATCH 487/508] Tests --- .../MetadataAsSource/MetadataAsSourceTests.cs | 9074 +++++++++-------- 1 file changed, 4855 insertions(+), 4219 deletions(-) diff --git a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs index f74a6ef7371d3..e0c0bd654e729 100644 --- a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs +++ b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs @@ -2,20 +2,19 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; +using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeGeneration; +using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; using CS = Microsoft.CodeAnalysis.CSharp; using VB = Microsoft.CodeAnalysis.VisualBasic; -using Microsoft.CodeAnalysis.CSharp.Formatting; -using System.Threading; namespace Microsoft.CodeAnalysis.Editor.UnitTests.MetadataAsSource; @@ -45,51 +44,59 @@ public async Task TestClass(OriginatingProjectLanguage language, bool signatures var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class [|C|] -{{ - public C(); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Class [|C|] - Public Sub New() -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|C|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|C|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class [|C|] + { + public C(); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Class [|C|] + Public Sub New() + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|C|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|C|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -104,49 +111,57 @@ public async Task TestInterface(OriginatingProjectLanguage language, bool signat var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public interface [|I|] -{{ -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Interface [|I|] -End Interface", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public interface [|I|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public interface [|I|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public interface [|I|] + { + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Interface [|I|] + End Interface + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public interface [|I|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public interface [|I|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -161,51 +176,59 @@ public async Task TestConstructor(OriginatingProjectLanguage language, bool sign var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class C -{{ - public [|C|](); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Class C - Public Sub [|New|]() -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|C|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|C|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class C + { + public [|C|](); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Class C + Public Sub [|New|]() + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|C|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|C|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -220,61 +243,69 @@ public async Task TestMethod(OriginatingProjectLanguage language, bool signature var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class C -{{ - public C(); - - public void [|Goo|](); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Class C - Public Sub New() - - Public Sub [|Goo|]() -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class C -{{ - public void [|Goo|]() - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class C -{{ - public void [|Goo|]() - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class C + { + public C(); + + public void [|Goo|](); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Class C + Public Sub New() + + Public Sub [|Goo|]() + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class C + { + public void [|Goo|]() + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class C + { + public void [|Goo|]() + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -289,57 +320,65 @@ public async Task TestField(OriginatingProjectLanguage language, bool signatures var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class C -{{ - public string [|S|]; - - public C(); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Class C - Public [|S|] As String - - Public Sub New() -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class C -{{ - public string [|S|]; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class C -{{ - public string [|S|]; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class C + { + public string [|S|]; + + public C(); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Class C + Public [|S|] As String + + Public Sub New() + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class C + { + public string [|S|]; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class C + { + public string [|S|]; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -354,57 +393,65 @@ public async Task TestProperty(OriginatingProjectLanguage language, bool signatu var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class C -{{ - public C(); - - public string [|S|] {{ get; protected set; }} -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Class C - Public Sub New() - - Public Property [|S|] As String -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class C -{{ - public string [|S|] {{ get; protected set; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class C -{{ - public string [|S|] {{ get; protected set; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class C + { + public C(); + + public string [|S|] { get; protected set; } + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Class C + Public Sub New() + + Public Property [|S|] As String + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class C + { + public string [|S|] { get; protected set; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class C + { + public string [|S|] { get; protected set; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -420,65 +467,73 @@ public async Task TestEvent(OriginatingProjectLanguage language, bool signatures var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System; - -public class C -{{ - public C(); - - public event Action [|E|]; -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System - -Public Class C - Public Sub New() - - Public Event [|E|] As Action -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; - -public class C -{{ - public event Action [|E|]; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; - -public class C -{{ - public event Action [|E|]; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System; + + public class C + { + public C(); + + public event Action [|E|]; + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System + + Public Class C + Public Sub New() + + Public Event [|E|] As Action + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + + public class C + { + public event Action [|E|]; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + + public class C + { + public event Action [|E|]; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -493,66 +548,74 @@ public async Task TestNestedType(OriginatingProjectLanguage language, bool signa var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class C -{{ - public C(); - - protected class [|D|] - {{ - public D(); - }} -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Class C - Public Sub New() - - Protected Class [|D|] - Public Sub New() - End Class -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class C -{{ - protected class [|D|] - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class C -{{ - protected class [|D|] - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class C + { + public C(); + + protected class [|D|] + { + public D(); + } + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Class C + Public Sub New() + + Protected Class [|D|] + Public Sub New() + End Class + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class C + { + protected class [|D|] + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class C + { + protected class [|D|] + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -567,61 +630,69 @@ public async Task TestEnum(OriginatingProjectLanguage language, bool signaturesO var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public enum [|E|] -{{ - A = 0, - B = 1, - C = 2 -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Enum [|E|] - A = 0 - B = 1 - C = 2 -End Enum", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public enum [|E|] -{{ - A, - B, - C -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public enum [|E|] -{{ - A, - B, - C -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public enum [|E|] + { + A = 0, + B = 1, + C = 2 + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Enum [|E|] + A = 0 + B = 1 + C = 2 + End Enum + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public enum [|E|] + { + A, + B, + C + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public enum [|E|] + { + A, + B, + C + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -636,61 +707,69 @@ public async Task TestEnumFromField(OriginatingProjectLanguage language, bool si var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public enum E -{{ - A = 0, - B = 1, - [|C|] = 2 -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Enum E - A = 0 - B = 1 - [|C|] = 2 -End Enum", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public enum E -{{ - A, - B, - [|C|] -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public enum E -{{ - A, - B, - [|C|] -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public enum E + { + A = 0, + B = 1, + [|C|] = 2 + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Enum E + A = 0 + B = 1 + [|C|] = 2 + End Enum + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public enum E + { + A, + B, + [|C|] + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public enum E + { + A, + B, + [|C|] + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -705,61 +784,69 @@ public async Task TestEnumWithUnderlyingType(OriginatingProjectLanguage language var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public enum E : short -{{ - A = 0, - B = 1, - [|C|] = 2 -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Enum E As Short - A = 0 - B = 1 - [|C|] = 2 -End Enum", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public enum E : short -{{ - A, - B, - [|C|] -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public enum E : short -{{ - A, - B, - [|C|] -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public enum E : short + { + A = 0, + B = 1, + [|C|] = 2 + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Enum E As Short + A = 0 + B = 1 + [|C|] = 2 + End Enum + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public enum E : short + { + A, + B, + [|C|] + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public enum E : short + { + A, + B, + [|C|] + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -774,53 +861,61 @@ public async Task TestEnumWithOverflowingUnderlyingType(OriginatingProjectLangua var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public enum E : ulong -{{ - [|A|] = 9223372036854775808 -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Enum E As ULong - [|A|] = 9223372036854775808UL -End Enum", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public enum E : ulong -{{ - [|A|] = 9223372036854775808uL -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public enum E : ulong -{{ - [|A|] = 9223372036854775808uL -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public enum E : ulong + { + [|A|] = 9223372036854775808 + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Enum E As ULong + [|A|] = 9223372036854775808UL + End Enum + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public enum E : ulong + { + [|A|] = 9223372036854775808uL + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public enum E : ulong + { + [|A|] = 9223372036854775808uL + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -835,61 +930,69 @@ public async Task TestEnumWithDifferentValues(OriginatingProjectLanguage languag var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public enum E : short -{{ - A = 1, - B = 2, - [|C|] = 3 -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Enum E As Short - A = 1 - B = 2 - [|C|] = 3 -End Enum", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public enum E : short -{{ - A = 1, - B, - [|C|] -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public enum E : short -{{ - A = 1, - B, - [|C|] -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public enum E : short + { + A = 1, + B = 2, + [|C|] = 3 + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Enum E As Short + A = 1 + B = 2 + [|C|] = 3 + End Enum + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public enum E : short + { + A = 1, + B, + [|C|] + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public enum E : short + { + A = 1, + B, + [|C|] + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -904,60 +1007,68 @@ public async Task TestTypeInNamespace(OriginatingProjectLanguage language, bool var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -namespace N -{{ - public class [|C|] - {{ - public C(); - }} -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Namespace N - Public Class [|C|] - Public Sub New() - End Class -End Namespace", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -namespace N; - -public class [|C|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -namespace N; - -public class [|C|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + namespace N + { + public class [|C|] + { + public C(); + } + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Namespace N + Public Class [|C|] + Public Sub New() + End Class + End Namespace + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + namespace N; + + public class [|C|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + namespace N; + + public class [|C|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -973,16 +1084,18 @@ public async Task TestTypeInFileScopedNamespace1() LanguageNames.CSharp, [metadataSource], languageVersion: "10", fileScopedNamespaces: true); await context.GenerateAndVerifySourceAsync("N.C", - $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion + $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion -namespace N; + namespace N; -public class [|C|] -{{ - public C(); -}}"); + public class [|C|] + { + public C(); + } + """); } [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546198")] @@ -994,17 +1107,19 @@ public async Task TestTypeInFileScopedNamespace2() LanguageNames.CSharp, [metadataSource], languageVersion: "9", fileScopedNamespaces: true); await context.GenerateAndVerifySourceAsync("N.C", - $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -namespace N -{{ - public class [|C|] - {{ - public C(); - }} -}}"); + $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + namespace N + { + public class [|C|] + { + public C(); + } + } + """); } [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546198")] @@ -1016,17 +1131,19 @@ public async Task TestTypeInFileScopedNamespace3() LanguageNames.CSharp, [metadataSource], languageVersion: "10"); await context.GenerateAndVerifySourceAsync("N.C", - $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -namespace N -{{ - public class [|C|] - {{ - public C(); - }} -}}"); + $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + namespace N + { + public class [|C|] + { + public C(); + } + } + """); } [WpfTheory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546223"), CombinatorialData] @@ -1037,57 +1154,65 @@ public async Task TestInlineConstant(OriginatingProjectLanguage language, bool s var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class C -{{ - public const string [|S|] = ""Hello mas""; - - public C(); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Class C - Public Const [|S|] As String = ""Hello mas"" - - Public Sub New() -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class C -{{ - public const string [|S|] = ""Hello mas""; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class C -{{ - public const string [|S|] = ""Hello mas""; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class C + { + public const string [|S|] = "Hello mas"; + + public C(); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Class C + Public Const [|S|] As String = "Hello mas" + + Public Sub New() + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class C + { + public const string [|S|] = "Hello mas"; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class C + { + public const string [|S|] = "Hello mas"; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -1097,70 +1222,80 @@ public class C [WpfTheory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546221"), CombinatorialData] public async Task TestInlineTypeOf(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -using System; + var metadataSource = """ -public class MyTypeAttribute : Attribute -{ - public MyTypeAttribute(Type type) {} -} + using System; + + public class MyTypeAttribute : Attribute + { + public MyTypeAttribute(Type type) {} + } -[MyType(typeof(string))] -public class C {}"; + [MyType(typeof(string))] + public class C {} + """; var symbolName = "C"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -[MyType(typeof(string))] -public class [|C|] -{{ - public C(); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - - -Public Class [|C|] - Public Sub New() -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -[MyType(typeof(string))] -public class [|C|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -[MyType(typeof(string))] -public class [|C|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + [MyType(typeof(string))] + public class [|C|] + { + public C(); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + + Public Class [|C|] + Public Sub New() + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + [MyType(typeof(string))] + public class [|C|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + [MyType(typeof(string))] + public class [|C|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -1175,55 +1310,63 @@ public async Task TestNoDefaultConstructorInStructs(OriginatingProjectLanguage l var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public struct [|S|] -{{ -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Structure [|S|] -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct [|S|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct [|S|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public struct [|S|] + { + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Structure [|S|] + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct [|S|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct [|S|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -1238,63 +1381,71 @@ public async Task TestReferenceDefinedType(OriginatingProjectLanguage language, var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class [|C|] -{{ - public C(); - - public static C Create(); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Class [|C|] - Public Sub New() - - Public Shared Function Create() As C -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|C|] -{{ - public static C Create() - {{ - return new C(); - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|C|] -{{ - public static C Create() - {{ - return new C(); - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class [|C|] + { + public C(); + + public static C Create(); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Class [|C|] + Public Sub New() + + Public Shared Function Create() As C + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|C|] + { + public static C Create() + { + return new C(); + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|C|] + { + public static C Create() + { + return new C(); + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -1309,57 +1460,65 @@ public async Task TestGenericType(OriginatingProjectLanguage language, bool sign var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class [|G|] -{{ - public SomeType S; - - public G(); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Class [|G|](Of SomeType) - Public S As SomeType - - Public Sub New() -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|G|] -{{ - public SomeType S; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|G|] -{{ - public SomeType S; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class [|G|] + { + public SomeType S; + + public G(); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Class [|G|](Of SomeType) + Public S As SomeType + + Public Sub New() + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|G|] + { + public SomeType S; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|G|] + { + public SomeType S; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -1370,73 +1529,83 @@ public class [|G|] [WorkItem("https://github.com/dotnet/roslyn/issues/38916")] public async Task TestParameterAttributes(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public class C<[My] T> -{ - public void Method([My] T x, [My] T y) { } -} + var metadataSource = """ -internal class MyAttribute : System.Attribute { } -"; + public class C<[My] T> + { + public void Method([My] T x, [My] T y) { } + } + + internal class MyAttribute : System.Attribute { } + + """; var symbolName = "C`1"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class [|C|]<[MyAttribute] T> -{{ - public C(); - - public void Method([MyAttribute] T x, [MyAttribute] T y); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Class [|C|](Of T) - Public Sub New() - - Public Sub Method( x As T, y As T) -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|C|]<[My] T> -{{ - public void Method([My] T x, [My] T y) - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|C|]<[My] T> -{{ - public void Method([My] T x, [My] T y) - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class [|C|]<[MyAttribute] T> + { + public C(); + + public void Method([MyAttribute] T x, [MyAttribute] T y); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Class [|C|](Of T) + Public Sub New() + + Public Sub Method( x As T, y As T) + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|C|]<[My] T> + { + public void Method([My] T x, [My] T y) + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|C|]<[My] T> + { + public void Method([My] T x, [My] T y) + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -1447,66 +1616,76 @@ public void Method([My] T x, [My] T y) [WorkItem("https://github.com/dotnet/roslyn/issues/38916")] public async Task TestGenericWithNullableReferenceTypes(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -#nullable enable -public interface C -{ - bool Equals([AllowNull] T other); -} + var metadataSource = """ + + #nullable enable + public interface C + { + bool Equals([AllowNull] T other); + } -internal class AllowNullAttribute : System.Attribute { } -"; + internal class AllowNullAttribute : System.Attribute { } + + """; var symbolName = "C`1"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public interface [|C|] -{{ - bool Equals([AllowNullAttribute] T other); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Interface [|C|](Of T) - Function Equals( other As T) As Boolean -End Interface", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public interface [|C|] -{{ - bool Equals([AllowNull] T other); -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public interface [|C|] -{{ - bool Equals([AllowNull] T other); -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public interface [|C|] + { + bool Equals([AllowNullAttribute] T other); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Interface [|C|](Of T) + Function Equals( other As T) As Boolean + End Interface + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public interface [|C|] + { + bool Equals([AllowNull] T other); + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public interface [|C|] + { + bool Equals([AllowNull] T other); + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -1521,56 +1700,64 @@ public async Task TestGenericDelegate(OriginatingProjectLanguage language, bool var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class C -{{ - public C(); - - public delegate void [|D|](SomeType s); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Class C - Public Sub New() - Public Delegate Sub [|D|](Of SomeType)(s As SomeType) -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class C -{{ - public delegate void [|D|](SomeType s); -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class C -{{ - public delegate void [|D|](SomeType s); -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class C + { + public C(); + + public delegate void [|D|](SomeType s); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Class C + Public Sub New() + Public Delegate Sub [|D|](Of SomeType)(s As SomeType) + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class C + { + public delegate void [|D|](SomeType s); + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class C + { + public delegate void [|D|](SomeType s); + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -1580,81 +1767,91 @@ public class C [WpfTheory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546200"), CombinatorialData] public async Task TestAttribute(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -using System; + var metadataSource = """ -namespace N -{ - public class WorkingAttribute : Attribute - { - public WorkingAttribute(bool working) {} - } -} + using System; + + namespace N + { + public class WorkingAttribute : Attribute + { + public WorkingAttribute(bool working) {} + } + } -[N.Working(true)] -public class C {}"; + [N.Working(true)] + public class C {} + """; var symbolName = "C"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using N; - -[Working(true)] -public class [|C|] -{{ - public C(); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports N - - -Public Class [|C|] - Public Sub New() -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using N; - -[Working(true)] -public class [|C|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using N; - -[Working(true)] -public class [|C|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using N; + + [Working(true)] + public class [|C|] + { + public C(); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports N + + + Public Class [|C|] + Public Sub New() + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using N; + + [Working(true)] + public class [|C|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using N; + + [Working(true)] + public class [|C|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -1712,6 +1909,7 @@ public async Task TestWorkspaceContextHasReasonableProjectName() { using var context = TestContext.Create(); var compilation = await context.DefaultProject.GetCompilationAsync(); + Assert.NotNull(compilation); var result = await context.GenerateSourceAsync(compilation.ObjectType); var openedDocument = context.GetDocument(result); @@ -1724,7 +1922,7 @@ public async Task TestReuseGenerateFromDifferentProject() { using var context = TestContext.Create(); var projectId = ProjectId.CreateNewId(); - var project = context.CurrentSolution.AddProject(projectId, "ProjectB", "ProjectB", LanguageNames.CSharp).GetProject(projectId) + var project = context.CurrentSolution.AddProject(projectId, "ProjectB", "ProjectB", LanguageNames.CSharp).GetRequiredProject(projectId) .WithMetadataReferences(context.DefaultProject.MetadataReferences) .WithCompilationOptions(new CS.CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); @@ -1738,7 +1936,7 @@ public async Task TestNotReusedGeneratingForDifferentLanguage() { using var context = TestContext.Create(LanguageNames.CSharp); var projectId = ProjectId.CreateNewId(); - var project = context.CurrentSolution.AddProject(projectId, "ProjectB", "ProjectB", LanguageNames.VisualBasic).GetProject(projectId) + var project = context.CurrentSolution.AddProject(projectId, "ProjectB", "ProjectB", LanguageNames.VisualBasic).GetRequiredProject(projectId) .WithMetadataReferences(context.DefaultProject.MetadataReferences) .WithCompilationOptions(new VB.VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); @@ -1759,28 +1957,32 @@ public async Task FormatMetadataAsSource() [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530829")] public async Task IndexedProperty() { - var metadataSource = @" -Public Class C - Public Property IndexProp(ByVal p1 As Integer) As String - Get - Return Nothing - End Get - Set(ByVal value As String) - - End Set - End Property -End Class"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class C -{{ - public C(); - - public string [|get_IndexProp|](int p1); - public void set_IndexProp(int p1, string value); -}}"; + var metadataSource = """ + + Public Class C + Public Property IndexProp(ByVal p1 As Integer) As String + Get + Return Nothing + End Get + Set(ByVal value As String) + + End Set + End Property + End Class + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class C + { + public C(); + + public string [|get_IndexProp|](int p1); + public void set_IndexProp(int p1, string value); + } + """; var symbolName = "C.get_IndexProp"; await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, expected); } @@ -1788,29 +1990,33 @@ public class C [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/566688")] public async Task AttributeReferencingInternalNestedType() { - var metadataSource = @"using System; -[My(typeof(D))] -public class C -{ - public C() { } - - internal class D { } -} - -public class MyAttribute : Attribute -{ - public MyAttribute(Type t) { } -}"; - - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -[My(typeof(D))] -public class [|C|] -{{ - public C(); -}}"; + var metadataSource = """ + using System; + [My(typeof(D))] + public class C + { + public C() { } + + internal class D { } + } + + public class MyAttribute : Attribute + { + public MyAttribute(Type t) { } + } + """; + + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + [My(typeof(D))] + public class [|C|] + { + public C(); + } + """; var symbolName = "C"; await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, expected); } @@ -1818,304 +2024,314 @@ public class [|C|] [WpfTheory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530978"), CombinatorialData] public async Task TestAttributesOnMembers(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @"using System; + var metadataSource = """ + using System; -[Obsolete] -public class C -{ - [Obsolete] - [ThreadStatic] - public int field1; + [Obsolete] + public class C + { + [Obsolete] + [ThreadStatic] + public int field1; - [Obsolete] - public int prop1 { get; set; } + [Obsolete] + public int prop1 { get; set; } - [Obsolete] - public int prop2 { get { return 10; } set {} } + [Obsolete] + public int prop2 { get { return 10; } set {} } - [Obsolete] - public void method1() {} + [Obsolete] + public void method1() {} - [Obsolete] - public C() {} + [Obsolete] + public C() {} - [Obsolete] - ~C() {} + [Obsolete] + ~C() {} - [Obsolete] - public int this[int x] { get { return 10; } set {} } + [Obsolete] + public int this[int x] { get { return 10; } set {} } - [Obsolete] - public event Action event1; + [Obsolete] + public event Action event1; - [Obsolete] - public event Action event2 { add {} remove {}} + [Obsolete] + public event Action event2 { add {} remove {}} - public void method2([System.Runtime.CompilerServices.CallerMemberName] string name = """") {} + public void method2([System.Runtime.CompilerServices.CallerMemberName] string name = "") {} - [Obsolete] - public static C operator + (C c1, C c2) { return new C(); } -} -"; + [Obsolete] + public static C operator + (C c1, C c2) { return new C(); } + } + + """; var symbolName = "C"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System; -using System.Reflection; -using System.Runtime.CompilerServices; - -[DefaultMember(""Item"")] -[Obsolete] -public class [|C|] -{{ - [Obsolete] - [ThreadStatic] - public int field1; - - [Obsolete] - public C(); - - [Obsolete] - ~C(); - - [Obsolete] - public int this[int x] {{ get; set; }} - - [Obsolete] - public int prop1 {{ get; set; }} - [Obsolete] - public int prop2 {{ get; set; }} - - [Obsolete] - public event Action event1; - [Obsolete] - public event Action event2; - - [Obsolete] - public void method1(); - public void method2([CallerMemberName] string name = """"); - - [Obsolete] - public static C operator +(C c1, C c2); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System -Imports System.Reflection -Imports System.Runtime.CompilerServices - - -Public Class [|C|] - - Public field1 As Integer - - - Public Sub New() - - - Public Property prop1 As Integer - - Public Property prop2 As Integer - - Default Public Property Item(x As Integer) As Integer - - - Public Event event1 As Action - - Public Event event2 As Action - - - Public Sub method1() - Public Sub method2( Optional name As String = """") - - Protected Overrides Sub Finalize() - - - Public Shared Operator +(c1 As C, c2 As C) As C -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; -using System.Runtime.CompilerServices; - -[Obsolete] -public class [|C|] -{{ - [Obsolete] - [ThreadStatic] - public int field1; - - [Obsolete] - public int prop1 {{ get; set; }} - - [Obsolete] - public int prop2 - {{ - get - {{ - return 10; - }} - set - {{ - }} - }} - - [Obsolete] - public int this[int x] - {{ - get - {{ - return 10; - }} - set - {{ - }} - }} - - [Obsolete] - public event Action event1; - - [Obsolete] - public event Action event2 - {{ - add - {{ - }} - remove - {{ - }} - }} - - [Obsolete] - public void method1() - {{ - }} - - [Obsolete] - public C() - {{ - }} - - [Obsolete] - ~C() - {{ - }} - - public void method2([CallerMemberName] string name = """") - {{ - }} - - [Obsolete] - public static C operator +(C c1, C c2) - {{ - return new C(); - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; -using System.Runtime.CompilerServices; - -[Obsolete] -public class [|C|] -{{ - [Obsolete] - [ThreadStatic] - public int field1; - - [Obsolete] - public int prop1 {{ get; set; }} - - [Obsolete] - public int prop2 - {{ - get - {{ - return 10; - }} - set - {{ - }} - }} - - [Obsolete] - public int this[int x] - {{ - get - {{ - return 10; - }} - set - {{ - }} - }} - - [Obsolete] - public event Action event1; - - [Obsolete] - public event Action event2 - {{ - add - {{ - }} - remove - {{ - }} - }} - - [Obsolete] - public void method1() - {{ - }} - - [Obsolete] - public C() - {{ - }} - - [Obsolete] - ~C() - {{ - }} - - public void method2([CallerMemberName] string name = """") - {{ - }} - - [Obsolete] - public static C operator +(C c1, C c2) - {{ - return new C(); - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System; + using System.Reflection; + using System.Runtime.CompilerServices; + + [DefaultMember("Item")] + [Obsolete] + public class [|C|] + { + [Obsolete] + [ThreadStatic] + public int field1; + + [Obsolete] + public C(); + + [Obsolete] + ~C(); + + [Obsolete] + public int this[int x] { get; set; } + + [Obsolete] + public int prop1 { get; set; } + [Obsolete] + public int prop2 { get; set; } + + [Obsolete] + public event Action event1; + [Obsolete] + public event Action event2; + + [Obsolete] + public void method1(); + public void method2([CallerMemberName] string name = ""); + + [Obsolete] + public static C operator +(C c1, C c2); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System + Imports System.Reflection + Imports System.Runtime.CompilerServices + + + Public Class [|C|] + + Public field1 As Integer + + + Public Sub New() + + + Public Property prop1 As Integer + + Public Property prop2 As Integer + + Default Public Property Item(x As Integer) As Integer + + + Public Event event1 As Action + + Public Event event2 As Action + + + Public Sub method1() + Public Sub method2( Optional name As String = "") + + Protected Overrides Sub Finalize() + + + Public Shared Operator +(c1 As C, c2 As C) As C + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + using System.Runtime.CompilerServices; + + [Obsolete] + public class [|C|] + { + [Obsolete] + [ThreadStatic] + public int field1; + + [Obsolete] + public int prop1 { get; set; } + + [Obsolete] + public int prop2 + { + get + { + return 10; + } + set + { + } + } + + [Obsolete] + public int this[int x] + { + get + { + return 10; + } + set + { + } + } + + [Obsolete] + public event Action event1; + + [Obsolete] + public event Action event2 + { + add + { + } + remove + { + } + } + + [Obsolete] + public void method1() + { + } + + [Obsolete] + public C() + { + } + + [Obsolete] + ~C() + { + } + + public void method2([CallerMemberName] string name = "") + { + } + + [Obsolete] + public static C operator +(C c1, C c2) + { + return new C(); + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + using System.Runtime.CompilerServices; + + [Obsolete] + public class [|C|] + { + [Obsolete] + [ThreadStatic] + public int field1; + + [Obsolete] + public int prop1 { get; set; } + + [Obsolete] + public int prop2 + { + get + { + return 10; + } + set + { + } + } + + [Obsolete] + public int this[int x] + { + get + { + return 10; + } + set + { + } + } + + [Obsolete] + public event Action event1; + + [Obsolete] + public event Action event2 + { + add + { + } + remove + { + } + } + + [Obsolete] + public void method1() + { + } + + [Obsolete] + public C() + { + } + + [Obsolete] + ~C() + { + } + + public void method2([CallerMemberName] string name = "") + { + } + + [Obsolete] + public static C operator +(C c1, C c2) + { + return new C(); + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -2125,248 +2341,258 @@ public void method2([CallerMemberName] string name = """") [WpfTheory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530923"), CombinatorialData] public async Task TestEmptyLineBetweenMembers(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @"using System; - -public class C -{ - public int field1; - public int prop1 { get; set; } - public int field2; - public int prop2 { get { return 10; } set {} } - public void method1() {} - public C() {} - public void method2([System.Runtime.CompilerServices.CallerMemberName] string name = """") {} - ~C() {} - public int this[int x] { get { return 10; } set {} } - public event Action event1; - public static C operator + (C c1, C c2) { return new C(); } - public event Action event2 { add {} remove {}} - public static C operator - (C c1, C c2) { return new C(); } -} -"; + var metadataSource = """ + using System; + + public class C + { + public int field1; + public int prop1 { get; set; } + public int field2; + public int prop2 { get { return 10; } set {} } + public void method1() {} + public C() {} + public void method2([System.Runtime.CompilerServices.CallerMemberName] string name = "") {} + ~C() {} + public int this[int x] { get { return 10; } set {} } + public event Action event1; + public static C operator + (C c1, C c2) { return new C(); } + public event Action event2 { add {} remove {}} + public static C operator - (C c1, C c2) { return new C(); } + } + + """; var symbolName = "C"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System; -using System.Reflection; -using System.Runtime.CompilerServices; - -[DefaultMember(""Item"")] -public class [|C|] -{{ - public int field1; - public int field2; - - public C(); - - ~C(); - - public int this[int x] {{ get; set; }} - - public int prop1 {{ get; set; }} - public int prop2 {{ get; set; }} - - public event Action event1; - public event Action event2; - - public void method1(); - public void method2([CallerMemberName] string name = """"); - - public static C operator +(C c1, C c2); - public static C operator -(C c1, C c2); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System -Imports System.Reflection -Imports System.Runtime.CompilerServices - - -Public Class [|C|] - Public field1 As Integer - Public field2 As Integer - - Public Sub New() - - Public Property prop1 As Integer - Public Property prop2 As Integer - Default Public Property Item(x As Integer) As Integer - - Public Event event1 As Action - Public Event event2 As Action - - Public Sub method1() - Public Sub method2( Optional name As String = """") - Protected Overrides Sub Finalize() - - Public Shared Operator +(c1 As C, c2 As C) As C - Public Shared Operator -(c1 As C, c2 As C) As C -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; -using System.Runtime.CompilerServices; - -public class [|C|] -{{ - public int field1; - - public int field2; - - public int prop1 {{ get; set; }} - - public int prop2 - {{ - get - {{ - return 10; - }} - set - {{ - }} - }} - - public int this[int x] - {{ - get - {{ - return 10; - }} - set - {{ - }} - }} - - public event Action event1; - - public event Action event2 - {{ - add - {{ - }} - remove - {{ - }} - }} - - public void method1() - {{ - }} - - public void method2([CallerMemberName] string name = """") - {{ - }} - - ~C() - {{ - }} - - public static C operator +(C c1, C c2) - {{ - return new C(); - }} - - public static C operator -(C c1, C c2) - {{ - return new C(); - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; -using System.Runtime.CompilerServices; - -public class [|C|] -{{ - public int field1; - - public int field2; - - public int prop1 {{ get; set; }} - - public int prop2 - {{ - get - {{ - return 10; - }} - set - {{ - }} - }} - - public int this[int x] - {{ - get - {{ - return 10; - }} - set - {{ - }} - }} - - public event Action event1; - - public event Action event2 - {{ - add - {{ - }} - remove - {{ - }} - }} - - public void method1() - {{ - }} - - public void method2([CallerMemberName] string name = """") - {{ - }} - - ~C() - {{ - }} - - public static C operator +(C c1, C c2) - {{ - return new C(); - }} - - public static C operator -(C c1, C c2) - {{ - return new C(); - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System; + using System.Reflection; + using System.Runtime.CompilerServices; + + [DefaultMember("Item")] + public class [|C|] + { + public int field1; + public int field2; + + public C(); + + ~C(); + + public int this[int x] { get; set; } + + public int prop1 { get; set; } + public int prop2 { get; set; } + + public event Action event1; + public event Action event2; + + public void method1(); + public void method2([CallerMemberName] string name = ""); + + public static C operator +(C c1, C c2); + public static C operator -(C c1, C c2); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System + Imports System.Reflection + Imports System.Runtime.CompilerServices + + + Public Class [|C|] + Public field1 As Integer + Public field2 As Integer + + Public Sub New() + + Public Property prop1 As Integer + Public Property prop2 As Integer + Default Public Property Item(x As Integer) As Integer + + Public Event event1 As Action + Public Event event2 As Action + + Public Sub method1() + Public Sub method2( Optional name As String = "") + Protected Overrides Sub Finalize() + + Public Shared Operator +(c1 As C, c2 As C) As C + Public Shared Operator -(c1 As C, c2 As C) As C + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + using System.Runtime.CompilerServices; + + public class [|C|] + { + public int field1; + + public int field2; + + public int prop1 { get; set; } + + public int prop2 + { + get + { + return 10; + } + set + { + } + } + + public int this[int x] + { + get + { + return 10; + } + set + { + } + } + + public event Action event1; + + public event Action event2 + { + add + { + } + remove + { + } + } + + public void method1() + { + } + + public void method2([CallerMemberName] string name = "") + { + } + + ~C() + { + } + + public static C operator +(C c1, C c2) + { + return new C(); + } + + public static C operator -(C c1, C c2) + { + return new C(); + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + using System.Runtime.CompilerServices; + + public class [|C|] + { + public int field1; + + public int field2; + + public int prop1 { get; set; } + + public int prop2 + { + get + { + return 10; + } + set + { + } + } + + public int this[int x] + { + get + { + return 10; + } + set + { + } + } + + public event Action event1; + + public event Action event2 + { + add + { + } + remove + { + } + } + + public void method1() + { + } + + public void method2([CallerMemberName] string name = "") + { + } + + ~C() + { + } + + public static C operator +(C c1, C c2) + { + return new C(); + } + + public static C operator -(C c1, C c2) + { + return new C(); + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -2376,111 +2602,121 @@ public void method2([CallerMemberName] string name = """") [WpfTheory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/728644"), CombinatorialData] public async Task TestEmptyLineBetweenMembers2(OriginatingProjectLanguage language, bool signaturesOnly) { - var source = @" -using System; + var source = """ -/// T:IGoo -public interface IGoo -{ - /// P:IGoo.Prop1 - Uri Prop1 { get; set; } - /// M:IGoo.Method1 - Uri Method1(); -} -"; + using System; + + /// T:IGoo + public interface IGoo + { + /// P:IGoo.Prop1 + Uri Prop1 { get; set; } + /// M:IGoo.Method1 + Uri Method1(); + } + + """; var symbolName = "IGoo"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System; - -// -// {FeaturesResources.Summary_colon} -// T:IGoo -public interface [|IGoo|] -{{ - // - // {FeaturesResources.Summary_colon} - // P:IGoo.Prop1 - Uri Prop1 {{ get; set; }} - - // - // {FeaturesResources.Summary_colon} - // M:IGoo.Method1 - Uri Method1(); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System - -' -' {FeaturesResources.Summary_colon} -' T:IGoo -Public Interface [|IGoo|] - ' - ' {FeaturesResources.Summary_colon} - ' P:IGoo.Prop1 - Property Prop1 As Uri - - ' - ' {FeaturesResources.Summary_colon} - ' M:IGoo.Method1 - Function Method1() As Uri -End Interface", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; - -public interface [|IGoo|] -{{ - Uri Prop1 {{ get; set; }} - - Uri Method1(); -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} ------------------- -{string.Format(FeaturesResources.Resolve_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "System.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; - -public interface [|IGoo|] -{{ - Uri Prop1 {{ get; set; }} - - Uri Method1(); -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} ------------------- -{string.Format(FeaturesResources.Resolve_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "System.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System; + + // + // {{FeaturesResources.Summary_colon}} + // T:IGoo + public interface [|IGoo|] + { + // + // {{FeaturesResources.Summary_colon}} + // P:IGoo.Prop1 + Uri Prop1 { get; set; } + + // + // {{FeaturesResources.Summary_colon}} + // M:IGoo.Method1 + Uri Method1(); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System + + ' + ' {FeaturesResources.Summary_colon} + ' T:IGoo + Public Interface [|IGoo|] + ' + ' {FeaturesResources.Summary_colon} + ' P:IGoo.Prop1 + Property Prop1 As Uri + + ' + ' {FeaturesResources.Summary_colon} + ' M:IGoo.Method1 + Function Method1() As Uri + End Interface + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + + public interface [|IGoo|] + { + Uri Prop1 { get; set; } + + Uri Method1(); + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "System.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + + public interface [|IGoo|] + { + Uri Prop1 { get; set; } + + Uri Method1(); + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "System.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -2490,81 +2726,91 @@ public interface [|IGoo|] [WpfTheory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/679114"), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/715013"), CombinatorialData] public async Task TestDefaultValueEnum(OriginatingProjectLanguage language, bool signaturesOnly) { - var source = @" -using System.IO; + var source = """ -public class Test -{ - public void goo(FileOptions options = 0) {} -} -"; + using System.IO; + + public class Test + { + public void goo(FileOptions options = 0) {} + } + + """; var symbolName = "Test"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System.IO; - -public class [|Test|] -{{ - public Test(); - - public void goo(FileOptions options = FileOptions.None); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System.IO - -Public Class [|Test|] - Public Sub New() - - Public Sub goo(Optional options As FileOptions = FileOptions.None) -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.IO; - -public class [|Test|] -{{ - public void goo(FileOptions options = FileOptions.None) - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.IO; - -public class [|Test|] -{{ - public void goo(FileOptions options = FileOptions.None) - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System.IO; + + public class [|Test|] + { + public Test(); + + public void goo(FileOptions options = FileOptions.None); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System.IO + + Public Class [|Test|] + Public Sub New() + + Public Sub goo(Optional options As FileOptions = FileOptions.None) + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.IO; + + public class [|Test|] + { + public void goo(FileOptions options = FileOptions.None) + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.IO; + + public class [|Test|] + { + public void goo(FileOptions options = FileOptions.None) + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -2574,85 +2820,95 @@ public void goo(FileOptions options = FileOptions.None) [WpfTheory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/651261"), CombinatorialData] public async Task TestNullAttribute(OriginatingProjectLanguage language, bool signaturesOnly) { - var source = @" -using System; + var source = """ -[Test(null)] -public class TestAttribute : Attribute -{ - public TestAttribute(int[] i) - { - } -}"; + using System; + + [Test(null)] + public class TestAttribute : Attribute + { + public TestAttribute(int[] i) + { + } + } + """; var symbolName = "TestAttribute"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System; - -[Test(null)] -public class [|TestAttribute|] : Attribute -{{ - public TestAttribute(int[] i); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System - - -Public Class [|TestAttribute|] - Inherits Attribute - - Public Sub New(i() As Integer) -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; - -[Test(null)] -public class [|TestAttribute|] : Attribute -{{ - public TestAttribute(int[] i) - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; - -[Test(null)] -public class [|TestAttribute|] : Attribute -{{ - public TestAttribute(int[] i) - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System; + + [Test(null)] + public class [|TestAttribute|] : Attribute + { + public TestAttribute(int[] i); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System + + + Public Class [|TestAttribute|] + Inherits Attribute + + Public Sub New(i() As Integer) + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + + [Test(null)] + public class [|TestAttribute|] : Attribute + { + public TestAttribute(int[] i) + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + + [Test(null)] + public class [|TestAttribute|] : Attribute + { + public TestAttribute(int[] i) + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -2662,27 +2918,33 @@ public TestAttribute(int[] i) [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/897006")] public async Task TestNavigationViaReducedExtensionMethodCS() { - var metadata = @"using System; -public static class ObjectExtensions -{ - public static void M(this object o, int x) { } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - new object().[|M|](5); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public static class ObjectExtensions -{{ - public static void [|M|](this object o, int x); -}}"; + var metadata = """ + using System; + public static class ObjectExtensions + { + public static void M(this object o, int x) { } + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + new object().[|M|](5); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public static class ObjectExtensions + { + public static void [|M|](this object o, int x); + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -2697,34 +2959,40 @@ public static class ObjectExtensions [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/897006")] public async Task TestNavigationViaReducedExtensionMethodVB() { - var metadata = @"Imports System.Runtime.CompilerServices -Namespace NS - Public Module StringExtensions - - Public Sub M(ByVal o As String, x As Integer) - End Sub - End Module -End Namespace"; - var sourceWithSymbolReference = @" -Imports NS.StringExtensions -Public Module C - Sub M() - Dim s = ""Yay"" - s.[|M|](1) - End Sub -End Module"; - var expected = $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System.Runtime.CompilerServices - -Namespace NS - - Public Module StringExtensions - Public Sub [|M|](o As String, x As Integer) - End Module -End Namespace"; + var metadata = """ + Imports System.Runtime.CompilerServices + Namespace NS + Public Module StringExtensions + + Public Sub M(ByVal o As String, x As Integer) + End Sub + End Module + End Namespace + """; + var sourceWithSymbolReference = """ + + Imports NS.StringExtensions + Public Module C + Sub M() + Dim s = "Yay" + s.[|M|](1) + End Sub + End Module + """; + var expected = $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System.Runtime.CompilerServices + + Namespace NS + + Public Module StringExtensions + Public Sub [|M|](o As String, x As Integer) + End Module + End Namespace + """; using var context = TestContext.Create( LanguageNames.VisualBasic, @@ -2739,118 +3007,128 @@ End Module [WpfTheory, CombinatorialData] public async Task TestIndexersAndOperators(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @"public class Program -{ - public int this[int x] - { - get - { - return 0; - } - set - { - - } - } - - public static Program operator + (Program p1, Program p2) - { - return new Program(); - } -}"; + var metadataSource = """ + public class Program + { + public int this[int x] + { + get + { + return 0; + } + set + { + + } + } + + public static Program operator + (Program p1, Program p2) + { + return new Program(); + } + } + """; var symbolName = "Program"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System.Reflection; - -[DefaultMember(""Item"")] -public class [|Program|] -{{ - public Program(); - - public int this[int x] {{ get; set; }} - - public static Program operator +(Program p1, Program p2); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System.Reflection - - -Public Class [|Program|] - Public Sub New() - - Default Public Property Item(x As Integer) As Integer - - Public Shared Operator +(p1 As Program, p2 As Program) As Program -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|Program|] -{{ - public int this[int x] - {{ - get - {{ - return 0; - }} - set - {{ - }} - }} - - public static Program operator +(Program p1, Program p2) - {{ - return new Program(); - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|Program|] -{{ - public int this[int x] - {{ - get - {{ - return 0; - }} - set - {{ - }} - }} - - public static Program operator +(Program p1, Program p2) - {{ - return new Program(); - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System.Reflection; + + [DefaultMember("Item")] + public class [|Program|] + { + public Program(); + + public int this[int x] { get; set; } + + public static Program operator +(Program p1, Program p2); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System.Reflection + + + Public Class [|Program|] + Public Sub New() + + Default Public Property Item(x As Integer) As Integer + + Public Shared Operator +(p1 As Program, p2 As Program) As Program + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|Program|] + { + public int this[int x] + { + get + { + return 0; + } + set + { + } + } + + public static Program operator +(Program p1, Program p2) + { + return new Program(); + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|Program|] + { + public int this[int x] + { + get + { + return 0; + } + set + { + } + } + + public static Program operator +(Program p1, Program p2) + { + return new Program(); + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -2860,103 +3138,113 @@ public int this[int x] [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/15387"), CombinatorialData] public async Task TestComImport1(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -using System.Runtime.InteropServices; + var metadataSource = """ -[ComImport] -[Guid(""666A175D-2448-447A-B786-CCC82CBEF156"")] -public interface IComImport -{ - void MOverload(); - void X(); - void MOverload(int i); - int Prop { get; } -}"; + using System.Runtime.InteropServices; + + [ComImport] + [Guid("666A175D-2448-447A-B786-CCC82CBEF156")] + public interface IComImport + { + void MOverload(); + void X(); + void MOverload(int i); + int Prop { get; } + } + """; var symbolName = "IComImport"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System.Runtime.InteropServices; - -[Guid(""666A175D-2448-447A-B786-CCC82CBEF156"")] -public interface [|IComImport|] -{{ - void MOverload(); - void X(); - void MOverload(int i); - - int Prop {{ get; }} -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System.Runtime.InteropServices - - -Public Interface [|IComImport|] - ReadOnly Property Prop As Integer - - Sub MOverload() - Sub X() - Sub MOverload(i As Integer) -End Interface", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[ComImport] -[Guid(""666A175D-2448-447A-B786-CCC82CBEF156"")] -public interface [|IComImport|] -{{ - void MOverload(); - - void X(); - - void MOverload(int i); - - int Prop {{ get; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[ComImport] -[Guid(""666A175D-2448-447A-B786-CCC82CBEF156"")] -public interface [|IComImport|] -{{ - void MOverload(); - - void X(); - - void MOverload(int i); - - int Prop {{ get; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System.Runtime.InteropServices; + + [Guid("666A175D-2448-447A-B786-CCC82CBEF156")] + public interface [|IComImport|] + { + void MOverload(); + void X(); + void MOverload(int i); + + int Prop { get; } + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System.Runtime.InteropServices + + + Public Interface [|IComImport|] + ReadOnly Property Prop As Integer + + Sub MOverload() + Sub X() + Sub MOverload(i As Integer) + End Interface + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [ComImport] + [Guid("666A175D-2448-447A-B786-CCC82CBEF156")] + public interface [|IComImport|] + { + void MOverload(); + + void X(); + + void MOverload(int i); + + int Prop { get; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [ComImport] + [Guid("666A175D-2448-447A-B786-CCC82CBEF156")] + public interface [|IComImport|] + { + void MOverload(); + + void X(); + + void MOverload(int i); + + int Prop { get; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -2966,79 +3254,89 @@ public interface [|IComImport|] [WpfTheory, CombinatorialData] public async Task TestOptionalParameterWithDefaultLiteral(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -using System.Threading; + var metadataSource = """ -public class C { - public void M(CancellationToken cancellationToken = default(CancellationToken)) { } -}"; + using System.Threading; + + public class C { + public void M(CancellationToken cancellationToken = default(CancellationToken)) { } + } + """; var symbolName = "C"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System.Threading; - -public class [|C|] -{{ - public C(); - - public void M(CancellationToken cancellationToken = default); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System.Threading - -Public Class [|C|] - Public Sub New() - - Public Sub M(Optional cancellationToken As CancellationToken = Nothing) -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Threading; - -public class [|C|] -{{ - public void M(CancellationToken cancellationToken = default(CancellationToken)) - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Threading; - -public class [|C|] -{{ - public void M(CancellationToken cancellationToken = default(CancellationToken)) - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System.Threading; + + public class [|C|] + { + public C(); + + public void M(CancellationToken cancellationToken = default); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System.Threading + + Public Class [|C|] + Public Sub New() + + Public Sub M(Optional cancellationToken As CancellationToken = Nothing) + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Threading; + + public class [|C|] + { + public void M(CancellationToken cancellationToken = default(CancellationToken)) + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Threading; + + public class [|C|] + { + public void M(CancellationToken cancellationToken = default(CancellationToken)) + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -3055,111 +3353,139 @@ public class [|C|] [WpfTheory, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems?id=446567"), CombinatorialData] public async Task TestDocCommentsWithUnixNewLine(OriginatingProjectLanguage language, bool signaturesOnly) { - var source = @" -using System; + var source = """ -/// T:IGoo" + "\n/// ABCDE\n" + @"/// FGHIJK -public interface IGoo -{ - /// P:IGoo.Prop1" + "\n/// ABCDE\n" + @"/// FGHIJK - Uri Prop1 { get; set; } - /// M:IGoo.Method1" + "\n/// ABCDE\n" + @"/// FGHIJK - Uri Method1(); -} -"; - var symbolName = "IGoo"; + using System; - var expected = (language, signaturesOnly) switch - { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion + /// T:IGoo + """ + """ -using System; + /// ABCDE -// -// {FeaturesResources.Summary_colon} -// T:IGoo ABCDE FGHIJK -public interface [|IGoo|] -{{ - // - // {FeaturesResources.Summary_colon} - // P:IGoo.Prop1 ABCDE FGHIJK - Uri Prop1 {{ get; set; }} - - // - // {FeaturesResources.Summary_colon} - // M:IGoo.Method1 ABCDE FGHIJK - Uri Method1(); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System - -' -' {FeaturesResources.Summary_colon} -' T:IGoo ABCDE FGHIJK -Public Interface [|IGoo|] - ' - ' {FeaturesResources.Summary_colon} - ' P:IGoo.Prop1 ABCDE FGHIJK - Property Prop1 As Uri - - ' - ' {FeaturesResources.Summary_colon} - ' M:IGoo.Method1 ABCDE FGHIJK - Function Method1() As Uri -End Interface", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion + """ + """ + /// FGHIJK + public interface IGoo + { + /// P:IGoo.Prop1 + """ + """ -using System; + /// ABCDE -public interface [|IGoo|] -{{ - Uri Prop1 {{ get; set; }} - - Uri Method1(); -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} ------------------- -{string.Format(FeaturesResources.Resolve_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "System.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion + """ + """ + /// FGHIJK + Uri Prop1 { get; set; } + /// M:IGoo.Method1 + """ + """ -using System; + /// ABCDE -public interface [|IGoo|] -{{ - Uri Prop1 {{ get; set; }} - - Uri Method1(); -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} ------------------- -{string.Format(FeaturesResources.Resolve_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "System.v4_6_1038_0.dll")} -#endif", + """ + """ + /// FGHIJK + Uri Method1(); + } + + """; + var symbolName = "IGoo"; + + var expected = (language, signaturesOnly) switch + { + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System; + + // + // {{FeaturesResources.Summary_colon}} + // T:IGoo ABCDE FGHIJK + public interface [|IGoo|] + { + // + // {{FeaturesResources.Summary_colon}} + // P:IGoo.Prop1 ABCDE FGHIJK + Uri Prop1 { get; set; } + + // + // {{FeaturesResources.Summary_colon}} + // M:IGoo.Method1 ABCDE FGHIJK + Uri Method1(); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System + + ' + ' {FeaturesResources.Summary_colon} + ' T:IGoo ABCDE FGHIJK + Public Interface [|IGoo|] + ' + ' {FeaturesResources.Summary_colon} + ' P:IGoo.Prop1 ABCDE FGHIJK + Property Prop1 As Uri + + ' + ' {FeaturesResources.Summary_colon} + ' M:IGoo.Method1 ABCDE FGHIJK + Function Method1() As Uri + End Interface + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + + public interface [|IGoo|] + { + Uri Prop1 { get; set; } + + Uri Method1(); + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "System.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + + public interface [|IGoo|] + { + Uri Prop1 { get; set; } + + Uri Method1(); + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "System.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -3169,26 +3495,32 @@ public interface [|IGoo|] [WpfFact] public async Task TestUnmanagedCSharpConstraint_Type() { - var metadata = @"using System; -public class TestType where T : unmanaged -{ -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new [|TestType|]<int>(); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class [|TestType|] where T : unmanaged -{{ - public TestType(); -}}"; + var metadata = """ + using System; + public class TestType where T : unmanaged + { + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new [|TestType|]<int>(); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class [|TestType|] where T : unmanaged + { + public TestType(); + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -3204,31 +3536,37 @@ public class [|TestType|] where T : unmanaged [WpfFact] public async Task TestUnmanagedCSharpConstraint_Method() { - var metadata = @"using System; -public class TestType -{ - public void M() where T : unmanaged - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M|]<int>(); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class TestType -{{ - public TestType(); - - public void [|M|]() where T : unmanaged; -}}"; + var metadata = """ + using System; + public class TestType + { + public void M() where T : unmanaged + { + } + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new TestType().[|M|]<int>(); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class TestType + { + public TestType(); + + public void [|M|]() where T : unmanaged; + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -3244,20 +3582,26 @@ public class TestType [WpfFact] public async Task TestUnmanagedCSharpConstraint_Delegate() { - var metadata = @"using System; -public delegate void D() where T : unmanaged;"; - var sourceWithSymbolReference = @" -class C -{ - void M([|D|]<int> lambda) - { - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public delegate void [|D|]() where T : unmanaged;"; + var metadata = """ + using System; + public delegate void D() where T : unmanaged; + """; + var sourceWithSymbolReference = """ + + class C + { + void M([|D|]<int> lambda) + { + } + } + """; + var expected = $""" + #region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {CodeAnalysisResources.InMemoryAssembly} + #endregion + + public delegate void [|D|]() where T : unmanaged; + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -3273,11 +3617,13 @@ class C [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/29786")] public async Task TestSByteMinValue() { - var source = @" -class C -{ - sbyte Goo = sbyte.[|MinValue|]; -}"; + var source = """ + + class C + { + sbyte Goo = sbyte.[|MinValue|]; + } + """; var expected = "public const SByte MinValue = -128;"; @@ -3287,10 +3633,12 @@ class C [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/29786")] public async Task TestSByteMinValueVB() { - var source = @" -Class C - Public Goo = SByte.[|MinValue|] -End Class"; + var source = """ + + Class C + Public Goo = SByte.[|MinValue|] + End Class + """; var expected = "Public Const MinValue As [SByte] = -128"; @@ -3300,11 +3648,13 @@ Class C [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/29786")] public async Task TestInt16MinValue() { - var source = @" -class C -{ - short Goo = short.[|MinValue|]; -}"; + var source = """ + + class C + { + short Goo = short.[|MinValue|]; + } + """; var expected = $"public const Int16 MinValue = -32768;"; @@ -3314,10 +3664,12 @@ class C [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/29786")] public async Task TestInt16MinValueVB() { - var source = @" -Class C - Public Goo = Short.[|MinValue|] -End Class"; + var source = """ + + Class C + Public Goo = Short.[|MinValue|] + End Class + """; var expected = $"Public Const MinValue As Int16 = -32768"; @@ -3327,11 +3679,13 @@ Class C [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/29786")] public async Task TestInt32MinValue() { - var source = @" -class C -{ - int Goo = int.[|MinValue|]; -}"; + var source = """ + + class C + { + int Goo = int.[|MinValue|]; + } + """; var expected = $"public const Int32 MinValue = -2147483648;"; @@ -3341,10 +3695,12 @@ class C [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/29786")] public async Task TestInt32MinValueVB() { - var source = @" -Class C - Public Goo = Integer.[|MinValue|] -End Class"; + var source = """ + + Class C + Public Goo = Integer.[|MinValue|] + End Class + """; var expected = $"Public Const MinValue As Int32 = -2147483648"; @@ -3354,11 +3710,13 @@ Class C [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/29786")] public async Task TestInt64MinValue() { - var source = @" -class C -{ - long Goo = long.[|MinValue|]; -}"; + var source = """ + + class C + { + long Goo = long.[|MinValue|]; + } + """; var expected = $"public const Int64 MinValue = -9223372036854775808;"; @@ -3368,10 +3726,12 @@ class C [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/29786")] public async Task TestInt64MinValueVB() { - var source = @" -Class C - Public Goo = Long.[|MinValue|] -End Class"; + var source = """ + + Class C + Public Goo = Long.[|MinValue|] + End Class + """; var expected = $"Public Const MinValue As Int64 = -9223372036854775808"; @@ -3381,65 +3741,75 @@ Class C [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestReadOnlyStruct_ReadOnlyField(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public readonly struct S -{ - public readonly int i; -} -"; + var metadataSource = """ + + public readonly struct S + { + public readonly int i; + } + + """; var symbolName = "S"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public readonly struct [|S|] -{{ - public readonly int i; -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - - - -Public Structure [|S|] - Public ReadOnly i As Integer -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public readonly struct [|S|] -{{ - public readonly int i; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public readonly struct [|S|] -{{ - public readonly int i; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public readonly struct [|S|] + { + public readonly int i; + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + + + Public Structure [|S|] + Public ReadOnly i As Integer + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public readonly struct [|S|] + { + public readonly int i; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public readonly struct [|S|] + { + public readonly int i; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -3449,63 +3819,73 @@ public readonly struct [|S|] [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStruct_ReadOnlyField(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public struct S -{ - public readonly int i; -} -"; + var metadataSource = """ + + public struct S + { + public readonly int i; + } + + """; var symbolName = "S"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public struct [|S|] -{{ - public readonly int i; -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Structure [|S|] - Public ReadOnly i As Integer -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public struct [|S|] -{{ - public readonly int i; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public struct [|S|] -{{ - public readonly int i; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public struct [|S|] + { + public readonly int i; + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Structure [|S|] + Public ReadOnly i As Integer + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public struct [|S|] + { + public readonly int i; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public struct [|S|] + { + public readonly int i; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -3515,67 +3895,77 @@ public struct [|S|] [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestRefStruct(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public ref struct S -{ -} -"; + var metadataSource = """ + + public ref struct S + { + } + + """; var symbolName = "S"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public ref struct [|S|] -{{ -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System - - -Public Structure [|S|] -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public ref struct [|S|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public ref struct [|S|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public ref struct [|S|] + { + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System + + + Public Structure [|S|] + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public ref struct [|S|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public ref struct [|S|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -3585,67 +3975,77 @@ public ref struct [|S|] [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestReadOnlyRefStruct(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public readonly ref struct S -{ -} -"; + var metadataSource = """ + + public readonly ref struct S + { + } + + """; var symbolName = "S"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public readonly ref struct [|S|] -{{ -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System - - -Public Structure [|S|] -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public readonly ref struct [|S|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public readonly ref struct [|S|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public readonly ref struct [|S|] + { + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System + + + Public Structure [|S|] + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public readonly ref struct [|S|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public readonly ref struct [|S|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -3655,74 +4055,84 @@ public readonly ref struct [|S|] [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestReadOnlyMethod(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public struct S -{ - public readonly void M() {} -} -"; + var metadataSource = """ + + public struct S + { + public readonly void M() {} + } + + """; var symbolName = "S.M"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public struct S -{{ - public readonly void [|M|](); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - - -Public Structure S - Public Sub [|M|]() -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public readonly void [|M|]() - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public readonly void [|M|]() - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public struct S + { + public readonly void [|M|](); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + + Public Structure S + Public Sub [|M|]() + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public readonly void [|M|]() + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public readonly void [|M|]() + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -3732,75 +4142,85 @@ public struct S [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestReadOnlyMethod_InReadOnlyStruct(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public readonly struct S -{ - public void M() {} -} -"; + var metadataSource = """ + + public readonly struct S + { + public void M() {} + } + + """; var symbolName = "S.M"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public readonly struct S -{{ - public void [|M|](); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - - - -Public Structure S - Public Sub [|M|]() -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public readonly struct S -{{ - public void [|M|]() - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public readonly struct S -{{ - public void [|M|]() - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public readonly struct S + { + public void [|M|](); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + + + Public Structure S + Public Sub [|M|]() + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public readonly struct S + { + public void [|M|]() + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public readonly struct S + { + public void [|M|]() + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -3810,63 +4230,73 @@ public readonly struct S [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStructProperty_ReadOnly(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public struct S -{ - public int P { get; } -} -"; + var metadataSource = """ + + public struct S + { + public int P { get; } + } + + """; var symbolName = "S.P"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public struct S -{{ - public readonly int [|P|] {{ get; }} -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Structure S - Public ReadOnly Property [|P|] As Integer -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public struct S -{{ - public int [|P|] {{ get; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public struct S -{{ - public int [|P|] {{ get; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public struct S + { + public readonly int [|P|] { get; } + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Structure S + Public ReadOnly Property [|P|] As Integer + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public struct S + { + public int [|P|] { get; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public struct S + { + public int [|P|] { get; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -3876,63 +4306,73 @@ public struct S [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStructProperty_ReadOnly_CSharp7_3(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public struct S -{ - public int P { get; } -} -"; + var metadataSource = """ + + public struct S + { + public int P { get; } + } + + """; var symbolName = "S.P"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public struct S -{{ - public int [|P|] {{ get; }} -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Structure S - Public ReadOnly Property [|P|] As Integer -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public struct S -{{ - public int [|P|] {{ get; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public struct S -{{ - public int [|P|] {{ get; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public struct S + { + public int [|P|] { get; } + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Structure S + Public ReadOnly Property [|P|] As Integer + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public struct S + { + public int [|P|] { get; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public struct S + { + public int [|P|] { get; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -3949,63 +4389,73 @@ public struct S [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStructProperty_ReadOnlyGet(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public struct S -{ - public readonly int P { get; } -} -"; + var metadataSource = """ + + public struct S + { + public readonly int P { get; } + } + + """; var symbolName = "S.P"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public struct S -{{ - public readonly int [|P|] {{ get; }} -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Structure S - Public ReadOnly Property [|P|] As Integer -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public struct S -{{ - public int [|P|] {{ get; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public struct S -{{ - public int [|P|] {{ get; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public struct S + { + public readonly int [|P|] { get; } + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Structure S + Public ReadOnly Property [|P|] As Integer + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public struct S + { + public int [|P|] { get; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public struct S + { + public int [|P|] { get; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -4015,65 +4465,75 @@ public struct S [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestReadOnlyStructProperty_ReadOnlyGet(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public readonly struct S -{ - public readonly int P { get; } -} -"; + var metadataSource = """ + + public readonly struct S + { + public readonly int P { get; } + } + + """; var symbolName = "S.P"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public readonly struct S -{{ - public int [|P|] {{ get; }} -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - - - -Public Structure S - Public ReadOnly Property [|P|] As Integer -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public readonly struct S -{{ - public int [|P|] {{ get; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public readonly struct S -{{ - public int [|P|] {{ get; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public readonly struct S + { + public int [|P|] { get; } + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + + + Public Structure S + Public ReadOnly Property [|P|] As Integer + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public readonly struct S + { + public int [|P|] { get; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public readonly struct S + { + public int [|P|] { get; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -4083,87 +4543,97 @@ public readonly struct S [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStructProperty_ReadOnlyGet_Set(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public struct S -{ - public int P { readonly get => 123; set {} } -} -"; + var metadataSource = """ + + public struct S + { + public int P { readonly get => 123; set {} } + } + + """; var symbolName = "S.P"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public struct S -{{ - public int [|P|] {{ readonly get; set; }} -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Structure S - Public Property [|P|] As Integer -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public int [|P|] - {{ - readonly get - {{ - return 123; - }} - set - {{ - }} - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public int [|P|] - {{ - readonly get - {{ - return 123; - }} - set - {{ - }} - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public struct S + { + public int [|P|] { readonly get; set; } + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Structure S + Public Property [|P|] As Integer + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public int [|P|] + { + readonly get + { + return 123; + } + set + { + } + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public int [|P|] + { + readonly get + { + return 123; + } + set + { + } + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -4173,87 +4643,97 @@ readonly get [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStructProperty_Get_ReadOnlySet(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public struct S -{ - public int P { get => 123; readonly set {} } -} -"; + var metadataSource = """ + + public struct S + { + public int P { get => 123; readonly set {} } + } + + """; var symbolName = "S.P"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public struct S -{{ - public int [|P|] {{ get; readonly set; }} -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Structure S - Public Property [|P|] As Integer -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public int [|P|] - {{ - get - {{ - return 123; - }} - readonly set - {{ - }} - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public int [|P|] - {{ - get - {{ - return 123; - }} - readonly set - {{ - }} - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public struct S + { + public int [|P|] { get; readonly set; } + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Structure S + Public Property [|P|] As Integer + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public int [|P|] + { + get + { + return 123; + } + readonly set + { + } + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public int [|P|] + { + get + { + return 123; + } + readonly set + { + } + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -4263,87 +4743,97 @@ readonly set [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStructProperty_ReadOnlyGet_ReadOnlySet(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public struct S -{ - public readonly int P { get => 123; set {} } -} -"; + var metadataSource = """ + + public struct S + { + public readonly int P { get => 123; set {} } + } + + """; var symbolName = "S.P"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public struct S -{{ - public readonly int [|P|] {{ get; set; }} -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Structure S - Public Property [|P|] As Integer -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public readonly int [|P|] - {{ - get - {{ - return 123; - }} - set - {{ - }} - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public readonly int [|P|] - {{ - get - {{ - return 123; - }} - set - {{ - }} - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public struct S + { + public readonly int [|P|] { get; set; } + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Structure S + Public Property [|P|] As Integer + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public readonly int [|P|] + { + get + { + return 123; + } + set + { + } + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public readonly int [|P|] + { + get + { + return 123; + } + set + { + } + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -4353,75 +4843,85 @@ public readonly int [|P|] [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStructIndexer_ReadOnlyGet(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public struct S -{ - public readonly int this[int i] => i; -} -"; + var metadataSource = """ + + public struct S + { + public readonly int this[int i] => i; + } + + """; var symbolName = "S.Item"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System.Reflection; - -[DefaultMember(""Item"")] -public struct S -{{ - public readonly int [|this|][int i] {{ get; }} -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System.Reflection - - -Public Structure S - Default Public ReadOnly Property [|Item|](i As Integer) As Integer -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public readonly int [|this|][int i] => i; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public readonly int [|this|][int i] => i; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System.Reflection; + + [DefaultMember("Item")] + public struct S + { + public readonly int [|this|][int i] { get; } + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System.Reflection + + + Public Structure S + Default Public ReadOnly Property [|Item|](i As Integer) As Integer + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public readonly int [|this|][int i] => i; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public readonly int [|this|][int i] => i; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -4431,93 +4931,103 @@ public struct S [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStructIndexer_ReadOnlyGet_Set(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public struct S -{ - public int this[int i] { readonly get => i; set {} } -} -"; + var metadataSource = """ + + public struct S + { + public int this[int i] { readonly get => i; set {} } + } + + """; var symbolName = "S.Item"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System.Reflection; - -[DefaultMember(""Item"")] -public struct S -{{ - public int [|this|][int i] {{ readonly get; set; }} -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System.Reflection - - -Public Structure S - Default Public Property [|Item|](i As Integer) As Integer -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public int [|this|][int i] - {{ - readonly get - {{ - return i; - }} - set - {{ - }} - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public int [|this|][int i] - {{ - readonly get - {{ - return i; - }} - set - {{ - }} - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System.Reflection; + + [DefaultMember("Item")] + public struct S + { + public int [|this|][int i] { readonly get; set; } + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System.Reflection + + + Public Structure S + Default Public Property [|Item|](i As Integer) As Integer + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public int [|this|][int i] + { + readonly get + { + return i; + } + set + { + } + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public int [|this|][int i] + { + readonly get + { + return i; + } + set + { + } + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -4527,91 +5037,101 @@ readonly get [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStruct_ReadOnlyEvent(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public struct S -{ - public readonly event System.Action E { add {} remove {} } -} -"; + var metadataSource = """ + + public struct S + { + public readonly event System.Action E { add {} remove {} } + } + + """; var symbolName = "S.E"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System; - -public struct S -{{ - public readonly event Action [|E|]; -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System - -Public Structure S - Public Event [|E|] As Action -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public readonly event Action [|E|] - {{ - add - {{ - }} - remove - {{ - }} - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public readonly event Action [|E|] - {{ - add - {{ - }} - remove - {{ - }} - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System; + + public struct S + { + public readonly event Action [|E|]; + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System + + Public Structure S + Public Event [|E|] As Action + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public readonly event Action [|E|] + { + add + { + } + remove + { + } + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public readonly event Action [|E|] + { + add + { + } + remove + { + } + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -4621,92 +5141,102 @@ public readonly event Action [|E|] [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestReadOnlyStruct_ReadOnlyEvent(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public readonly struct S -{ - public event System.Action E { add {} remove {} } -} -"; + var metadataSource = """ + + public readonly struct S + { + public event System.Action E { add {} remove {} } + } + + """; var symbolName = "S.E"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System; - -public readonly struct S -{{ - public event Action [|E|]; -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System - - -Public Structure S - Public Event [|E|] As Action -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public readonly struct S -{{ - public event Action [|E|] - {{ - add - {{ - }} - remove - {{ - }} - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public readonly struct S -{{ - public event Action [|E|] - {{ - add - {{ - }} - remove - {{ - }} - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System; + + public readonly struct S + { + public event Action [|E|]; + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System + + + Public Structure S + Public Event [|E|] As Action + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public readonly struct S + { + public event Action [|E|] + { + add + { + } + remove + { + } + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public readonly struct S + { + public event Action [|E|] + { + add + { + } + remove + { + } + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -4716,26 +5246,32 @@ public event Action [|E|] [WpfFact] public async Task TestNotNullCSharpConstraint_Type() { - var metadata = @"using System; -public class TestType where T : notnull -{ -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new [|TestType|]<int>(); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class [|TestType|] where T : notnull -{{ - public TestType(); -}}"; + var metadata = """ + using System; + public class TestType where T : notnull + { + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new [|TestType|]<int>(); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class [|TestType|] where T : notnull + { + public TestType(); + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -4753,31 +5289,37 @@ public class [|TestType|] where T : notnull [WpfFact] public async Task TestNotNullCSharpConstraint_Method() { - var metadata = @"using System; -public class TestType -{ - public void M() where T : notnull - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M|]<int>(); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class TestType -{{ - public TestType(); - - public void [|M|]() where T : notnull; -}}"; + var metadata = """ + using System; + public class TestType + { + public void M() where T : notnull + { + } + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new TestType().[|M|]<int>(); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class TestType + { + public TestType(); + + public void [|M|]() where T : notnull; + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -4795,20 +5337,26 @@ public class TestType [WpfFact] public async Task TestNotNullCSharpConstraint_Delegate() { - var metadata = @"using System; -public delegate void D() where T : notnull;"; - var sourceWithSymbolReference = @" -class C -{ - void M([|D|]<int> lambda) - { - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public delegate void [|D|]() where T : notnull;"; + var metadata = """ + using System; + public delegate void D() where T : notnull; + """; + var sourceWithSymbolReference = """ + + class C + { + void M([|D|]<int> lambda) + { + } + } + """; + var expected = $""" + #region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {CodeAnalysisResources.InMemoryAssembly} + #endregion + + public delegate void [|D|]() where T : notnull; + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -4826,47 +5374,53 @@ class C [WpfFact] public async Task TestNullableEnableDisable1() { - var metadata = @" -#nullable enable + var metadata = """ -using System; + #nullable enable -public class TestType -{ - public void M1(string s) - { - } + using System; -#nullable disable + public class TestType + { + public void M1(string s) + { + } - public void M2(string s) - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M1|](null); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion + #nullable disable + + public void M2(string s) + { + } + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new TestType().[|M1|](null); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion -#nullable enable + #nullable enable -public class TestType -{{ - public TestType(); + public class TestType + { + public TestType(); - public void [|M1|](string s); -#nullable disable - public void M2(string s); + public void [|M1|](string s); + #nullable disable + public void M2(string s); -#nullable enable -}}"; + #nullable enable + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -4884,44 +5438,50 @@ public class TestType [WpfFact] public async Task TestNullableEnableDisable2() { - var metadata = @" -using System; - -public class TestType -{ - public void M1(string s) - { - } - -#nullable enable - - public void M2(string s) - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M1|](null); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -#nullable enable - -public class TestType -{{ - public TestType(); - -#nullable disable - public void [|M1|](string s); -#nullable enable - public void M2(string s); -}}"; + var metadata = """ + + using System; + + public class TestType + { + public void M1(string s) + { + } + + #nullable enable + + public void M2(string s) + { + } + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new TestType().[|M1|](null); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + #nullable enable + + public class TestType + { + public TestType(); + + #nullable disable + public void [|M1|](string s); + #nullable enable + public void M2(string s); + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -4939,52 +5499,58 @@ public class TestType [WpfFact] public async Task TestNullableEnableDisable3() { - var metadata = @" -#nullable enable + var metadata = """ -using System; + #nullable enable -public class TestType -{ - public void M1(string s) - { - } + using System; -#nullable disable + public class TestType + { + public void M1(string s) + { + } - public void M2(string s) - { - } + #nullable disable - public void M3(string s) - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M1|](null); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion + public void M2(string s) + { + } + + public void M3(string s) + { + } + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new TestType().[|M1|](null); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion -#nullable enable + #nullable enable -public class TestType -{{ - public TestType(); + public class TestType + { + public TestType(); - public void [|M1|](string s); -#nullable disable - public void M2(string s); - public void M3(string s); + public void [|M1|](string s); + #nullable disable + public void M2(string s); + public void M3(string s); -#nullable enable -}}"; + #nullable enable + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -5002,39 +5568,45 @@ public class TestType [WpfFact] public async Task TestNullableEnableDisable4() { - var metadata = @" -#nullable enable + var metadata = """ -using System; + #nullable enable -public class TestType -{ - public void M1(ICloneable s) - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M1|](null); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion + using System; -#nullable enable + public class TestType + { + public void M1(ICloneable s) + { + } + } + """; + var sourceWithSymbolReference = """ -using System; + class C + { + void M() + { + var obj = new TestType().[|M1|](null); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + #nullable enable -public class TestType -{{ - public TestType(); + using System; - public void [|M1|](ICloneable s); -}}"; + public class TestType + { + public TestType(); + + public void [|M1|](ICloneable s); + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -5052,40 +5624,46 @@ public class TestType [WpfFact] public async Task TestNullableEnableDisable5() { - var metadata = @" -#nullable enable + var metadata = """ -using System; + #nullable enable -public class TestType -{ - public void M1(ICloneable s) - { -#nullable disable - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M1|](null); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion + using System; -#nullable enable + public class TestType + { + public void M1(ICloneable s) + { + #nullable disable + } + } + """; + var sourceWithSymbolReference = """ -using System; + class C + { + void M() + { + var obj = new TestType().[|M1|](null); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion -public class TestType -{{ - public TestType(); + #nullable enable - public void [|M1|](ICloneable s); -}}"; + using System; + + public class TestType + { + public TestType(); + + public void [|M1|](ICloneable s); + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -5103,37 +5681,43 @@ public class TestType [WpfFact] public async Task TestNullableEnableDisable6() { - var metadata = @" -#nullable enable + var metadata = """ -using System; + #nullable enable -public class TestType -{ - public void M1(T? s) where T : class - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M1|](""""); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion + using System; + + public class TestType + { + public void M1(T? s) where T : class + { + } + } + """; + var sourceWithSymbolReference = """ -#nullable enable + class C + { + void M() + { + var obj = new TestType().[|M1|](""); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion -public class TestType -{{ - public TestType(); + #nullable enable - public void [|M1|](T? s) where T : class; -}}"; + public class TestType + { + public TestType(); + + public void [|M1|](T? s) where T : class; + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -5151,37 +5735,43 @@ public class TestType [WpfFact] public async Task TestNullableEnableDisable7() { - var metadata = @" -#nullable enable + var metadata = """ -using System; + #nullable enable -public class TestType -{ - public void M1(T s) where T : class - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M1|](""""); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion + using System; + + public class TestType + { + public void M1(T s) where T : class + { + } + } + """; + var sourceWithSymbolReference = """ -#nullable enable + class C + { + void M() + { + var obj = new TestType().[|M1|](""); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion -public class TestType -{{ - public TestType(); + #nullable enable - public void [|M1|](T s) where T : class; -}}"; + public class TestType + { + public TestType(); + + public void [|M1|](T s) where T : class; + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -5199,35 +5789,41 @@ public class TestType [WpfFact] public async Task TestNullableEnableDisable8() { - var metadata = @" -#nullable enable - -using System; - -public class TestType -{ - public void M1(T? s) where T : struct - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M1|]((int?)0); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class TestType -{{ - public TestType(); - - public void [|M1|](T? s) where T : struct; -}}"; + var metadata = """ + + #nullable enable + + using System; + + public class TestType + { + public void M1(T? s) where T : struct + { + } + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new TestType().[|M1|]((int?)0); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class TestType + { + public TestType(); + + public void [|M1|](T? s) where T : struct; + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -5245,35 +5841,41 @@ public class TestType [WpfFact] public async Task TestNullableEnableDisable9() { - var metadata = @" -#nullable enable - -using System; - -public class TestType -{ - public void M1(T s) where T : struct - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M1|](0); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class TestType -{{ - public TestType(); - - public void [|M1|](T s) where T : struct; -}}"; + var metadata = """ + + #nullable enable + + using System; + + public class TestType + { + public void M1(T s) where T : struct + { + } + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new TestType().[|M1|](0); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class TestType + { + public TestType(); + + public void [|M1|](T s) where T : struct; + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -5291,35 +5893,41 @@ public class TestType [WpfFact] public async Task TestNullableEnableDisable10() { - var metadata = @" -#nullable enable - -using System; - -public class TestType -{ - public void M1(T s) - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M1|](""""); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class TestType -{{ - public TestType(); - - public void [|M1|](T s); -}}"; + var metadata = """ + + #nullable enable + + using System; + + public class TestType + { + public void M1(T s) + { + } + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new TestType().[|M1|](""); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class TestType + { + public TestType(); + + public void [|M1|](T s); + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -5337,33 +5945,39 @@ public class TestType [WpfFact] public async Task TestNullableEnableDisable11() { - var metadata = @" -using System; - -public class TestType -{ - public void M1(T s) - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M1|](""""); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class TestType -{{ - public TestType(); - - public void [|M1|](T s); -}}"; + var metadata = """ + + using System; + + public class TestType + { + public void M1(T s) + { + } + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new TestType().[|M1|](""); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class TestType + { + public TestType(); + + public void [|M1|](T s); + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -5381,53 +5995,59 @@ public class TestType [WpfFact] public async Task TestNullableEnableDisable12() { - var metadata = @" -#nullable enable - -using System; - -namespace N -{ - public class TestType - { - public void M1(string s) - { - } - - #nullable disable - - public void M2(string s) - { - } - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new N.TestType().[|M1|](null); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -#nullable enable - -namespace N -{{ - public class TestType - {{ - public TestType(); - - public void [|M1|](string s); -#nullable disable - public void M2(string s); - -#nullable enable - }} -}}"; + var metadata = """ + + #nullable enable + + using System; + + namespace N + { + public class TestType + { + public void M1(string s) + { + } + + #nullable disable + + public void M2(string s) + { + } + } + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new N.TestType().[|M1|](null); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + #nullable enable + + namespace N + { + public class TestType + { + public TestType(); + + public void [|M1|](string s); + #nullable disable + public void M2(string s); + + #nullable enable + } + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -5445,63 +6065,69 @@ public class TestType [WpfFact] public async Task TestNullableEnableDisable13() { - var metadata = @" -#nullable enable + var metadata = """ -using System; + #nullable enable -public class TestType -{ - public void M1(string s) - { - } + using System; -#nullable disable + public class TestType + { + public void M1(string s) + { + } - public class Nested - { - public void NestedM(string s) - { - } - } + #nullable disable -#nullable enable + public class Nested + { + public void NestedM(string s) + { + } + } - public void M2(string s) - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M1|](null); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion + #nullable enable + + public void M2(string s) + { + } + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new TestType().[|M1|](null); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion -#nullable enable + #nullable enable -public class TestType -{{ - public TestType(); + public class TestType + { + public TestType(); - public void [|M1|](string s); - public void M2(string s); + public void [|M1|](string s); + public void M2(string s); - public class Nested - {{ - public Nested(); + public class Nested + { + public Nested(); -#nullable disable - public void NestedM(string s); + #nullable disable + public void NestedM(string s); -#nullable enable - }} -}}"; + #nullable enable + } + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -5519,33 +6145,39 @@ public class Nested [WpfFact] public async Task TestDynamic1() { - var metadata = @" -using System; - -public class TestType -{ - public void M1(dynamic s) - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M1|](null); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class TestType -{{ - public TestType(); - - public void [|M1|](dynamic s); -}}"; + var metadata = """ + + using System; + + public class TestType + { + public void M1(dynamic s) + { + } + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new TestType().[|M1|](null); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class TestType + { + public TestType(); + + public void [|M1|](dynamic s); + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -5563,27 +6195,31 @@ public class TestType [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/22431")] public async Task TestCDATAComment() { - var source = @" -public enum BinaryOperatorKind -{ - /// - /// Represents the operator. - /// - LeftShift = 0x8, -} -"; + var source = """ + + public enum BinaryOperatorKind + { + /// + /// Represents the operator. + /// + LeftShift = 0x8, + } + + """; var symbolName = "BinaryOperatorKind.LeftShift"; - var expectedCS = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public enum BinaryOperatorKind -{{ - // - // {FeaturesResources.Summary_colon} - // Represents the '<<' operator. - [|LeftShift|] = 8 -}}"; + var expectedCS = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public enum BinaryOperatorKind + { + // + // {{FeaturesResources.Summary_colon}} + // Represents the '<<' operator. + [|LeftShift|] = 8 + } + """; await GenerateAndVerifySourceAsync(source, symbolName, LanguageNames.CSharp, expectedCS, includeXmlDocComments: true); } } From e24848d2dacb7cd7767a7038771c9f698703ebb5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 21 Nov 2024 22:59:00 -0800 Subject: [PATCH 488/508] Implement interface --- .../Core/CodeGeneration/Symbols/CodeGenerationTypeSymbol.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationTypeSymbol.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationTypeSymbol.cs index 4ca0057a0fea0..8c8e34469a8d4 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationTypeSymbol.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationTypeSymbol.cs @@ -77,7 +77,7 @@ public ImmutableArray ToMinimalDisplayParts(SemanticModel sem public override bool IsType => true; - bool ITypeSymbol.IsRefLikeType => throw new System.NotImplementedException(); + bool ITypeSymbol.IsRefLikeType => false; bool ITypeSymbol.IsUnmanagedType => throw new System.NotImplementedException(); From c51d430438a220753ebf7edf5c68039b1a57539d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 21 Nov 2024 23:00:06 -0800 Subject: [PATCH 489/508] Use primary constructor --- .../Symbols/CodeGenerationTypeSymbol.cs | 39 +++++++------------ 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationTypeSymbol.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationTypeSymbol.cs index 8c8e34469a8d4..610fafd8b76b1 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationTypeSymbol.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationTypeSymbol.cs @@ -14,24 +14,18 @@ namespace Microsoft.CodeAnalysis.CodeGeneration; -internal abstract class CodeGenerationTypeSymbol : CodeGenerationNamespaceOrTypeSymbol, ITypeSymbol +internal abstract class CodeGenerationTypeSymbol( + IAssemblySymbol containingAssembly, + INamedTypeSymbol containingType, + ImmutableArray attributes, + Accessibility declaredAccessibility, + DeclarationModifiers modifiers, + string name, + SpecialType specialType, + NullableAnnotation nullableAnnotation) + : CodeGenerationNamespaceOrTypeSymbol(containingAssembly, containingType, attributes, declaredAccessibility, modifiers, name), ITypeSymbol { - public SpecialType SpecialType { get; protected set; } - - protected CodeGenerationTypeSymbol( - IAssemblySymbol containingAssembly, - INamedTypeSymbol containingType, - ImmutableArray attributes, - Accessibility declaredAccessibility, - DeclarationModifiers modifiers, - string name, - SpecialType specialType, - NullableAnnotation nullableAnnotation) - : base(containingAssembly, containingType, attributes, declaredAccessibility, modifiers, name) - { - this.SpecialType = specialType; - this.NullableAnnotation = nullableAnnotation; - } + public SpecialType SpecialType { get; protected set; } = specialType; public abstract TypeKind TypeKind { get; } @@ -85,17 +79,10 @@ public ImmutableArray ToMinimalDisplayParts(SemanticModel sem public virtual bool IsRecord => false; - public NullableAnnotation NullableAnnotation { get; } + public NullableAnnotation NullableAnnotation { get; } = nullableAnnotation; public ITypeSymbol WithNullableAnnotation(NullableAnnotation nullableAnnotation) - { - if (this.NullableAnnotation == nullableAnnotation) - { - return this; - } - - return CloneWithNullableAnnotation(nullableAnnotation); - } + => this.NullableAnnotation == nullableAnnotation ? this : CloneWithNullableAnnotation(nullableAnnotation); protected sealed override CodeGenerationSymbol Clone() => CloneWithNullableAnnotation(this.NullableAnnotation); From b9becb98ea9f592637f7a50860450ed4ec8fed52 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 22 Nov 2024 10:22:02 +0100 Subject: [PATCH 490/508] [main] Update dependencies from dotnet/source-build-reference-packages (#75972) * Update dependencies from https://github.com/dotnet/source-build-reference-packages build 20241118.2 Microsoft.SourceBuild.Intermediate.source-build-reference-packages From Version 10.0.0-alpha.1.24561.2 -> To Version 10.0.0-alpha.1.24568.2 * Failed to perform coherency update for one or more dependencies. --------- Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index a4646df17c587..b6c81c0755e44 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -8,9 +8,9 @@ - + https://github.com/dotnet/source-build-reference-packages - 92a51d1379daa1fa7892a9d06840ba833fcd6298 + df6fdaf26c24fa7c897ca8062227aa28e76e2339 From a498b922a30fa48eeac4594c0150281e903b6447 Mon Sep 17 00:00:00 2001 From: kasperk81 <83082615+kasperk81@users.noreply.github.com> Date: Fri, 22 Nov 2024 12:00:25 +0200 Subject: [PATCH 491/508] Make MaxSupportedLangVersion calculation dynamic (#75795) --- .../Test/CommandLine/CommandLineTests.cs | 2 +- .../MSBuildTask/Microsoft.CSharp.Core.targets | 34 ++++++++----------- .../Core/MSBuildTaskTests/TargetTests.cs | 2 +- 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index 35200629caa39..99048d694be53 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -1728,7 +1728,7 @@ public void LanguageVersionAdded_Canary() // When a new version is added, this test will break. This list must be checked: // - update the "UpgradeProject" codefixer // - update all the tests that call this canary - // - update MaxSupportedLangVersion (a relevant test should break when new version is introduced) + // - update _MaxAvailableLangVersion (a relevant test should break when new version is introduced) // - email release management to add to the release notes (see old example: https://github.com/dotnet/core/pull/1454) AssertEx.SetEqual(new[] { "default", "1", "2", "3", "4", "5", "6", "7.0", "7.1", "7.2", "7.3", "8.0", "9.0", "10.0", "11.0", "12.0", "13.0", "latest", "latestmajor", "preview" }, Enum.GetValues(typeof(LanguageVersion)).Cast().Select(v => v.ToDisplayString())); diff --git a/src/Compilers/Core/MSBuildTask/Microsoft.CSharp.Core.targets b/src/Compilers/Core/MSBuildTask/Microsoft.CSharp.Core.targets index ad407778bafb0..f11e8b1adcf90 100644 --- a/src/Compilers/Core/MSBuildTask/Microsoft.CSharp.Core.targets +++ b/src/Compilers/Core/MSBuildTask/Microsoft.CSharp.Core.targets @@ -7,31 +7,25 @@ <_MaxSupportedLangVersion Condition="('$(TargetFrameworkIdentifier)' != '.NETCoreApp' OR '$(_TargetFrameworkVersionWithoutV)' < '3.0') AND ('$(TargetFrameworkIdentifier)' != '.NETStandard' OR '$(_TargetFrameworkVersionWithoutV)' < '2.1')">7.3 - + <_MaxSupportedLangVersion Condition="(('$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND '$(_TargetFrameworkVersionWithoutV)' < '5.0') OR ('$(TargetFrameworkIdentifier)' == '.NETStandard' AND '$(_TargetFrameworkVersionWithoutV)' == '2.1')) AND '$(_MaxSupportedLangVersion)' == ''">8.0 - - <_MaxSupportedLangVersion Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND '$(_TargetFrameworkVersionWithoutV)' == '5.0' AND - '$(_MaxSupportedLangVersion)' == ''">9.0 - - - <_MaxSupportedLangVersion Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND '$(_TargetFrameworkVersionWithoutV)' == '6.0' AND - '$(_MaxSupportedLangVersion)' == ''">10.0 - - - <_MaxSupportedLangVersion Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND '$(_TargetFrameworkVersionWithoutV)' == '7.0' AND - '$(_MaxSupportedLangVersion)' == ''">11.0 - - - <_MaxSupportedLangVersion Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND '$(_TargetFrameworkVersionWithoutV)' == '8.0' AND - '$(_MaxSupportedLangVersion)' == ''">12.0 - - - <_MaxSupportedLangVersion Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND '$(_TargetFrameworkVersionWithoutV)' == '9.0' AND - '$(_MaxSupportedLangVersion)' == ''">13.0 + + <_MaxSupportedLangVersion Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND + '$(_MaxSupportedLangVersion)' == ''">$([MSBuild]::Add(9, $([MSBuild]::Subtract($(_TargetFrameworkVersionWithoutV), 5)))).0 + + + <_MaxAvailableLangVersion>13.0 + <_MaxSupportedLangVersion Condition="'$(_MaxSupportedLangVersion)' != '' AND + '$(_MaxSupportedLangVersion)' > '$(_MaxAvailableLangVersion)'">$(_MaxAvailableLangVersion) $(_MaxSupportedLangVersion) $(_MaxSupportedLangVersion) diff --git a/src/Compilers/Core/MSBuildTaskTests/TargetTests.cs b/src/Compilers/Core/MSBuildTaskTests/TargetTests.cs index fcc0626771d81..45c1d1a755fb3 100644 --- a/src/Compilers/Core/MSBuildTaskTests/TargetTests.cs +++ b/src/Compilers/Core/MSBuildTaskTests/TargetTests.cs @@ -403,7 +403,7 @@ public void GenerateEditorConfigCoreHandlesMalformedCompilerVisibleItemMetadata( [InlineData(".NETCoreApp", "7.0", "11.0")] [InlineData(".NETCoreApp", "8.0", "12.0")] [InlineData(".NETCoreApp", "9.0", "13.0")] - [InlineData(".NETCoreApp", "10.0", "")] + [InlineData(".NETCoreApp", "10.0", "13.0")] // update when 14.0 is released [InlineData(".NETStandard", "1.0", "7.3")] [InlineData(".NETStandard", "1.5", "7.3")] From 0f18f42dfcca44d374a6a9098ee1a7a0689464bf Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 22 Nov 2024 10:24:39 +0000 Subject: [PATCH 492/508] [main] Update dependencies from dotnet/source-build-externals (#75971) * Update dependencies from https://github.com/dotnet/source-build-externals build 20241118.2 Microsoft.SourceBuild.Intermediate.source-build-externals From Version 9.0.0-alpha.1.24516.3 -> To Version 9.0.0-alpha.1.24568.2 * Failed to perform coherency update for one or more dependencies. --------- Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index b6c81c0755e44..cb4f6fbbb104e 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -2,9 +2,9 @@ - + https://github.com/dotnet/source-build-externals - 4df883d781a4290873b3b968afc0ff0df7132507 + c65b1c1affed1f4847f9c3f81623dfa929d21e1a From d0ece16ab138736963dc3582880501043825e53f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 22 Nov 2024 10:37:49 -0800 Subject: [PATCH 493/508] Remove imports should only format the section of the document that was touched --- .../RemoveUnnecessaryImportsTests.cs | 36 +++++++++++- ...RemoveUnnecessaryImportsCodeFixProvider.cs | 8 +-- ...emoveUnnecessaryImportsService.Rewriter.cs | 4 +- .../CSharpRemoveUnnecessaryImportsService.cs | 57 ++++++------------- 4 files changed, 58 insertions(+), 47 deletions(-) diff --git a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryImports/RemoveUnnecessaryImportsTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryImports/RemoveUnnecessaryImportsTests.cs index c538aa68e5bf9..de8563ded1116 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryImports/RemoveUnnecessaryImportsTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryImports/RemoveUnnecessaryImportsTests.cs @@ -19,8 +19,10 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.RemoveUnnecessaryImport CSharpRemoveUnnecessaryImportsCodeFixProvider>; [Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessaryImports)] -public class RemoveUnnecessaryImportsTests +public sealed class RemoveUnnecessaryImportsTests { + private static readonly string s_tab = "\t"; + [Fact] public async Task TestNoReferences() { @@ -2246,4 +2248,36 @@ static void Main(string[] args) """ }.RunAsync(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65114")] + public async Task DoNotTouchInnerNamespaceWithoutUsings() + { + await VerifyCS.VerifyCodeFixAsync( + $$""" + [|{|IDE0005:using System; + using System.Collections.Generic; + using System.Linq;|}|] + + namespace N + { + {{s_tab}}class Program + { + static void Main(string[] args) + { + } + } + } + """, + $$""" + namespace N + { + {{s_tab}}class Program + { + static void Main(string[] args) + { + } + } + } + """); + } } diff --git a/src/Analyzers/Core/CodeFixes/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsCodeFixProvider.cs index a731d282a912c..c12b81448274a 100644 --- a/src/Analyzers/Core/CodeFixes/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsCodeFixProvider.cs @@ -2,13 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -30,7 +28,7 @@ public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) context.RegisterCodeFix( CodeAction.Create( title, - c => RemoveUnnecessaryImportsAsync(context.Document, c), + cancellationToken => RemoveUnnecessaryImportsAsync(context.Document, cancellationToken), title), context.Diagnostics); return Task.CompletedTask; @@ -38,11 +36,11 @@ public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) protected abstract string GetTitle(); - private static async Task RemoveUnnecessaryImportsAsync( + private static Task RemoveUnnecessaryImportsAsync( Document document, CancellationToken cancellationToken) { var service = document.GetRequiredLanguageService(); - return await service.RemoveUnnecessaryImportsAsync(document, cancellationToken).ConfigureAwait(false); + return service.RemoveUnnecessaryImportsAsync(document, cancellationToken); } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.Rewriter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.Rewriter.cs index 4db25ff8b4363..332951421c61a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.Rewriter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.Rewriter.cs @@ -189,7 +189,7 @@ public override SyntaxNode VisitCompilationUnit(CompilationUnitSyntax node) resultCompilationUnit = resultCompilationUnit.ReplaceToken(firstToken, newFirstToken); } - return resultCompilationUnit; + return resultCompilationUnit.WithAdditionalAnnotations(s_annotation); } public override SyntaxNode VisitFileScopedNamespaceDeclaration(FileScopedNamespaceDeclarationSyntax node) @@ -241,7 +241,7 @@ private SyntaxNode VisitBaseNamespaceDeclaration( resultNamespace = resultNamespace.ReplaceToken(firstToken, newFirstToken); } - return resultNamespace; + return resultNamespace.WithAdditionalAnnotations(s_annotation); } public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.cs index f014b6b6ce952..0d4435cf001cb 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.cs @@ -3,17 +3,16 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.RemoveUnnecessaryImports; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -22,14 +21,12 @@ namespace Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryImports; [ExportLanguageService(typeof(IRemoveUnnecessaryImportsService), LanguageNames.CSharp), Shared] -internal partial class CSharpRemoveUnnecessaryImportsService : +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal partial class CSharpRemoveUnnecessaryImportsService() : AbstractRemoveUnnecessaryImportsService { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpRemoveUnnecessaryImportsService() - { - } + private static readonly SyntaxAnnotation s_annotation = new(); private static ISyntaxFormatting SyntaxFormatting => CSharpSyntaxFormatting.Instance; @@ -54,13 +51,22 @@ public override async Task RemoveUnnecessaryImportsAsync( var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var oldRoot = (CompilationUnitSyntax)root; - var newRoot = (CompilationUnitSyntax)new Rewriter(unnecessaryImports, cancellationToken).Visit(oldRoot); + var newRoot = new Rewriter(unnecessaryImports, cancellationToken).Visit(root); cancellationToken.ThrowIfCancellationRequested(); - var spansToFormat = new List(); - AddFormattingSpans(newRoot, spansToFormat, cancellationToken); + using var _ = ArrayBuilder.GetInstance(out var spansToFormat); + foreach (var node in newRoot.GetAnnotatedNodes(s_annotation)) + { + if (node is CompilationUnitSyntax { Members: [var firstMemberA, ..] }) + { + spansToFormat.Add(TextSpan.FromBounds(0, firstMemberA.SpanStart)); + } + else if (node is BaseNamespaceDeclarationSyntax { Members: [var firstMemberB, ..] } baseNamespace) + { + spansToFormat.Add(TextSpan.FromBounds(baseNamespace.Name.Span.End, firstMemberB.SpanStart)); + } + } var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(SyntaxFormatting, cancellationToken).ConfigureAwait(false); var formattedRoot = SyntaxFormatting.GetFormattingResult(newRoot, spansToFormat, formattingOptions, rules: default, cancellationToken).GetFormattedRoot(cancellationToken); @@ -68,31 +74,4 @@ public override async Task RemoveUnnecessaryImportsAsync( return document.WithSyntaxRoot(formattedRoot); } } - - private static void AddFormattingSpans( - CompilationUnitSyntax compilationUnit, - List spans, - CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - spans.Add(TextSpan.FromBounds(0, GetEndPosition(compilationUnit, compilationUnit.Members))); - - foreach (var @namespace in compilationUnit.Members.OfType()) - AddFormattingSpans(@namespace, spans, cancellationToken); - } - - private static void AddFormattingSpans( - BaseNamespaceDeclarationSyntax namespaceMember, - List spans, - CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - spans.Add(TextSpan.FromBounds(namespaceMember.SpanStart, GetEndPosition(namespaceMember, namespaceMember.Members))); - - foreach (var @namespace in namespaceMember.Members.OfType()) - AddFormattingSpans(@namespace, spans, cancellationToken); - } - - private static int GetEndPosition(SyntaxNode container, SyntaxList list) - => list.Count > 0 ? list[0].SpanStart : container.Span.End; } From 2ae8b34942f31aab9b924d4708abf996bc8c5fc9 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 22 Nov 2024 11:07:54 -0800 Subject: [PATCH 494/508] Keep trivia in 'make method synchronous' --- .../MakeMethodSynchronousTests.cs | 55 +++++++++--------- .../RemoveAsyncModifierTests.cs | 48 +++++++++++++--- .../Test/Utilities/CSharp/CSharpTestBase.cs | 2 +- .../CodeGeneration/CSharpSyntaxGenerator.cs | 56 +++++++++---------- .../MemberDeclarationSyntaxExtensions.cs | 6 +- 5 files changed, 97 insertions(+), 70 deletions(-) diff --git a/src/Analyzers/CSharp/Tests/MakeMethodSynchronous/MakeMethodSynchronousTests.cs b/src/Analyzers/CSharp/Tests/MakeMethodSynchronous/MakeMethodSynchronousTests.cs index 899e883712fe8..5747aa4eff078 100644 --- a/src/Analyzers/CSharp/Tests/MakeMethodSynchronous/MakeMethodSynchronousTests.cs +++ b/src/Analyzers/CSharp/Tests/MakeMethodSynchronous/MakeMethodSynchronousTests.cs @@ -16,9 +16,10 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.MakeMethodSynchronous; EmptyDiagnosticAnalyzer, CSharpMakeMethodSynchronousCodeFixProvider>; -public class MakeMethodSynchronousTests +[Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] +public sealed class MakeMethodSynchronousTests { - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestTaskReturnType() { await VerifyCS.VerifyCodeFixAsync( @@ -44,7 +45,7 @@ void Goo() """); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestTaskOfTReturnType() { await VerifyCS.VerifyCodeFixAsync( @@ -72,7 +73,7 @@ class C """); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestSecondModifier() { await VerifyCS.VerifyCodeFixAsync( @@ -98,7 +99,7 @@ public void Goo() """); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestFirstModifier() { await VerifyCS.VerifyCodeFixAsync( @@ -124,7 +125,7 @@ public void Goo() """); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestTrailingTrivia() { await VerifyCS.VerifyCodeFixAsync( @@ -151,7 +152,7 @@ void Goo() """); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestRenameMethod() { await VerifyCS.VerifyCodeFixAsync( @@ -177,7 +178,7 @@ void Goo() """); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestRenameMethod1() { await VerifyCS.VerifyCodeFixAsync( @@ -213,7 +214,7 @@ void Bar() """); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestParenthesizedLambda() { var source = @@ -260,7 +261,7 @@ void Goo() }.RunAsync(); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestSimpleLambda() { var source = @@ -307,7 +308,7 @@ void Goo() }.RunAsync(); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestLambdaWithExpressionBody() { var source = @@ -356,7 +357,7 @@ void Goo() }.RunAsync(); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestAnonymousMethod() { var source = @@ -403,7 +404,7 @@ void Goo() }.RunAsync(); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestFixAll() { await VerifyCS.VerifyCodeFixAsync( @@ -443,7 +444,7 @@ void Goo() """); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/13961")] public async Task TestRemoveAwaitFromCaller1() { @@ -492,7 +493,7 @@ void Goo() }.RunAsync(); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/13961")] public async Task TestRemoveAwaitFromCaller2() { @@ -541,7 +542,7 @@ void Goo() }.RunAsync(); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/13961")] public async Task TestRemoveAwaitFromCaller3() { @@ -590,7 +591,7 @@ void Goo() }.RunAsync(); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/13961")] public async Task TestRemoveAwaitFromCaller4() { @@ -639,7 +640,7 @@ void Goo() }.RunAsync(); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/13961")] public async Task TestRemoveAwaitFromCallerNested1() { @@ -690,7 +691,7 @@ int Goo(int i) }.RunAsync(); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/13961")] public async Task TestRemoveAwaitFromCallerNested() { @@ -861,7 +862,7 @@ class C """); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task MethodWithUsingAwait() { var source = @@ -890,7 +891,7 @@ async System.Threading.Tasks.Task MAsync() }.RunAsync(); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task MethodWithUsingNoAwait() { await VerifyCS.VerifyCodeFixAsync( @@ -920,7 +921,7 @@ void M() """); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task MethodWithAwaitForEach() { var source = @@ -943,7 +944,7 @@ await VerifyCS.VerifyCodeFixAsync( source); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task MethodWithForEachNoAwait() { await VerifyCS.VerifyCodeFixAsync( @@ -971,7 +972,7 @@ void M() """); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task MethodWithForEachVariableAwait() { var source = @@ -994,7 +995,7 @@ await VerifyCS.VerifyCodeFixAsync( source); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task MethodWithForEachVariableNoAwait() { await VerifyCS.VerifyCodeFixAsync( @@ -1022,7 +1023,7 @@ void M() """); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestIAsyncEnumerableReturnType() { var source = @@ -1060,7 +1061,7 @@ IEnumerable M() }.RunAsync(); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestIAsyncEnumeratorReturnTypeOnLocalFunction() { var source = diff --git a/src/Analyzers/CSharp/Tests/RemoveAsyncModifier/RemoveAsyncModifierTests.cs b/src/Analyzers/CSharp/Tests/RemoveAsyncModifier/RemoveAsyncModifierTests.cs index 147d114808c47..cf0d1601d21de 100644 --- a/src/Analyzers/CSharp/Tests/RemoveAsyncModifier/RemoveAsyncModifierTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveAsyncModifier/RemoveAsyncModifierTests.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.RemoveAsyncModifier; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Testing; @@ -17,7 +18,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.RemoveAsyncModifier; CSharpRemoveAsyncModifierCodeFixProvider>; [Trait(Traits.Feature, Traits.Features.CodeActionsRemoveAsyncModifier)] -public class RemoveAsyncModifierTests : CodeAnalysis.CSharp.Test.Utilities.CSharpTestBase +public sealed class RemoveAsyncModifierTests { [Fact] public async Task Method_Task_MultipleAndNested() @@ -917,7 +918,7 @@ public void M1() } [Fact] - public async Task ParenthesisedLambda_TaskOfT_ExpressionBody() + public async Task ParenthesizedLambda_TaskOfT_ExpressionBody() { await VerifyCS.VerifyCodeFixAsync( """ @@ -947,7 +948,7 @@ public void M1() } [Fact] - public async Task ParenthesisedLambda_TaskOfT_BlockBody() + public async Task ParenthesizedLambda_TaskOfT_BlockBody() { await VerifyCS.VerifyCodeFixAsync( """ @@ -982,7 +983,7 @@ public void M1() } [Fact] - public async Task ParenthesisedLambda_Task_ExpressionBody() + public async Task ParenthesizedLambda_Task_ExpressionBody() { await VerifyCS.VerifyCodeFixAsync( """ @@ -1012,7 +1013,7 @@ public void M1() } [Fact] - public async Task ParenthesisedLambda_Task_BlockBody() + public async Task ParenthesizedLambda_Task_BlockBody() { await VerifyCS.VerifyCodeFixAsync( """ @@ -1163,7 +1164,7 @@ async IAsyncEnumerable M() yield return 1; } } - """ + AsyncStreamsTypes; + """ + CSharpTestBase.AsyncStreamsTypes; await new VerifyCS.Test { @@ -1207,7 +1208,7 @@ async void M() } [Fact] - public async Task ParenthesisedLambda_AsyncVoid_Missing() + public async Task ParenthesizedLambda_AsyncVoid_Missing() { var source = """ using System; @@ -1263,4 +1264,37 @@ void M() FixedCode = source, }.RunAsync(); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65380")] + public async Task TestCloseBraceTrivia() + { + await VerifyCS.VerifyCodeFixAsync( + """ + using System; + using System.Threading.Tasks; + + public class Class1 + { + public async Task {|CS1998:Goo|}() + { + //Hello + Console.WriteLine("Goo"); + //World + } + } + """, + """ + using System; + using System.Threading.Tasks; + + public class Class1 + { + public void Goo() + { + //Hello + Console.WriteLine("Goo"); + //World + } + } + """); + } } diff --git a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs index e5bc18a51842e..a2d5397815847 100644 --- a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs @@ -587,7 +587,7 @@ public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter } "; - protected static readonly string AsyncStreamsTypes = DisposableAsyncEnumeratorDefinition + CommonAsyncStreamsTypes; + public static readonly string AsyncStreamsTypes = DisposableAsyncEnumeratorDefinition + CommonAsyncStreamsTypes; protected static readonly string EnumeratorCancellationAttributeType = @" namespace System.Runtime.CompilerServices diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs index 95dc4d1a23c5d..afc010776719f 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs @@ -2499,38 +2499,27 @@ SyntaxKind.AddAccessorDeclaration or public override SyntaxNode WithStatements(SyntaxNode declaration, IEnumerable statements) { - var body = CreateBlock(statements); + var existingBlock = declaration switch + { + BaseMethodDeclarationSyntax baseMethod => baseMethod.Body, + AccessorDeclarationSyntax accessor => accessor.Body, + LocalFunctionStatementSyntax localFunction => localFunction.Body, + AnonymousFunctionExpressionSyntax anonymousFunction => anonymousFunction.Block, + _ => null, + }; + + var body = CreateBlock(statements, existingBlock, addSimplifierAnnotation: false); var somebody = statements != null ? body : null; var semicolon = statements == null ? SemicolonToken : default; - switch (declaration.Kind()) + return declaration switch { - case SyntaxKind.MethodDeclaration: - return ((MethodDeclarationSyntax)declaration).WithBody(somebody).WithSemicolonToken(semicolon).WithExpressionBody(null); - case SyntaxKind.OperatorDeclaration: - return ((OperatorDeclarationSyntax)declaration).WithBody(somebody).WithSemicolonToken(semicolon).WithExpressionBody(null); - case SyntaxKind.ConversionOperatorDeclaration: - return ((ConversionOperatorDeclarationSyntax)declaration).WithBody(somebody).WithSemicolonToken(semicolon).WithExpressionBody(null); - case SyntaxKind.ConstructorDeclaration: - return ((ConstructorDeclarationSyntax)declaration).WithBody(somebody).WithSemicolonToken(semicolon).WithExpressionBody(null); - case SyntaxKind.DestructorDeclaration: - return ((DestructorDeclarationSyntax)declaration).WithBody(somebody).WithSemicolonToken(semicolon).WithExpressionBody(null); - case SyntaxKind.LocalFunctionStatement: - return ((LocalFunctionStatementSyntax)declaration).WithBody(somebody).WithSemicolonToken(semicolon).WithExpressionBody(null); - case SyntaxKind.AnonymousMethodExpression: - return ((AnonymousMethodExpressionSyntax)declaration).WithBody(body); - case SyntaxKind.ParenthesizedLambdaExpression: - return ((ParenthesizedLambdaExpressionSyntax)declaration).WithBody(body); - case SyntaxKind.SimpleLambdaExpression: - return ((SimpleLambdaExpressionSyntax)declaration).WithBody(body); - case SyntaxKind.GetAccessorDeclaration: - case SyntaxKind.SetAccessorDeclaration: - case SyntaxKind.AddAccessorDeclaration: - case SyntaxKind.RemoveAccessorDeclaration: - return ((AccessorDeclarationSyntax)declaration).WithBody(somebody).WithSemicolonToken(semicolon).WithExpressionBody(null); - default: - return declaration; - } + BaseMethodDeclarationSyntax baseMethod => baseMethod.WithBody(somebody).WithSemicolonToken(semicolon).WithExpressionBody(null), + AccessorDeclarationSyntax accessor => accessor.WithBody(somebody).WithSemicolonToken(semicolon).WithExpressionBody(null), + LocalFunctionStatementSyntax localFunction => localFunction.WithBody(somebody).WithSemicolonToken(semicolon).WithExpressionBody(null), + AnonymousFunctionExpressionSyntax anonymousFunction => anonymousFunction.WithBody(body), + _ => declaration, + }; } public override IReadOnlyList GetAccessors(SyntaxNode declaration) @@ -3134,8 +3123,15 @@ public override SyntaxNode IfStatement(SyntaxNode condition, IEnumerable? statements = null) - => SyntaxFactory.Block(AsStatementList(statements)).WithAdditionalAnnotations(Simplifier.Annotation); + private static BlockSyntax CreateBlock( + IEnumerable? statements = null, + BlockSyntax? existingBlock = null, + bool addSimplifierAnnotation = true) + { + var block = existingBlock ?? SyntaxFactory.Block(); + block = block.WithStatements(AsStatementList(statements)); + return addSimplifierAnnotation ? block.WithAdditionalAnnotations(Simplifier.Annotation) : block; + } private static SyntaxList AsStatementList(IEnumerable? nodes) => nodes == null ? default : [.. nodes.Select(AsStatement)]; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/MemberDeclarationSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/MemberDeclarationSyntaxExtensions.cs index b1cf56d09951c..a8d8ba4ab5a73 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/MemberDeclarationSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/MemberDeclarationSyntaxExtensions.cs @@ -167,11 +167,7 @@ public static bool HasMethodShape(this MemberDeclarationSyntax memberDeclaration => memberDeclaration is BaseMethodDeclarationSyntax; public static BlockSyntax GetBody(this MemberDeclarationSyntax memberDeclaration) - => memberDeclaration switch - { - BaseMethodDeclarationSyntax method => method.Body, - _ => null, - }; + => (memberDeclaration as BaseMethodDeclarationSyntax)?.Body; public static ArrowExpressionClauseSyntax GetExpressionBody(this MemberDeclarationSyntax memberDeclaration) => memberDeclaration switch From af008100a609746b2ef05dfba0a0dcb57e3fc0d5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 22 Nov 2024 11:14:38 -0800 Subject: [PATCH 495/508] Docs --- .../RemoveAsyncModifier/RemoveAsyncModifierTests.cs | 3 ++- .../Portable/CodeGeneration/CSharpSyntaxGenerator.cs | 11 ++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Analyzers/CSharp/Tests/RemoveAsyncModifier/RemoveAsyncModifierTests.cs b/src/Analyzers/CSharp/Tests/RemoveAsyncModifier/RemoveAsyncModifierTests.cs index cf0d1601d21de..99fad10dcca2d 100644 --- a/src/Analyzers/CSharp/Tests/RemoveAsyncModifier/RemoveAsyncModifierTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveAsyncModifier/RemoveAsyncModifierTests.cs @@ -1288,10 +1288,11 @@ public class Class1 public class Class1 { - public void Goo() + public Task Goo() { //Hello Console.WriteLine("Goo"); + return Task.CompletedTask; //World } } diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs index afc010776719f..e46146d0a7d73 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs @@ -14,6 +14,7 @@ using Microsoft.CodeAnalysis.CSharp.LanguageService; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -3129,7 +3130,15 @@ private static BlockSyntax CreateBlock( bool addSimplifierAnnotation = true) { var block = existingBlock ?? SyntaxFactory.Block(); - block = block.WithStatements(AsStatementList(statements)); + + var statementList = AsStatementList(statements); + + // If we're adding any statements, make sure the open brace can move around. This allows `{ }` on an existing + // one-line construct to have the braces move to their own lines in accordance to the user's formatting rules. + if (statementList.Count > 0) + block = block.WithOpenBraceToken(block.OpenBraceToken.WithAdditionalAnnotations(Formatter.Annotation)); + + block = block.WithStatements(statementList); return addSimplifierAnnotation ? block.WithAdditionalAnnotations(Simplifier.Annotation) : block; } From 2c4b274e82ef2e42caf51d3babbf09587a6c9a9f Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Fri, 22 Nov 2024 11:23:30 -0800 Subject: [PATCH 496/508] Fix symbol display for backing fields (#76000) --- .../SymbolDisplayVisitor.Members.cs | 24 ++- .../SymbolDisplay/SymbolDisplayTests.cs | 178 +++++++++++++++++- .../Test/Symbol/Symbols/Source/RecordTests.cs | 4 +- .../Portable/CodeGen/CompilationTestData.cs | 2 +- .../SymbolDisplayCompilerInternalOptions.cs | 5 +- .../SymbolDisplay/SymbolDisplayFormat.cs | 4 +- .../SymbolDisplayVisitor.Members.vb | 18 +- .../SymbolDisplay/SymbolDisplayTests.vb | 2 +- .../QuickInfo/SemanticQuickInfoSourceTests.cs | 18 ++ 9 files changed, 227 insertions(+), 28 deletions(-) diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs index 0825bdf3f429f..0fa99aec99b2f 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs @@ -59,7 +59,15 @@ public override void VisitField(IFieldSymbol symbol) AddPunctuation(SyntaxKind.DotToken); } - if (symbol.ContainingType.TypeKind == TypeKind.Enum) + if (!Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) + && symbol is Symbols.PublicModel.FieldSymbol + && symbol.AssociatedSymbol is IPropertySymbol associatedProperty) + { + AddPropertyNameAndParameters(associatedProperty); + AddPunctuation(SyntaxKind.DotToken); + AddKeyword(SyntaxKind.FieldKeyword); + } + else if (symbol.ContainingType.TypeKind == TypeKind.Enum) { Builder.Add(CreatePart(SymbolDisplayPartKind.EnumMemberName, symbol, symbol.Name)); } @@ -321,7 +329,7 @@ public override void VisitMethod(IMethodSymbol symbol) // If we're using the metadata format, then include the return type. // Otherwise we eschew it since it is redundant in a conversion // signature. - if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames)) + if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames)) { goto default; } @@ -332,7 +340,7 @@ public override void VisitMethod(IMethodSymbol symbol) // If we're using the metadata format, then include the return type. // Otherwise we eschew it since it is redundant in a conversion // signature. - if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) || + if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) || tryGetUserDefinedOperatorTokenKind(symbol.MetadataName) == SyntaxKind.None) { goto default; @@ -462,7 +470,7 @@ public override void VisitMethod(IMethodSymbol symbol) // Note: we are using the metadata name also in the case that // symbol.containingType is null (which should never be the case here) or is an // anonymous type (which 'does not have a name'). - var name = Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) || symbol.ContainingType == null || symbol.ContainingType.IsAnonymousType + var name = Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) || symbol.ContainingType == null || symbol.ContainingType.IsAnonymousType ? symbol.Name : symbol.ContainingType.Name; @@ -476,7 +484,7 @@ public override void VisitMethod(IMethodSymbol symbol) var partKind = GetPartKindForConstructorOrDestructor(symbol); // Note: we are using the metadata name also in the case that symbol.containingType is null, which should never be the case here. - if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) || symbol.ContainingType == null) + if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) || symbol.ContainingType == null) { Builder.Add(CreatePart(partKind, symbol, symbol.Name)); } @@ -491,7 +499,7 @@ public override void VisitMethod(IMethodSymbol symbol) { AddExplicitInterfaceIfNeeded(symbol.ExplicitInterfaceImplementations); - if (!Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) && + if (!Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) && symbol.GetSymbol()?.OriginalDefinition is SourceUserDefinedOperatorSymbolBase sourceUserDefinedOperatorSymbolBase) { var operatorName = symbol.MetadataName; @@ -520,7 +528,7 @@ public override void VisitMethod(IMethodSymbol symbol) case MethodKind.UserDefinedOperator: case MethodKind.BuiltinOperator: { - if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames)) + if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames)) { Builder.Add(CreatePart(SymbolDisplayPartKind.MethodName, symbol, symbol.MetadataName)); } @@ -541,7 +549,7 @@ public override void VisitMethod(IMethodSymbol symbol) } case MethodKind.Conversion: { - if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames)) + if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames)) { Builder.Add(CreatePart(SymbolDisplayPartKind.MethodName, symbol, symbol.MetadataName)); } diff --git a/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs b/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs index 5bd36448cc083..add91eb5d5684 100644 --- a/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs @@ -1448,7 +1448,7 @@ class C { var format = new SymbolDisplayFormat( memberOptions: SymbolDisplayMemberOptions.IncludeType, - compilerInternalOptions: SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames); + compilerInternalOptions: SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames); TestSymbolDescription( text, @@ -2049,8 +2049,10 @@ class C SymbolDisplayPartKind.Punctuation); } - [Fact] - public void TestPropertyGetAccessor() + [Theory] + [InlineData(SymbolDisplayCompilerInternalOptions.None)] + [InlineData(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames)] + internal void TestPropertyGetAccessor(SymbolDisplayCompilerInternalOptions internalOptions) { var text = @" class C { @@ -2062,6 +2064,7 @@ class C { GetMembers("get_P").Single(); var format = new SymbolDisplayFormat( + internalOptions, memberOptions: SymbolDisplayMemberOptions.IncludeAccessibility | SymbolDisplayMemberOptions.IncludeContainingType | @@ -2123,6 +2126,175 @@ class C { SymbolDisplayPartKind.Keyword); } + [Fact] + public void TestPropertyBackingField() + { + var text = @" +#nullable enable +class C { + string P { get; set; } } +"; + + Func findSymbol = global => + global.GetTypeMembers("C", 0).Single(). + GetMembers("

k__BackingField").Single(); + + var format = new SymbolDisplayFormat( + memberOptions: + SymbolDisplayMemberOptions.IncludeAccessibility | + SymbolDisplayMemberOptions.IncludeContainingType | + SymbolDisplayMemberOptions.IncludeExplicitInterface | + SymbolDisplayMemberOptions.IncludeModifiers | + SymbolDisplayMemberOptions.IncludeParameters | + SymbolDisplayMemberOptions.IncludeType); + + TestSymbolDescription( + text, + findSymbol, + format, + "private String C.P.field", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.ClassName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.ClassName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Keyword); + } + + [Fact] + public void TestPropertyBackingFieldFromCompilationReference() + { + var text = @" +#nullable enable +class C { + string P { get; set; } } +"; + + var format = new SymbolDisplayFormat( + memberOptions: SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeModifiers | SymbolDisplayMemberOptions.IncludeAccessibility | SymbolDisplayMemberOptions.IncludeType, + parameterOptions: SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeName | SymbolDisplayParameterOptions.IncludeDefaultValue, + miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + + var comp1 = CreateCompilation(text); + var comp2 = CreateCompilation("", references: [comp1.ToMetadataReference()], options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); + + var prop = comp2.GetMember("C.

k__BackingField").GetPublicSymbol(); + var parts = SymbolDisplay.ToDisplayParts(prop, format); + + Verify( + parts, + "private string P.field", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Keyword); + } + + [Fact] + public void TestPropertyBackingField_UseMetadataMethodNames() + { + var text = @" +#nullable enable +class C { + string P { get; set; } } +"; + + Func findSymbol = global => + global.GetTypeMembers("C", 0).Single(). + GetMembers("

k__BackingField").Single(); + + var format = new SymbolDisplayFormat( + compilerInternalOptions: SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames, + memberOptions: + SymbolDisplayMemberOptions.IncludeAccessibility | + SymbolDisplayMemberOptions.IncludeContainingType | + SymbolDisplayMemberOptions.IncludeExplicitInterface | + SymbolDisplayMemberOptions.IncludeModifiers | + SymbolDisplayMemberOptions.IncludeParameters | + SymbolDisplayMemberOptions.IncludeType); + + TestSymbolDescription( + text, + findSymbol, + format, + "private String C.

k__BackingField", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.ClassName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.ClassName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.FieldName); + } + + [Theory] + [InlineData(SymbolDisplayCompilerInternalOptions.None)] + [InlineData(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames)] + internal void TestPropertyBackingFieldFromMetadata(SymbolDisplayCompilerInternalOptions internalOptions) + { + // Metadata symbols do not associate the backing field with the property, so the metadata name is always displayed. + var text = @" +#nullable enable +class C { + string P { get; set; } } +"; + + var format = new SymbolDisplayFormat( + compilerInternalOptions: internalOptions, + memberOptions: SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeModifiers | SymbolDisplayMemberOptions.IncludeAccessibility | SymbolDisplayMemberOptions.IncludeType, + parameterOptions: SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeName | SymbolDisplayParameterOptions.IncludeDefaultValue, + miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + + var comp1 = CreateCompilation(text); + var comp2 = CreateCompilation("", references: [comp1.EmitToImageReference()], options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); + + var prop = comp2.GetMember("C.

k__BackingField").GetPublicSymbol(); + var parts = SymbolDisplay.ToDisplayParts(prop, format); + + Verify( + parts, + "private string

k__BackingField", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.FieldName); + } + + [Fact] + public void TestPropertyBackingFieldVB() + { + var text = @" +Class A + Public Property Prop As String +End Class"; + + var format = new SymbolDisplayFormat( + memberOptions: SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeModifiers | SymbolDisplayMemberOptions.IncludeAccessibility | SymbolDisplayMemberOptions.IncludeType, + parameterOptions: SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeName | SymbolDisplayParameterOptions.IncludeDefaultValue, + miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + + var comp = CreateVisualBasicCompilation("c", text); + var a = (ITypeSymbol)comp.GlobalNamespace.GetMembers("A").Single(); + var goo = a.GetMembers("_Prop").Single(); + var parts = SymbolDisplay.ToDisplayParts(goo, format); + + Verify( + parts, + "private string _Prop", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.FieldName); + } + [Fact] public void TestMemberEventAll() { diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs index 603c82ac99b84..f824f4e8e58a1 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs @@ -1076,11 +1076,11 @@ record C AssertEx.Equal(new[] { "System.Type! C.EqualityContract.get", "System.Type! C.EqualityContract { get; }", - "System.Int32 C.k__BackingField", + "System.Int32 C.X.field", "System.Int32 C.X { get; init; }", "System.Int32 C.X.get", "void C.X.init", - "System.String! C.k__BackingField", + "System.String! C.Y.field", "System.String! C.Y { get; init; }", "System.String! C.Y.get", "void C.Y.init", diff --git a/src/Compilers/Core/Portable/CodeGen/CompilationTestData.cs b/src/Compilers/Core/Portable/CodeGen/CompilationTestData.cs index b57b9d31da861..3797649644974 100644 --- a/src/Compilers/Core/Portable/CodeGen/CompilationTestData.cs +++ b/src/Compilers/Core/Portable/CodeGen/CompilationTestData.cs @@ -86,7 +86,7 @@ public ImmutableDictionary GetMethodsByName() private static readonly SymbolDisplayFormat _testDataKeyFormat = new SymbolDisplayFormat( compilerInternalOptions: - SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames | + SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames | SymbolDisplayCompilerInternalOptions.IncludeContainingFileForFileTypes, globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.OmittedAsContaining, typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, diff --git a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayCompilerInternalOptions.cs b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayCompilerInternalOptions.cs index b7ecd7c4f9f6b..0acb9aa43f5fb 100644 --- a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayCompilerInternalOptions.cs +++ b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayCompilerInternalOptions.cs @@ -17,9 +17,10 @@ internal enum SymbolDisplayCompilerInternalOptions None = 0, ///

- /// ".ctor" instead of "Goo" + /// - ".ctor" instead of "Goo" + /// - "<Prop>k__backingField" instead of "Prop.field" (for C# backing fields) /// - UseMetadataMethodNames = 1 << 0, + UseMetadataMemberNames = 1 << 0, /// /// "List`1" instead of "List<T>" ("List(of T)" in VB). Overrides GenericsOptions on diff --git a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayFormat.cs b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayFormat.cs index 5e0ba2c34e6ca..20f1a5586c8b8 100644 --- a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayFormat.cs +++ b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayFormat.cs @@ -199,7 +199,7 @@ public class SymbolDisplayFormat SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier, compilerInternalOptions: SymbolDisplayCompilerInternalOptions.IncludeScriptType | - SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames | + SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames | SymbolDisplayCompilerInternalOptions.FlagMissingMetadataTypes | SymbolDisplayCompilerInternalOptions.IncludeCustomModifiers | SymbolDisplayCompilerInternalOptions.IncludeContainingFileForFileTypes); @@ -257,7 +257,7 @@ public class SymbolDisplayFormat miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes | SymbolDisplayMiscellaneousOptions.ExpandValueTuple, - compilerInternalOptions: SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames); + compilerInternalOptions: SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames); /// /// Used to normalize explicit interface implementation member names. diff --git a/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.vb b/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.vb index 4f872a3a02462..93acdd2c23223 100644 --- a/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.vb +++ b/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.vb @@ -199,7 +199,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic AddSpace() Case MethodKind.PropertyGet - If Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) Then + If Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) Then AddKeyword(SyntaxKind.FunctionKeyword) AddSpace() Else @@ -210,7 +210,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End If Case MethodKind.PropertySet - If Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) Then + If Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) Then AddKeyword(SyntaxKind.SubKeyword) AddSpace() Else @@ -224,7 +224,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic MethodKind.EventRemove, MethodKind.EventRaise - If Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) Then + If Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) Then AddKeyword(SyntaxKind.SubKeyword) AddSpace() Else @@ -245,7 +245,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Dim tokenKind As SyntaxKind = TryGetConversionTokenKind(symbol) - If tokenKind = SyntaxKind.None OrElse Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) Then + If tokenKind = SyntaxKind.None OrElse Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) Then AddKeyword(SyntaxKind.FunctionKeyword) AddSpace() Else @@ -259,7 +259,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Case MethodKind.UserDefinedOperator, MethodKind.BuiltinOperator Dim tokenKind As SyntaxKind = TryGetOperatorTokenKind(symbol) - If tokenKind = SyntaxKind.None OrElse Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) Then + If tokenKind = SyntaxKind.None OrElse Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) Then AddKeyword(SyntaxKind.FunctionKeyword) AddSpace() Else @@ -322,7 +322,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic MethodKind.EventRemove, MethodKind.EventRaise - If Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) Then + If Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) Then Builder.Add(CreatePart(SymbolDisplayPartKind.MethodName, symbol, symbol.Name, visitedParents)) Else Dim associatedPropertyOrEvent = symbol.AssociatedSymbol @@ -336,7 +336,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End If Case MethodKind.Constructor, MethodKind.StaticConstructor - If Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) Then + If Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) Then Builder.Add(CreatePart(SymbolDisplayPartKind.MethodName, symbol, symbol.Name, visitedParents)) Else AddKeyword(SyntaxKind.NewKeyword) @@ -346,7 +346,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Dim tokenKind As SyntaxKind = TryGetOperatorTokenKind(symbol) - If tokenKind = SyntaxKind.None OrElse Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) Then + If tokenKind = SyntaxKind.None OrElse Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) Then Builder.Add(CreatePart(SymbolDisplayPartKind.MethodName, symbol, symbol.Name, visitedParents)) ElseIf SyntaxFacts.IsKeywordKind(tokenKind) Then ' Prefer to add the operator token as a keyword if it considered both a keyword and an operator. @@ -361,7 +361,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Case MethodKind.Conversion Dim tokenKind As SyntaxKind = TryGetConversionTokenKind(symbol) - If tokenKind = SyntaxKind.None OrElse Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) Then + If tokenKind = SyntaxKind.None OrElse Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) Then Builder.Add(CreatePart(SymbolDisplayPartKind.MethodName, symbol, symbol.Name, visitedParents)) Else AddKeyword(SyntaxKind.CTypeKeyword) diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb index c6a271ba02d81..5f52b065c237d 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb @@ -1552,7 +1552,7 @@ end class Dim format = New SymbolDisplayFormat( memberOptions:=SymbolDisplayMemberOptions.IncludeType, kindOptions:=SymbolDisplayKindOptions.IncludeMemberKeyword, - compilerInternalOptions:=SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) + compilerInternalOptions:=SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) TestSymbolDescription( text, diff --git a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs index 8b0dc9a91d517..69f27e542c324 100644 --- a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs +++ b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs @@ -8926,6 +8926,24 @@ void M(Person p) Documentation("The person's first name.")); } + [Fact] + public async Task QuickInfoFieldKeyword() + { + await TestWithOptionsAsync( + Options.Regular.WithLanguageVersion(LanguageVersion.Preview), + """ + class C + { + int Prop + { + get => $$field; + set => field = value; + } + } + """, +MainDescription("(field) int C.Prop.field")); + } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51615")] public async Task TestVarPatternOnVarKeyword() { From 2731462ddaee30214a94a03b296e0b9bfef24c6e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 22 Nov 2024 12:07:13 -0800 Subject: [PATCH 497/508] Fix --- .../AssignOutParameters/AssignOutParametersAtStartTests.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Analyzers/CSharp/Tests/AssignOutParameters/AssignOutParametersAtStartTests.cs b/src/Analyzers/CSharp/Tests/AssignOutParameters/AssignOutParametersAtStartTests.cs index a9315dcd7cec7..8009c4464e795 100644 --- a/src/Analyzers/CSharp/Tests/AssignOutParameters/AssignOutParametersAtStartTests.cs +++ b/src/Analyzers/CSharp/Tests/AssignOutParameters/AssignOutParametersAtStartTests.cs @@ -514,7 +514,6 @@ char M(bool b, out int i, out int j) return 'a'; } } - char N(bool b, out int i, out int j) { i = 0; @@ -599,7 +598,6 @@ char M(bool b, out int i, out int j) else return 'a'; } - char N(bool b, out int i, out int j) { i = 0; @@ -710,7 +708,6 @@ char M(bool b, out int i, out int j) return 'a'; } } - char N(bool b, out int i, out int j) { i = 0; From ea5386fc617822ba0c5ed4ca02ecde86abd9f2cc Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 22 Nov 2024 12:17:03 -0800 Subject: [PATCH 498/508] Fix mixed case in automatic line ending --- .../AutomaticLineEnderCommandHandler.cs | 27 +++++++++---------- ...utomaticLineEnderCommandHandler_Helpers.cs | 2 +- .../AutomaticLineEnderTests.cs | 27 ++++++++++++++++--- 3 files changed, 36 insertions(+), 20 deletions(-) diff --git a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs index 9636bd3fbb871..0341f9fbbce32 100644 --- a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs +++ b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs @@ -32,9 +32,6 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.AutomaticCompletion; -/// -/// csharp automatic line ender command handler -/// [Export(typeof(ICommandHandler))] [ContentType(ContentTypeNames.CSharpContentType)] [Name(PredefinedCommandHandlerNames.AutomaticLineEnder)] @@ -324,18 +321,18 @@ protected override void ModifySelectedNode( { // For these syntax node, braces pair could be easily added by modify the syntax tree if (selectedNode is BaseTypeDeclarationSyntax - or BaseMethodDeclarationSyntax - or LocalFunctionStatementSyntax - or AccessorDeclarationSyntax - or ObjectCreationExpressionSyntax - or WhileStatementSyntax - or ForEachStatementSyntax - or ForStatementSyntax - or LockStatementSyntax - or UsingStatementSyntax - or DoStatementSyntax - or IfStatementSyntax - or ElseClauseSyntax) + or BaseMethodDeclarationSyntax + or LocalFunctionStatementSyntax + or AccessorDeclarationSyntax + or ObjectCreationExpressionSyntax + or WhileStatementSyntax + or CommonForEachStatementSyntax + or ForStatementSyntax + or LockStatementSyntax + or UsingStatementSyntax + or DoStatementSyntax + or IfStatementSyntax + or ElseClauseSyntax) { // Add the braces and get the next caretPosition var (newRoot, nextCaretPosition) = AddBraceToSelectedNode(document.SolutionServices, document.Root, selectedNode, formattingOptions, cancellationToken); diff --git a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler_Helpers.cs b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler_Helpers.cs index 4986a0098536b..26c858636746f 100644 --- a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler_Helpers.cs +++ b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler_Helpers.cs @@ -942,7 +942,7 @@ private static SyntaxNode AddBlockToEmbeddedStatementOwner( return embeddedStatementOwner switch { DoStatementSyntax doStatementNode => doStatementNode.WithStatement(block), - ForEachStatementSyntax forEachStatementNode => forEachStatementNode.WithStatement(block), + CommonForEachStatementSyntax forEachStatementNode => forEachStatementNode.WithStatement(block), ForStatementSyntax forStatementNode => forStatementNode.WithStatement(block), IfStatementSyntax ifStatementNode => ifStatementNode.WithStatement(block), ElseClauseSyntax elseClauseNode => elseClauseNode.WithStatement(block), diff --git a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs index d795151cdd000..5d01e77c022a5 100644 --- a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs +++ b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using Microsoft.CodeAnalysis.Editor.CSharp.AutomaticCompletion; using Microsoft.CodeAnalysis.Editor.UnitTests.AutomaticCompletion; @@ -16,7 +14,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.AutomaticCompletion; [Trait(Traits.Feature, Traits.Features.AutomaticCompletion)] -public class AutomaticLineEnderTests : AbstractAutomaticLineEnderTests +public sealed class AutomaticLineEnderTests : AbstractAutomaticLineEnderTests { [WpfFact] public void Creation() @@ -609,7 +607,7 @@ object goo(object o) } [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530352")] - public void EmbededStatement3() + public void EmbeddedStatement3() { Test(@"class Program { @@ -629,6 +627,27 @@ void Method() }"); } + [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530352")] + public void EmbeddedStatement4() + { + Test(@"class Program +{ + void Method() + { + foreach (var (x, y) in z) + { + $$ + } + } +}", @"class Program +{ + void Method() + { + foreach (var (x, y) in z$$) + } +}"); + } + [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530716")] public void DoNotAssertOnMultilineToken() { From 5664eb223d539d0bc0effb3f572863a254d8d7e1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 22 Nov 2024 12:17:31 -0800 Subject: [PATCH 499/508] Spelling --- .../AutomaticCompletion/AutomaticLineEnderTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs index 5d01e77c022a5..15a94006a007f 100644 --- a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs +++ b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs @@ -137,7 +137,7 @@ event System.EventHandler$$ } [WpfFact] - public void EmbededStatement() + public void EmbeddedStatement() { Test(@"class C { @@ -158,7 +158,7 @@ void Method() } [WpfFact] - public void EmbededStatement1() + public void EmbeddedStatement1() { Test(@"class C { @@ -179,7 +179,7 @@ void Method() } [WpfFact] - public void EmbededStatement2() + public void EmbeddedStatement2() { Test(@"class C { From d2c36dd9db0d096058a7aeb24d27e75aa9d48f27 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 22 Nov 2024 12:19:19 -0800 Subject: [PATCH 500/508] File scoped namespaces --- .../AutomaticLineEnderTests.cs | 5254 +++++++++-------- 1 file changed, 2817 insertions(+), 2437 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs index 15a94006a007f..812a649812878 100644 --- a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs +++ b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs @@ -19,878 +19,1081 @@ public sealed class AutomaticLineEnderTests : AbstractAutomaticLineEnderTests [WpfFact] public void Creation() { - Test(@" -$$", "$$"); + Test(""" + $$ + """, "$$"); } [WpfFact] public void Usings() { - Test(@"using System; -$$", @"using System$$"); + Test(""" + using System; + $$ + """, @"using System$$"); } [WpfFact] public void Namespace() { - Test(@"namespace {} -$$", @"namespace {$$}"); + Test(""" + namespace {} + $$ + """, @"namespace {$$}"); } [WpfFact] public void Class() { - Test(@"class {} -$$", "class {$$}"); + Test(""" + class {} + $$ + """, "class {$$}"); } [WpfFact] public void Method() { - Test(@"class C -{ - void Method() {$$} -}", @"class C -{ - void Method() {$$} -}", assertNextHandlerInvoked: true); + Test(""" + class C + { + void Method() {$$} + } + """, """ + class C + { + void Method() {$$} + } + """, assertNextHandlerInvoked: true); } [WpfFact] public void Field() { - Test(@"class C -{ - private readonly int i = 3; - $$ -}", @"class C -{ - pri$$vate re$$adonly i$$nt i = 3$$ -}"); + Test(""" + class C + { + private readonly int i = 3; + $$ + } + """, """ + class C + { + pri$$vate re$$adonly i$$nt i = 3$$ + } + """); } [WpfFact] public void EventField() { - Test(@"class C -{ - event System.EventHandler e = null; - $$ -}", @"class C -{ - e$$vent System.Even$$tHandler e$$ = null$$ -}"); + Test(""" + class C + { + event System.EventHandler e = null; + $$ + } + """, """ + class C + { + e$$vent System.Even$$tHandler e$$ = null$$ + } + """); } [WpfFact] public void Field2() { - Test(@"class C -{ - private readonly int i; - $$ -}", @"class C -{ - private readonly int i$$ -}"); + Test(""" + class C + { + private readonly int i; + $$ + } + """, """ + class C + { + private readonly int i$$ + } + """); } [WpfFact] public void EventField2() { - Test(@"class C -{ - event System.EventHandler e - { - $$ - } -}", @"class C -{ - eve$$nt System.E$$ventHandler e$$ -}"); + Test(""" + class C + { + event System.EventHandler e + { + $$ + } + } + """, """ + class C + { + eve$$nt System.E$$ventHandler e$$ + } + """); } [WpfFact] public void Field3() { - Test(@"class C -{ - private readonly int - $$ -}", @"class C -{ - private readonly int$$ -}"); + Test(""" + class C + { + private readonly int + $$ + } + """, """ + class C + { + private readonly int$$ + } + """); } [WpfFact] public void EventField3() { - Test(@"class C -{ - event System.EventHandler - $$ -}", @"class C -{ - event System.EventHandler$$ -}"); + Test(""" + class C + { + event System.EventHandler + $$ + } + """, """ + class C + { + event System.EventHandler$$ + } + """); } [WpfFact] public void EmbeddedStatement() { - Test(@"class C -{ - void Method() - { - if (true) - { - $$ - } - } -}", @"class C -{ - void Method() - { - if (true) $$ - } -}"); + Test(""" + class C + { + void Method() + { + if (true) + { + $$ + } + } + } + """, """ + class C + { + void Method() + { + if (true) $$ + } + } + """); } [WpfFact] public void EmbeddedStatement1() { - Test(@"class C -{ - void Method() - { - if (true) - Console.WriteLine() - $$ - } -}", @"class C -{ - void Method() - { - if (true) - Console.WriteLine()$$ - } -}"); + Test(""" + class C + { + void Method() + { + if (true) + Console.WriteLine() + $$ + } + } + """, """ + class C + { + void Method() + { + if (true) + Console.WriteLine()$$ + } + } + """); } [WpfFact] public void EmbeddedStatement2() { - Test(@"class C -{ - void Method() - { - if (true) - Console.WriteLine(); - $$ - } -}", @"class C -{ - void Method() - { - if (true) - Console.WriteLine($$) - } -}"); + Test(""" + class C + { + void Method() + { + if (true) + Console.WriteLine(); + $$ + } + } + """, """ + class C + { + void Method() + { + if (true) + Console.WriteLine($$) + } + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/57323")] public void EmbededStatementFollowedByStatement() { - Test(@"class C -{ - void Method() - { - if (true) - { - } - if (true) - { - $$ - } - if (true) - { - } - } -}", @"class C -{ - void Method() - { - if (true) - { - } - if (true$$) - if (true) - { - } - } -}"); + Test(""" + class C + { + void Method() + { + if (true) + { + } + if (true) + { + $$ + } + if (true) + { + } + } + } + """, """ + class C + { + void Method() + { + if (true) + { + } + if (true$$) + if (true) + { + } + } + } + """); } [WpfFact] public void Statement() { - Test(@"class C -{ - void Method() - { - int i; - $$ - } -}", @"class C -{ - void Method() - { - int i$$ - } -}"); + Test(""" + class C + { + void Method() + { + int i; + $$ + } + } + """, """ + class C + { + void Method() + { + int i$$ + } + } + """); } [WpfFact] public void Statement1() { - Test(@"class C -{ - void Method() - { - int - $$ - } -}", @"class C -{ - void Method() - { - int$$ - } -}"); + Test(""" + class C + { + void Method() + { + int + $$ + } + } + """, """ + class C + { + void Method() + { + int$$ + } + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/3944")] public void ExpressionBodiedMethod() { - Test(@"class T -{ - int M() => 1 + 2; - $$ -}", @"class T -{ - int M() => 1 + 2$$ -}"); + Test(""" + class T + { + int M() => 1 + 2; + $$ + } + """, """ + class T + { + int M() => 1 + 2$$ + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/3944")] public void ExpressionBodiedOperator() { - Test(@"class Complex -{ - int real; int imaginary; - public static Complex operator +(Complex a, Complex b) => a.Add(b.real + 1); - $$ - private Complex Add(int b) => null; -}", @"class Complex -{ - int real; int imaginary; - public static Complex operator +(Complex a, Complex b) => a.Add(b.real + 1)$$ - private Complex Add(int b) => null; -}"); + Test(""" + class Complex + { + int real; int imaginary; + public static Complex operator +(Complex a, Complex b) => a.Add(b.real + 1); + $$ + private Complex Add(int b) => null; + } + """, """ + class Complex + { + int real; int imaginary; + public static Complex operator +(Complex a, Complex b) => a.Add(b.real + 1)$$ + private Complex Add(int b) => null; + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/3944")] public void ExpressionBodiedConversionOperator() { - Test(@"using System; -public struct DBBool -{ - public static readonly DBBool dbFalse = new DBBool(-1); - int value; + Test(""" + using System; + public struct DBBool + { + public static readonly DBBool dbFalse = new DBBool(-1); + int value; - DBBool(int value) - { - this.value = value; - } + DBBool(int value) + { + this.value = value; + } - public static implicit operator DBBool(bool x) => x ? new DBBool(1) : dbFalse; - $$ -}", @"using System; -public struct DBBool -{ - public static readonly DBBool dbFalse = new DBBool(-1); - int value; + public static implicit operator DBBool(bool x) => x ? new DBBool(1) : dbFalse; + $$ + } + """, """ + using System; + public struct DBBool + { + public static readonly DBBool dbFalse = new DBBool(-1); + int value; - DBBool(int value) - { - this.value = value; - } + DBBool(int value) + { + this.value = value; + } - public static implicit operator DBBool(bool x) => x ? new DBBool(1) : dbFalse$$ -}"); + public static implicit operator DBBool(bool x) => x ? new DBBool(1) : dbFalse$$ + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/3944")] public void ExpressionBodiedProperty() { - Test(@"class T -{ - int P1 => 1 + 2; - $$ -}", @"class T -{ - int P1 => 1 + 2$$ -}"); + Test(""" + class T + { + int P1 => 1 + 2; + $$ + } + """, """ + class T + { + int P1 => 1 + 2$$ + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/3944")] public void ExpressionBodiedIndexer() { - Test(@"using System; -class SampleCollection -{ - private T[] arr = new T[100]; - public T this[int i] => i > 0 ? arr[i + 1] : arr[i + 2]; - $$ -}", @"using System; -class SampleCollection -{ - private T[] arr = new T[100]; - public T this[int i] => i > 0 ? arr[i + 1] : arr[i + 2]$$ -}"); + Test(""" + using System; + class SampleCollection + { + private T[] arr = new T[100]; + public T this[int i] => i > 0 ? arr[i + 1] : arr[i + 2]; + $$ + } + """, """ + using System; + class SampleCollection + { + private T[] arr = new T[100]; + public T this[int i] => i > 0 ? arr[i + 1] : arr[i + 2]$$ + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/3944")] public void ExpressionBodiedMethodWithBlockBodiedAnonymousMethodExpression() { - Test(@"using System; -class TestClass -{ - Func Y() => delegate (int x) - { - return 9; - }; - $$ -}", @"using System; -class TestClass -{ - Func Y() => delegate (int x) - { - return 9; - }$$ -}"); + Test(""" + using System; + class TestClass + { + Func Y() => delegate (int x) + { + return 9; + }; + $$ + } + """, """ + using System; + class TestClass + { + Func Y() => delegate (int x) + { + return 9; + }$$ + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/3944")] public void ExpressionBodiedMethodWithSingleLineBlockBodiedAnonymousMethodExpression() { - Test(@"using System; -class TestClass -{ - Func Y() => delegate (int x) { return 9; }; - $$ -}", @"using System; -class TestClass -{ - Func Y() => delegate (int x) { return 9; }$$ -}"); + Test(""" + using System; + class TestClass + { + Func Y() => delegate (int x) { return 9; }; + $$ + } + """, """ + using System; + class TestClass + { + Func Y() => delegate (int x) { return 9; }$$ + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/3944")] public void ExpressionBodiedMethodWithBlockBodiedSimpleLambdaExpression() { - Test(@"using System; -class TestClass -{ - Func Y() => f => - { - return f * 9; - }; - $$ -}", @"using System; -class TestClass -{ - Func Y() => f => - { - return f * 9; - }$$ -}"); + Test(""" + using System; + class TestClass + { + Func Y() => f => + { + return f * 9; + }; + $$ + } + """, """ + using System; + class TestClass + { + Func Y() => f => + { + return f * 9; + }$$ + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/3944")] public void ExpressionBodiedMethodWithExpressionBodiedSimpleLambdaExpression() { - Test(@"using System; -class TestClass -{ - Func Y() => f => f * 9; - $$ -}", @"using System; -class TestClass -{ - Func Y() => f => f * 9$$ -}"); - } + Test(""" + using System; + class TestClass + { + Func Y() => f => f * 9; + $$ + } + """, """ + using System; + class TestClass + { + Func Y() => f => f * 9$$ + } + """); + } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/3944")] public void ExpressionBodiedMethodWithBlockBodiedAnonymousMethodExpressionInMethodArgs() { - Test(@"using System; -class TestClass -{ - public int Prop => Method1(delegate () - { - return 8; - }); - $$ + Test(""" + using System; + class TestClass + { + public int Prop => Method1(delegate () + { + return 8; + }); + $$ - private int Method1(Func p) => null; -}", @"using System; -class TestClass -{ - public int Prop => Method1(delegate() - { - return 8; - })$$ + private int Method1(Func p) => null; + } + """, """ + using System; + class TestClass + { + public int Prop => Method1(delegate() + { + return 8; + })$$ - private int Method1(Func p) => null; -}"); + private int Method1(Func p) => null; + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/3944")] public void Format_SimpleExpressionBodiedMember() { - Test(@"class T -{ - int M() => 1 + 2; - $$ -}", @"class T -{ - int M() => 1 + 2$$ -}"); + Test(""" + class T + { + int M() => 1 + 2; + $$ + } + """, """ + class T + { + int M() => 1 + 2$$ + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/3944")] public void Format_ExpressionBodiedMemberWithSingleLineBlock() { - Test(@"using System; -class TestClass -{ - Func Y() => delegate (int x) { return 9; }; - $$ -}", @"using System; -class TestClass -{ - Func Y () => delegate(int x) { return 9 ; }$$ -}"); + Test(""" + using System; + class TestClass + { + Func Y() => delegate (int x) { return 9; }; + $$ + } + """, """ + using System; + class TestClass + { + Func Y () => delegate(int x) { return 9 ; }$$ + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/3944")] public void Format_ExpressionBodiedMemberWithMultiLineBlock() { - Test(@"using System; -class TestClass -{ - Func Y() => delegate (int x) - { - return 9; - }; - $$ -}", @"using System; -class TestClass -{ - Func Y() => delegate(int x) - { - return 9; - }$$ -}"); + Test(""" + using System; + class TestClass + { + Func Y() => delegate (int x) + { + return 9; + }; + $$ + } + """, """ + using System; + class TestClass + { + Func Y() => delegate(int x) + { + return 9; + }$$ + } + """); } [WpfFact] public void Format_Statement() { - Test(@"class C -{ - void Method() - { - int i = 1; - $$ - } -}", @"class C -{ - void Method() - { - int i = 1 $$ - } -}"); + Test(""" + class C + { + void Method() + { + int i = 1; + $$ + } + } + """, """ + class C + { + void Method() + { + int i = 1 $$ + } + } + """); } [WpfFact] public void Format_Using() { - Test(@"using System.Linq; -$$", @" using System . Linq $$"); + Test(""" + using System.Linq; + $$ + """, @" using System . Linq $$"); } [WpfFact] public void Format_Using2() { - Test(@"using - System.Linq; -$$", @" using - System . Linq $$"); + Test(""" + using + System.Linq; + $$ + """, """ + using + System . Linq $$ + """); } [WpfFact] public void Format_Field() { - Test(@"class C -{ - int i = 1; - $$ -}", @"class C -{ - int i = 1 $$ -}"); + Test(""" + class C + { + int i = 1; + $$ + } + """, """ + class C + { + int i = 1 $$ + } + """); } [WpfFact] public void Statement_Trivia() { - Test(@"class C -{ - void goo() - { - goo(); //comment - $$ - } -}", @"class C -{ - void goo() - { - goo()$$ //comment - } -}"); + Test(""" + class C + { + void goo() + { + goo(); //comment + $$ + } + } + """, """ + class C + { + void goo() + { + goo()$$ //comment + } + } + """); } [WpfFact] public void TrailingText_Negative() { - Test(@"class C -{ - event System.EventHandler e = null int i = 2; - $$ -}", @"class C -{ - event System.EventHandler e = null$$ int i = 2; -}"); + Test(""" + class C + { + event System.EventHandler e = null int i = 2; + $$ + } + """, """ + class C + { + event System.EventHandler e = null$$ int i = 2; + } + """); } [WpfFact] public void CompletionSetUp() { - Test(@"class Program -{ - object goo(object o) - { - return goo(); - $$ - } -}", @"class Program -{ - object goo(object o) - { - return goo($$) - } -}", completionActive: true); + Test(""" + class Program + { + object goo(object o) + { + return goo(); + $$ + } + } + """, """ + class Program + { + object goo(object o) + { + return goo($$) + } + } + """, completionActive: true); } [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530352")] public void EmbeddedStatement3() { - Test(@"class Program -{ - void Method() - { - foreach (var x in y) - { - $$ - } - } -}", @"class Program -{ - void Method() - { - foreach (var x in y$$) - } -}"); + Test(""" + class Program + { + void Method() + { + foreach (var x in y) + { + $$ + } + } + } + """, """ + class Program + { + void Method() + { + foreach (var x in y$$) + } + } + """); } [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530352")] public void EmbeddedStatement4() { - Test(@"class Program -{ - void Method() - { - foreach (var (x, y) in z) - { - $$ - } - } -}", @"class Program -{ - void Method() - { - foreach (var (x, y) in z$$) - } -}"); + Test(""" + class Program + { + void Method() + { + foreach (var (x, y) in z) + { + $$ + } + } + } + """, """ + class Program + { + void Method() + { + foreach (var (x, y) in z$$) + } + } + """); } [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530716")] public void DoNotAssertOnMultilineToken() { - Test(@"interface I -{ - void M(string s = @"""""" -$$ -}", @"interface I -{ - void M(string s = @""""""$$ -}"); + Test("""" + interface I + { + void M(string s = @""" + $$ + } + """", """" + interface I + { + void M(string s = @"""$$ + } + """"); } [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530718")] public void AutomaticLineFormat() { - Test(@"class C -{ - public string P { set; get; } - $$ -}", @"class C -{ - public string P {set;get;$$} -}"); + Test(""" + class C + { + public string P { set; get; } + $$ + } + """, """ + class C + { + public string P {set;get;$$} + } + """); } [WpfFact] public void NotAfterExisitingSemicolon() { - Test(@"class TestClass -{ - private int i; - $$ -}", @"class TestClass -{ - private int i;$$ -}"); + Test(""" + class TestClass + { + private int i; + $$ + } + """, """ + class TestClass + { + private int i;$$ + } + """); } [WpfFact] public void NotAfterCloseBraceInMethod() { - Test(@"class TestClass -{ - void Test() { } - $$ -}", @"class TestClass -{ - void Test() { }$$ -}"); + Test(""" + class TestClass + { + void Test() { } + $$ + } + """, """ + class TestClass + { + void Test() { }$$ + } + """); } [WpfFact] public void NotAfterCloseBraceInStatement() { - Test(@"class TestClass -{ - void Test() - { - if (true) { } - $$ - } -}", @"class TestClass -{ - void Test() - { - if (true) { }$$ - } -}"); + Test(""" + class TestClass + { + void Test() + { + if (true) { } + $$ + } + } + """, """ + class TestClass + { + void Test() + { + if (true) { }$$ + } + } + """); } [WpfFact] public void NotAfterAutoPropertyAccessor() { - Test(@"class TestClass -{ - public int A { get; set } - $$ -}", @"class TestClass -{ - public int A { get; set$$ } -}"); + Test(""" + class TestClass + { + public int A { get; set } + $$ + } + """, """ + class TestClass + { + public int A { get; set$$ } + } + """); } [WpfFact] public void NotAfterAutoPropertyDeclaration() { - Test(@"class TestClass -{ - public int A { get; set; } - $$ -}", @"class TestClass -{ - public int A { get; set; }$$ -}"); + Test(""" + class TestClass + { + public int A { get; set; } + $$ + } + """, """ + class TestClass + { + public int A { get; set; }$$ + } + """); } [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/150480")] public void DelegatedInEmptyBlock() { - Test(@"class TestClass -{ - void Method() - { - try { $$} - } -}", @"class TestClass -{ - void Method() - { - try { $$} - } -}", assertNextHandlerInvoked: true); + Test(""" + class TestClass + { + void Method() + { + try { $$} + } + } + """, """ + class TestClass + { + void Method() + { + try { $$} + } + } + """, assertNextHandlerInvoked: true); } [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/150480")] public void DelegatedInEmptyBlock2() { - Test(@"class TestClass -{ - void Method() - { - if (true) { $$} - } -}", @"class TestClass -{ - void Method() - { - if (true) { $$} - } -}", assertNextHandlerInvoked: true); + Test(""" + class TestClass + { + void Method() + { + if (true) { $$} + } + } + """, """ + class TestClass + { + void Method() + { + if (true) { $$} + } + } + """, assertNextHandlerInvoked: true); } [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/150480")] public void NotDelegatedOutsideEmptyBlock() { - Test(@"class TestClass -{ - void Method() - { - try { } - $$ - } -}", @"class TestClass -{ - void Method() - { - try { }$$ - } -}"); + Test(""" + class TestClass + { + void Method() + { + try { } + $$ + } + } + """, """ + class TestClass + { + void Method() + { + try { }$$ + } + } + """); } [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/150480")] public void NotDelegatedAfterOpenBraceAndMissingCloseBrace() { - Test(@"class TestClass -{ - void Method() - { - try { - $$ - } -}", @"class TestClass -{ - void Method() - { - try {$$ - } -}"); + Test(""" + class TestClass + { + void Method() + { + try { + $$ + } + } + """, """ + class TestClass + { + void Method() + { + try {$$ + } + } + """); } [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/150480")] public void NotDelegatedInNonEmptyBlock() { - Test(@"class TestClass -{ - void Method() - { - try { x } - $$ - } -}", @"class TestClass -{ - void Method() - { - try { x$$ } - } -}"); + Test(""" + class TestClass + { + void Method() + { + try { x } + $$ + } + } + """, """ + class TestClass + { + void Method() + { + try { x$$ } + } + } + """); } [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/150480")] public void NotDelegatedAfterOpenBraceInAnonymousObjectCreationExpression() { - Test(@"class TestClass -{ - void Method() - { - var pet = new { }; - $$ - } -}", @"class TestClass -{ - void Method() - { - var pet = new { $$} - } -}"); + Test(""" + class TestClass + { + void Method() + { + var pet = new { }; + $$ + } + } + """, """ + class TestClass + { + void Method() + { + var pet = new { $$} + } + } + """); } [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/150480")] public void NotDelegatedAfterOpenBraceObjectCreationExpression() { - Test(@"class TestClass -{ - void Method() - { - var pet = new List(); - $$ - } -}", @"class TestClass -{ - void Method() - { - var pet = new List { $$} - } -}"); + Test(""" + class TestClass + { + void Method() + { + var pet = new List(); + $$ + } + } + """, """ + class TestClass + { + void Method() + { + var pet = new List { $$} + } + } + """); } [WpfFact] public void TestMulitpleNamespace() { - Test($@" -namespace Bar2 -{{ - $$ -}} -namespace Bar -{{ -}}", $@" -namespace B$$ar2$$ -namespace Bar -{{ -}}"); + Test($$""" + namespace Bar2 + { + $$ + } + namespace Bar + { + } + """, $$""" + namespace B$$ar2$$ + namespace Bar + { + } + """); } [WpfTheory] @@ -902,12 +1105,14 @@ namespace Bar [InlineData("interface")] public void TestEmptyBaseTypeDeclarationAndNamespace(string typeKeyword) { - Test($@" -public {typeKeyword} Bar -{{ - $$ -}}", $@" -pu$$blic {typeKeyword} $$Bar$$"); + Test($$""" + public {{typeKeyword}} Bar + { + $$ + } + """, $""" + pu$$blic {typeKeyword} $$Bar$$ + """); } [WpfTheory] @@ -918,55 +1123,61 @@ public void TestEmptyBaseTypeDeclarationAndNamespace(string typeKeyword) [InlineData("interface")] public void TestMultipleBaseTypeDeclaration(string typeKeyword) { - Test($@" -public {typeKeyword} Bar2 -{{ - $$ -}} + Test($$""" + public {{typeKeyword}} Bar2 + { + $$ + } -public {typeKeyword} Bar -{{ -}}", $@" -pub$$lic {typeKeyword} B$$ar2$$ -public {typeKeyword} Bar -{{ -}}"); + public {{typeKeyword}} Bar + { + } + """, $$""" + pub$$lic {{typeKeyword}} B$$ar2$$ + public {{typeKeyword}} Bar + { + } + """); } [WpfFact] public void TestNestedTypeDeclaration() { - Test(@" -public class Bar1 -{ - public class Bar2 - { - $$ - } -}", -@" -public class Bar1 -{ - pu$$blic cla$$ss B$$ar2$$ -}"); + Test(""" + public class Bar1 + { + public class Bar2 + { + $$ + } + } + """, + """ + public class Bar1 + { + pu$$blic cla$$ss B$$ar2$$ + } + """); } [WpfFact] public void TestNestedNamespace() { - Test(@" -namespace Bar1 -{ - namespace Bar2 - { - $$ - } -}", -@" -namespace Bar1 -{ - namespa$$ce $$B$$ar2$$ -}"); + Test(""" + namespace Bar1 + { + namespace Bar2 + { + $$ + } + } + """, + """ + namespace Bar1 + { + namespa$$ce $$B$$ar2$$ + } + """); } [WpfTheory] @@ -978,10 +1189,12 @@ namespace Bar1 [InlineData("interface")] public void TestBaseTypeDeclarationAndNamespaceWithOpenBrace(string typeKeyword) { - Test($@" -public {typeKeyword} Bar {{ - $$", $@" -pub$$lic {typeKeyword} B$$ar {{$$"); + Test($$""" + public {{typeKeyword}} Bar { + $$ + """, $$""" + pub$$lic {{typeKeyword}} B$$ar {$$ + """); } [WpfTheory] @@ -993,210 +1206,233 @@ public void TestBaseTypeDeclarationAndNamespaceWithOpenBrace(string typeKeyword) [InlineData("interface")] public void TestValidTypeDeclarationAndNamespace(string typeKeyword) { - Test($@"public {typeKeyword} Bar {{}} -$$", + Test($$""" + public {{typeKeyword}} Bar {} + $$ + """, $@"public {typeKeyword}$$ Ba$$r {{}}$$"); } [WpfFact] public void TestMethod() { - Test(@" -public class Bar -{ - void Main() - { - $$ - } -}", @" -public class Bar -{ - v$$oid Ma$$in($$)$$ -}"); + Test(""" + public class Bar + { + void Main() + { + $$ + } + } + """, """ + public class Bar + { + v$$oid Ma$$in($$)$$ + } + """); } [WpfFact] public void TestConstructor() { - Test(@" -public class Bar -{ - void Bar() - { - $$ - } -}", @" -public class Bar -{ - v$$oid Ba$$r($$)$$ -}"); + Test(""" + public class Bar + { + void Bar() + { + $$ + } + } + """, """ + public class Bar + { + v$$oid Ba$$r($$)$$ + } + """); } [WpfFact] public void TestValidMethodInInterface() { - Test(@" -public interface Bar -{ - void Main(); - $$ -}", @" -public interface Bar -{ - v$$oid Mai$$n($$)$$; -}"); + Test(""" + public interface Bar + { + void Main(); + $$ + } + """, """ + public interface Bar + { + v$$oid Mai$$n($$)$$; + } + """); } [WpfFact] public void TestMissingSemicolonMethodInInterface() { - Test(@" -public interface Bar -{ - void Main() - $$ -}", @" -public interface Bar -{ - v$$oid Mai$$n($$)$$ -}"); + Test(""" + public interface Bar + { + void Main() + $$ + } + """, """ + public interface Bar + { + v$$oid Mai$$n($$)$$ + } + """); } [WpfFact] public void TestValidLocalFunction() { - Test(@" -public class Bar -{ - void Main() - { - void Local() - $$ - { - } - } -}", @" -public class Bar -{ - void Main() - { - v$$oid Loc$$al($$)$$ - { - } - } -}"); + Test(""" + public class Bar + { + void Main() + { + void Local() + $$ + { + } + } + } + """, """ + public class Bar + { + void Main() + { + v$$oid Loc$$al($$)$$ + { + } + } + } + """); } [WpfFact] public void TestLocalFunction() { - Test(@" -public class Bar -{ - void Main() - { - void Local() - { - $$ - } - } -}", @" -public class Bar -{ - void Main() - { - v$$oid Loca$$l($$)$$ - } -}"); + Test(""" + public class Bar + { + void Main() + { + void Local() + { + $$ + } + } + } + """, """ + public class Bar + { + void Main() + { + v$$oid Loca$$l($$)$$ + } + } + """); } [WpfFact] public void TestIndexerAsLastElementInClass() { - Test(@" -public class Bar -{ - public int this[int i] - { - $$ - } -}", @" -public class Bar -{ - p$$ublic in$$t thi$$s[in$$t i]$$ -}"); + Test(""" + public class Bar + { + public int this[int i] + { + $$ + } + } + """, """ + public class Bar + { + p$$ublic in$$t thi$$s[in$$t i]$$ + } + """); } [WpfFact] public void TestIndexerNotAsLastElementInClass() { - Test(@" -public class Bar -{ - public int this[int i] - { - $$ - } - void Main() {} -}", @" -public class Bar -{ - p$$ublic in$$t thi$$s[in$$t i]$$ - void Main() {} -}"); + Test(""" + public class Bar + { + public int this[int i] + { + $$ + } + void Main() {} + } + """, """ + public class Bar + { + p$$ublic in$$t thi$$s[in$$t i]$$ + void Main() {} + } + """); } [WpfFact] public void TestValidIndexer() { - Test(@" -public class Bar -{ - public int this[int i] - $$ - { - } -}", @" -public class Bar -{ - p$$ublic i$$nt thi$$s[in$$t i]$$ - { - } -}"); + Test(""" + public class Bar + { + public int this[int i] + $$ + { + } + } + """, """ + public class Bar + { + p$$ublic i$$nt thi$$s[in$$t i]$$ + { + } + } + """); } [WpfFact] public void TestGetAccessorOfProperty() { - var initialMarkup = @" -public class Bar -{ - public int P - { - ge$$t$$ - } -}"; + var initialMarkup = """ + public class Bar + { + public int P + { + ge$$t$$ + } + } + """; - var firstResult = @" -public class Bar -{ - public int P - { - get - { - $$ - } - } -}"; - var secondResult = @" -public class Bar -{ - public int P - { - get; - $$ - } -}"; + var firstResult = """ + public class Bar + { + public int P + { + get + { + $$ + } + } + } + """; + var secondResult = """ + public class Bar + { + public int P + { + get; + $$ + } + } + """; Test(firstResult, initialMarkup); Test(secondResult, firstResult); } @@ -1204,34 +1440,37 @@ public int P [WpfFact] public void TestSetAccessorOfProperty() { - var initialMarkup = @" -public class Bar -{ - public int P - { - set$$ - } -}"; - var firstResult = @" -public class Bar -{ - public int P - { - set - { - $$ - } - } -}"; - var secondResult = @" -public class Bar -{ - public int P - { - set; - $$ - } -}"; + var initialMarkup = """ + public class Bar + { + public int P + { + set$$ + } + } + """; + var firstResult = """ + public class Bar + { + public int P + { + set + { + $$ + } + } + } + """; + var secondResult = """ + public class Bar + { + public int P + { + set; + $$ + } + } + """; Test(firstResult, initialMarkup); Test(secondResult, firstResult); } @@ -1239,313 +1478,336 @@ public int P [WpfFact] public void TestGetAccessorOfIndexer() { - Test(@" -public class Bar -{ - public int this[int i] - { - get - { - $$ - } - } -}", @" -public class Bar -{ - public int this[int i] - { - ge$$t$$ - } -}"); + Test(""" + public class Bar + { + public int this[int i] + { + get + { + $$ + } + } + } + """, """ + public class Bar + { + public int this[int i] + { + ge$$t$$ + } + } + """); } [WpfFact] public void TestValidGetAccessorOfIndexer() { - Test(@" -public class Bar -{ - public int this[int i] - { - get - { + Test(""" + public class Bar + { + public int this[int i] + { + get + { - $$ - } - } -}", @" -public class Bar -{ - public int this[int i] - { - get - { - $$ - } - } -}"); + $$ + } + } + } + """, """ + public class Bar + { + public int this[int i] + { + get + { + $$ + } + } + } + """); } [WpfFact] public void TestNonEmptyGetAccessor() { - Test(@" -public Class Bar -{ - public int P - { - get - { - if (true) - $$ + Test(""" + public Class Bar { - return 1; + public int P + { + get + { + if (true) + $$ + { + return 1; + } + } + } } - } - } -}", - @" -public Class Bar -{ - public int P - { - get - { - i$$f ($$true$$)$$ + """, + """ + public Class Bar { - return 1; + public int P + { + get + { + i$$f ($$true$$)$$ + { + return 1; + } + } + } } - } - } -}"); + """); } [WpfFact] public void TestNonEmptySetAccessor() { - Test(@" -public Class Bar -{ - public int P - { - get; - set - { - if (true) - $$ + Test(""" + public Class Bar { + public int P + { + get; + set + { + if (true) + $$ + { + } + } + } } - } - } -}", - @" -public Class Bar -{ - public int P - { - get; - set - { - i$$f (t$$rue)$$ + """, + """ + public Class Bar { + public int P + { + get; + set + { + i$$f (t$$rue)$$ + { + } + } + } } - } - } -}"); + """); } [WpfFact] public void TestSetAccessorOfIndexer() { - Test(@" -public class Bar -{ - public int this[int i] - { - get; - set - { - $$ - } - } -}", @" -public class Bar -{ - public int this[int i] - { - get; - se$$t$$ - } -}"); + Test(""" + public class Bar + { + public int this[int i] + { + get; + set + { + $$ + } + } + } + """, """ + public class Bar + { + public int this[int i] + { + get; + se$$t$$ + } + } + """); } [WpfFact] public void TestValidSetAccessorOfIndexer() { - Test(@" -public class Bar -{ - public int this[int i] - { - get; - set - { + Test(""" + public class Bar + { + public int this[int i] + { + get; + set + { - $$ - } - } -}", @" -public class Bar -{ - public int this[int i] - { - get; - set - { - $$ - } - } -}"); + $$ + } + } + } + """, """ + public class Bar + { + public int this[int i] + { + get; + set + { + $$ + } + } + } + """); } [WpfFact] public void TestAddAccessorInEventDeclaration() { - Test(@" -using System; -public class Bar -{ - public event EventHandler e - { - add - { - $$ - } - remove - } -}", @" -using System; -public class Bar -{ - public event EventHandler e - { - ad$$d$$ - remove - } -}"); + Test(""" + using System; + public class Bar + { + public event EventHandler e + { + add + { + $$ + } + remove + } + } + """, """ + using System; + public class Bar + { + public event EventHandler e + { + ad$$d$$ + remove + } + } + """); } [WpfFact] public void TestValidAddAccessorInEventDeclaration() { - Test(@" -using System; -public class Bar -{ - public event EventHandler e - { - add - { + Test(""" + using System; + public class Bar + { + public event EventHandler e + { + add + { - $$ - } - remove { } - } -}", @" -using System; -public class Bar -{ - public event EventHandler e - { - add - { - $$ - } - remove { } - } -}"); + $$ + } + remove { } + } + } + """, """ + using System; + public class Bar + { + public event EventHandler e + { + add + { + $$ + } + remove { } + } + } + """); } [WpfFact] public void TestRemoveAccessor() { - Test(@" -using System; -public class Bar -{ - public event EventHandler e - { - add - remove - { - $$ - } - } -}", @" -using System; -public class Bar -{ - public event EventHandler e - { - add - remo$$ve$$ - } -}"); + Test(""" + using System; + public class Bar + { + public event EventHandler e + { + add + remove + { + $$ + } + } + } + """, """ + using System; + public class Bar + { + public event EventHandler e + { + add + remo$$ve$$ + } + } + """); } [WpfFact] public void TestValidRemoveAccessor() { - Test(@" -using System; -public class Bar -{ - public event EventHandler e - { - add { } - remove - { - - $$ - } - } -}", @" -using System; -public class Bar -{ - public event EventHandler e - { - add { } - remove - { - $$ - } - } -}"); + Test(""" + using System; + public class Bar + { + public event EventHandler e + { + add { } + remove + { + + $$ + } + } + } + """, """ + using System; + public class Bar + { + public event EventHandler e + { + add { } + remove + { + $$ + } + } + } + """); } [WpfFact] public void TestField() { - var initialMarkup = @" -public class Bar -{ - p$$ublic i$$nt i$$ii$$ -}"; - var firstResult = @" -public class Bar -{ - public int iii - { - $$ - } -}"; - var secondResult = @" -public class Bar -{ - public int iii; - $$ -}"; + var initialMarkup = """ + public class Bar + { + p$$ublic i$$nt i$$ii$$ + } + """; + var firstResult = """ + public class Bar + { + public int iii + { + $$ + } + } + """; + var secondResult = """ + public class Bar + { + public int iii; + $$ + } + """; Test(firstResult, initialMarkup); Test(secondResult, firstResult); @@ -1554,96 +1816,107 @@ public class Bar [WpfFact] public void TestReadonlyField() { - Test(@" -public class Bar -{ - public readonly int iii; - $$ -}", @" -public class Bar -{ - p$$ublic reado$$nly i$$nt i$$ii$$ -}"); + Test(""" + public class Bar + { + public readonly int iii; + $$ + } + """, """ + public class Bar + { + p$$ublic reado$$nly i$$nt i$$ii$$ + } + """); } [WpfFact] public void TestNonEmptyProperty() { - Test(@" -public class Bar -{ - public int Foo - { - get { } - $$ - } -}", @" -public class Bar -{ - public int Foo - { - $$get$$ { }$$ - } -}"); + Test(""" + public class Bar + { + public int Foo + { + get { } + $$ + } + } + """, """ + public class Bar + { + public int Foo + { + $$get$$ { }$$ + } + } + """); } [WpfFact] public void TestMulitpleFields() { - Test(@" -public class Bar -{ - public int apple, banana; - $$ -}", @" -public class Bar -{ - p$$ublic i$$nt ap$$ple$$, ba$$nana;$$ -}"); + Test(""" + public class Bar + { + public int apple, banana; + $$ + } + """, """ + public class Bar + { + p$$ublic i$$nt ap$$ple$$, ba$$nana;$$ + } + """); } [WpfFact] public void TestMultipleEvents() { - Test(@" -using System; -public class Bar -{ - public event EventHandler apple, banana; - $$ -}", @" -using System; -public class Bar -{ - p$$ublic event EventHandler ap$$ple$$, ba$$nana$$;$$ -}"); + Test(""" + using System; + public class Bar + { + public event EventHandler apple, banana; + $$ + } + """, """ + using System; + public class Bar + { + p$$ublic event EventHandler ap$$ple$$, ba$$nana$$;$$ + } + """); } [WpfFact] public void TestEvent() { - var initialMarkup = @" -using System; -public class Bar -{ - pu$$blic e$$vent EventHand$$ler c$$c$$ -}"; - var firstResult = @" -using System; -public class Bar -{ - public event EventHandler cc - { - $$ - } -}"; - var secondResult = @" -using System; -public class Bar -{ - public event EventHandler cc; - $$ -}"; + var initialMarkup = """ + using System; + public class Bar + { + pu$$blic e$$vent EventHand$$ler c$$c$$ + } + """; + var firstResult = """ + using System; + public class Bar + { + public event EventHandler cc + { + $$ + } + } + """; + var secondResult = """ + using System; + public class Bar + { + public event EventHandler cc; + $$ + } + """; Test(firstResult, initialMarkup); Test(secondResult, firstResult); } @@ -1651,74 +1924,79 @@ public class Bar [WpfFact] public void TestNonEmptyEvent() { - Test(@" -using System; -public class Bar -{ - public event EventHandler Foo - { - add { } - $$ - } -}", @" -using System; -public class Bar -{ - public event EventHandler Foo - { - $$add$$ {$$ }$$ - } -}"); + Test(""" + using System; + public class Bar + { + public event EventHandler Foo + { + add { } + $$ + } + } + """, """ + using System; + public class Bar + { + public event EventHandler Foo + { + $$add$$ {$$ }$$ + } + } + """); } [WpfFact] public void TestObjectCreationExpressionWithParenthesis() { - var initialMarkup = @" -public class Bar -{ - public void M() - { - var f = n$$ew F$$oo($$)$$ - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + var initialMarkup = """ + public class Bar + { + public void M() + { + var f = n$$ew F$$oo($$)$$ + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; - var firstResult = @" -public class Bar -{ - public void M() - { - var f = new Foo() - { - $$ - }; - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + var firstResult = """ + public class Bar + { + public void M() + { + var f = new Foo() + { + $$ + }; + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; - var secondResult = @" -public class Bar -{ - public void M() - { - var f = new Foo(); - $$ - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + var secondResult = """ + public class Bar + { + public void M() + { + var f = new Foo(); + $$ + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; Test(firstResult, initialMarkup); Test(secondResult, firstResult); @@ -1727,51 +2005,54 @@ public class Foo [WpfFact] public void TestObjectCreationExpressionWithNoParenthesis() { - var initialMarkUp = @" -public class Bar -{ - public void M() - { - var f = n$$ew F$$oo$$ - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + var initialMarkUp = """ + public class Bar + { + public void M() + { + var f = n$$ew F$$oo$$ + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; - var firstResult = @" -public class Bar -{ - public void M() - { - var f = new Foo() - { - $$ - }; - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + var firstResult = """ + public class Bar + { + public void M() + { + var f = new Foo() + { + $$ + }; + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; - var secondResult = @" -public class Bar -{ - public void M() - { - var f = new Foo(); - $$ - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + var secondResult = """ + public class Bar + { + public void M() + { + var f = new Foo(); + $$ + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; Test(firstResult, initialMarkUp); Test(secondResult, firstResult); @@ -1780,116 +2061,122 @@ public class Foo [WpfFact] public void TestObjectCreationExpressionWithCorrectSemicolon() { - var initialMarkUp = @" -public class Bar -{ - public void M() - { - var f = n$$ew F$$oo$$; - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; - - var firstResult = @" -public class Bar -{ - public void M() - { - var f = new Foo() - { - $$ - }; - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + var initialMarkUp = """ + public class Bar + { + public void M() + { + var f = n$$ew F$$oo$$; + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; - var secondResult = @" -public class Bar -{ - public void M() - { - var f = new Foo(); - $$ - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + var firstResult = """ + public class Bar + { + public void M() + { + var f = new Foo() + { + $$ + }; + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; - Test(firstResult, initialMarkUp); - Test(secondResult, firstResult); - } + var secondResult = """ + public class Bar + { + public void M() + { + var f = new Foo(); + $$ + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; + + Test(firstResult, initialMarkUp); + Test(secondResult, firstResult); + } [WpfFact] public void TestObjectCreationExpressionUsedAsExpression() { - var initialMarkUp = @" -public class Bar -{ - public void M() - { - N(ne$$w Fo$$o$$); - } + var initialMarkUp = """ + public class Bar + { + public void M() + { + N(ne$$w Fo$$o$$); + } - private void N(Foo f) - { - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + private void N(Foo f) + { + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; - var firstResult = @" -public class Bar -{ - public void M() - { - N(new Foo() - { - $$ - }); - } + var firstResult = """ + public class Bar + { + public void M() + { + N(new Foo() + { + $$ + }); + } - private void N(Foo f) - { - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + private void N(Foo f) + { + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; - var secondResult = @" -public class Bar -{ - public void M() - { - N(new Foo()); - $$ - } + var secondResult = """ + public class Bar + { + public void M() + { + N(new Foo()); + $$ + } - private void N(Foo f) - { - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + private void N(Foo f) + { + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; Test(firstResult, initialMarkUp); Test(secondResult, firstResult); @@ -1898,51 +2185,54 @@ public class Foo [WpfFact] public void TestObjectCreationExpressionInUsingStatement() { - var initialMarkup = @" -public class Bar -{ - public void M() - { - using(var a = n$$ew F$$oo($$)$$) - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + var initialMarkup = """ + public class Bar + { + public void M() + { + using(var a = n$$ew F$$oo($$)$$) + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; - var firstResult = @" -public class Bar -{ - public void M() - { - using(var a = new Foo() - { - $$ - }) - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + var firstResult = """ + public class Bar + { + public void M() + { + using(var a = new Foo() + { + $$ + }) + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; - var secondResult = @" -public class Bar -{ - public void M() - { - using(var a = new Foo()) - $$ - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + var secondResult = """ + public class Bar + { + public void M() + { + using(var a = new Foo()) + $$ + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; Test(firstResult, initialMarkup); Test(secondResult, firstResult); @@ -1952,33 +2242,35 @@ public class Foo public void TestObjectCreationExpressionWithNonEmptyInitializer() { Test( - @" -public class Bar -{ - public void M() - { - var a = new Foo() { HH = 1, PP = 2 }; - $$ - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}", - @" -public class Bar -{ - public void M() - { - var a = n$$ew Fo$$o($$) {$$ HH = 1$$, PP = 2 $$}; - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"); + """ + public class Bar + { + public void M() + { + var a = new Foo() { HH = 1, PP = 2 }; + $$ + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """, + """ + public class Bar + { + public void M() + { + var a = n$$ew Fo$$o($$) {$$ HH = 1$$, PP = 2 $$}; + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """); } @@ -2093,779 +2385,833 @@ public void M() [WpfFact] public void TestIfStatementWithInnerStatement() { - Test(@" -public class Bar -{ - public void Main(bool x) - { - if (x) - { - $$ - } - var z = 1; - } -}", @" -public class Bar -{ - public void Main(bool x) - { - i$$f$$ ($$x)$$ - var z = 1; - } -}"); - } - - [WpfFact] - public void TestIfStatementWithFollowingElseClause() - { - Test(@" -public class Bar -{ - public void Main(bool x) - { - if (x) - { - $$ - var z = 1; - } - else if (!x) - } -}", @" -public class Bar -{ - public void Main(bool x) - { - i$$f$$ ($$x)$$ - var z = 1; - else if (!x) - } -}"); - } - - [WpfFact] - public void TestIfStatementWithoutStatement() - { - Test(@" -public class Bar -{ - public void Main(bool x) - { - if (x) - { - $$ - } - } -}", @" -public class Bar -{ - public void Main(bool x) - { - i$$f$$ ($$x)$$ - } -}"); - } - - [WpfFact] - public void TestNestIfStatementWithInnerStatement() - { - Test(@" -public class Bar -{ - public void Main(int x) - { - if (x == 1) - if (x == 2) - if (x == 3) - if (x == 4) + Test(""" + public class Bar + { + public void Main(bool x) + { + if (x) { $$ - var a = 1000; } - } -}", @" -public class Bar -{ - public void Main(int x) - { - if (x == 1) - if (x == 2) - if (x == 3) - i$$f ($$x =$$= 4)$$ - var a = 1000; - } -}"); + var z = 1; + } + } + """, """ + public class Bar + { + public void Main(bool x) + { + i$$f$$ ($$x)$$ + var z = 1; + } + } + """); } [WpfFact] - public void TestNestIfStatementWithoutInnerStatement() - { - Test(@" -public class Bar -{ - public void Main(int x) + public void TestIfStatementWithFollowingElseClause() { - if (x == 1) - if (x == 2) - if (x == 3) - if (x == 4) + Test(""" + public class Bar + { + public void Main(bool x) + { + if (x) { $$ + var z = 1; } - } -}", @" -public class Bar -{ - public void Main(int x) - { - if (x == 1) - if (x == 2) - if (x == 3) - i$$f ($$x =$$= 4)$$ - } -}"); - } - - [WpfFact] - public void TestNestedElseIfStatementWithInnerStatement() - { - Test(@" -public class Bar -{ - public void Fo(int i) - { - if (i == 1) - { - } - else if (i == 2) - if (i == 3) - { - $$ - var i = 10; - } - else - { + else if (!x) + } } - } -}", @" -public class Bar -{ - public void Fo(int i) - { - if (i == 1) - { - } - else if (i == 2) - i$$f (i$$ == 3)$$ - var i = 10; - else + """, """ + public class Bar { + public void Main(bool x) + { + i$$f$$ ($$x)$$ + var z = 1; + else if (!x) + } } - } -}"); + """); } [WpfFact] - public void TestNestIfElseStatementWithBlockWithInnerStatement() - { - Test(@" -public class Bar -{ - public void Main(int x) + public void TestIfStatementWithoutStatement() { - if (x == 1) - if (x == 2) - if (x == 3) + Test(""" + public class Bar + { + public void Main(bool x) { - if (x == 4) + if (x) { $$ } - var i = 10; } - else + } + """, """ + public class Bar + { + public void Main(bool x) { + i$$f$$ ($$x)$$ } + } + """); } -}", @" -public class Bar -{ - public void Main(int x) + + [WpfFact] + public void TestNestIfStatementWithInnerStatement() { - if (x == 1) - if (x == 2) - if (x == 3) + Test(""" + public class Bar + { + public void Main(int x) { - i$$f ($$x =$$= 4)$$ - var i = 10; + if (x == 1) + if (x == 2) + if (x == 3) + if (x == 4) + { + $$ + var a = 1000; + } } - else + } + """, """ + public class Bar + { + public void Main(int x) { + if (x == 1) + if (x == 2) + if (x == 3) + i$$f ($$x =$$= 4)$$ + var a = 1000; } - } -}"); + } + """); } [WpfFact] - public void TestEmptyDoStatement() - { - Test(@" -public class Bar -{ - public void Main() + public void TestNestIfStatementWithoutInnerStatement() { - do - { - $$ - } + Test(""" + public class Bar + { + public void Main(int x) + { + if (x == 1) + if (x == 2) + if (x == 3) + if (x == 4) + { + $$ + } + } + } + """, """ + public class Bar + { + public void Main(int x) + { + if (x == 1) + if (x == 2) + if (x == 3) + i$$f ($$x =$$= 4)$$ + } + } + """); } -}", @" -public class Bar -{ - public void Main() + + [WpfFact] + public void TestNestedElseIfStatementWithInnerStatement() { - d$$o$$ - } -}"); + Test(""" + public class Bar + { + public void Fo(int i) + { + if (i == 1) + { + } + else if (i == 2) + if (i == 3) + { + $$ + var i = 10; + } + else + { + } + } + } + """, """ + public class Bar + { + public void Fo(int i) + { + if (i == 1) + { + } + else if (i == 2) + i$$f (i$$ == 3)$$ + var i = 10; + else + { + } + } + } + """); } [WpfFact] - public void TestDoStatementWithInnerStatement() - { - Test(@" -public class Bar -{ - public void Main() + public void TestNestIfElseStatementWithBlockWithInnerStatement() { - do - { - $$ - } - var c = 10; + Test(""" + public class Bar + { + public void Main(int x) + { + if (x == 1) + if (x == 2) + if (x == 3) + { + if (x == 4) + { + $$ + } + var i = 10; + } + else + { + } + } + } + """, """ + public class Bar + { + public void Main(int x) + { + if (x == 1) + if (x == 2) + if (x == 3) + { + i$$f ($$x =$$= 4)$$ + var i = 10; + } + else + { + } + } + } + """); } -}", @" -public class Bar -{ - public void Main() + + [WpfFact] + public void TestEmptyDoStatement() { - d$$o$$ - var c = 10; - } -}"); + Test(""" + public class Bar + { + public void Main() + { + do + { + $$ + } + } + } + """, """ + public class Bar + { + public void Main() + { + d$$o$$ + } + } + """); } [WpfFact] - public void TestDoStatementWithWhileClause() - { - Test(@" -public class Bar -{ - public void Main() + public void TestDoStatementWithInnerStatement() { - do - { - $$ - var c = 10; - } - while (true); + Test(""" + public class Bar + { + public void Main() + { + do + { + $$ + } + var c = 10; + } + } + """, """ + public class Bar + { + public void Main() + { + d$$o$$ + var c = 10; + } + } + """); } -}", @" -public class Bar -{ - public void Main() + + [WpfFact] + public void TestDoStatementWithWhileClause() { - d$$o$$ - var c = 10; - while (true); - } -}"); + Test(""" + public class Bar + { + public void Main() + { + do + { + $$ + var c = 10; + } + while (true); + } + } + """, """ + public class Bar + { + public void Main() + { + d$$o$$ + var c = 10; + while (true); + } + } + """); } [WpfFact] public void TestSingleElseStatement() { - Test(@" -public class Bar -{ - public void Fo() - { - if (true) - { - } - else - { - $$ - } - } -}", @" -public class Bar -{ - public void Fo() - { - if (true) - { - } - e$$lse$$ - } -}"); + Test(""" + public class Bar + { + public void Fo() + { + if (true) + { + } + else + { + $$ + } + } + } + """, """ + public class Bar + { + public void Fo() + { + if (true) + { + } + e$$lse$$ + } + } + """); } [WpfFact] public void TestElseStatementWithInnerStatement() { - Test(@" -public class Bar -{ - public void Fo() - { - if (true) - { - } - else - { - $$ - } - var c = 10; - } -}", @" -public class Bar -{ - public void Fo() - { - if (true) - { - } - e$$lse$$ - var c = 10; - } -}"); + Test(""" + public class Bar + { + public void Fo() + { + if (true) + { + } + else + { + $$ + } + var c = 10; + } + } + """, """ + public class Bar + { + public void Fo() + { + if (true) + { + } + e$$lse$$ + var c = 10; + } + } + """); } [WpfFact] public void TestElseIfStatement() { - Test(@" -public class Bar -{ - public void Fo() - { - if (true) - { - } - else if (false) - { - $$ - } - } -}", @" -public class Bar -{ - public void Fo() - { - if (true) - { - } - e$$lse i$$f ($$false)$$ - } -}"); + Test(""" + public class Bar + { + public void Fo() + { + if (true) + { + } + else if (false) + { + $$ + } + } + } + """, """ + public class Bar + { + public void Fo() + { + if (true) + { + } + e$$lse i$$f ($$false)$$ + } + } + """); } [WpfFact] public void TestElseIfInTheMiddleWithInnerStatement() { - Test(@" -public class Bar -{ - public void Fo() - { - if (true) - { - } - else if (false) - { - $$ - var i = 10; - } - else - { - } - } -}", @" -public class Bar -{ - public void Fo() - { - if (true) - { - } - e$$lse i$$f ($$false)$$ - var i = 10; - else - { - } - } -}"); + Test(""" + public class Bar + { + public void Fo() + { + if (true) + { + } + else if (false) + { + $$ + var i = 10; + } + else + { + } + } + } + """, """ + public class Bar + { + public void Fo() + { + if (true) + { + } + e$$lse i$$f ($$false)$$ + var i = 10; + else + { + } + } + } + """); } [WpfFact] public void TestElseClauseInNestedIfStatement() { - Test(@" -public class Bar -{ - public void Fo(int i) - { - if (i == 1) - { - if (i == 2) - var i = 10; - else + Test(""" + public class Bar { - $$ + public void Fo(int i) + { + if (i == 1) + { + if (i == 2) + var i = 10; + else + { + $$ + } + var c = 100; + } + } } - var c = 100; - } - } -}", @" -public class Bar -{ - public void Fo(int i) - { - if (i == 1) - { - if (i == 2) - var i = 10; - el$$se - var c = 100; - } - } -}"); + """, """ + public class Bar + { + public void Fo(int i) + { + if (i == 1) + { + if (i == 2) + var i = 10; + el$$se + var c = 100; + } + } + } + """); } [WpfFact] public void TestForStatementWithoutStatement() { - Test(@" -public class Bar -{ - public void Fo() - { - for (int i; i < 10; i++) - { - $$ - } - } -}", @" -public class Bar -{ - public void Fo() - { - f$$or (i$$nt i; i < 10;$$ i++)$$ - } -}"); + Test(""" + public class Bar + { + public void Fo() + { + for (int i; i < 10; i++) + { + $$ + } + } + } + """, """ + public class Bar + { + public void Fo() + { + f$$or (i$$nt i; i < 10;$$ i++)$$ + } + } + """); } [WpfFact] public void TestForStatementWithInnerStatement() { - Test(@" -public class Bar -{ - public void Fo() - { - for (int i; i < 10; i++) - { - $$ - } - var c = 10; - } -}", @" -public class Bar -{ - public void Fo() - { - f$$or (i$$nt i; i < 10;$$ i++)$$ - var c = 10; - } -}"); + Test(""" + public class Bar + { + public void Fo() + { + for (int i; i < 10; i++) + { + $$ + } + var c = 10; + } + } + """, """ + public class Bar + { + public void Fo() + { + f$$or (i$$nt i; i < 10;$$ i++)$$ + var c = 10; + } + } + """); } [WpfFact] public void TestForEachStatementWithoutInnerStatement() { - Test(@" -public class Bar -{ - public void Fo() - { - foreach (var x in """") - { - $$ - } - var c = 10; - } -}", @" -public class Bar -{ - public void Fo() - { - forea$$ch (var x $$in """")$$ - var c = 10; - } -}"); + Test(""" + public class Bar + { + public void Fo() + { + foreach (var x in "") + { + $$ + } + var c = 10; + } + } + """, """ + public class Bar + { + public void Fo() + { + forea$$ch (var x $$in "")$$ + var c = 10; + } + } + """); } [WpfFact] public void TestLockStatementWithoutInnerStatement() { - Test(@" -public class Bar -{ - object o = new object(); - public void Fo() - { - lock (o) - { - $$ - } - } -}", @" -public class Bar -{ - object o = new object(); - public void Fo() - { - l$$ock$$(o)$$ - } -}"); + Test(""" + public class Bar + { + object o = new object(); + public void Fo() + { + lock (o) + { + $$ + } + } + } + """, """ + public class Bar + { + object o = new object(); + public void Fo() + { + l$$ock$$(o)$$ + } + } + """); } [WpfFact] public void TestLockStatementWithInnerStatement() { - Test(@" -public class Bar -{ - object o = new object(); - public void Fo() - { - lock (o) - { - $$ - } - var i = 10; - } -}", @" -public class Bar -{ - object o = new object(); - public void Fo() - { - l$$ock$$(o)$$ - var i = 10; - } -}"); + Test(""" + public class Bar + { + object o = new object(); + public void Fo() + { + lock (o) + { + $$ + } + var i = 10; + } + } + """, """ + public class Bar + { + object o = new object(); + public void Fo() + { + l$$ock$$(o)$$ + var i = 10; + } + } + """); } [WpfFact] public void TestUsingStatementWithoutInnerStatement() { - Test(@" -using System; -public class Bar -{ - public void Fo() - { - using (var d = new D()) - { - $$ - } - } -} -public class D : IDisposable -{ - public void Dispose() - {} -}", @" -using System; -public class Bar -{ - public void Fo() - { - usi$$ng (va$$r d = new D())$$ - } -} -public class D : IDisposable -{ - public void Dispose() - {} -}"); + Test(""" + using System; + public class Bar + { + public void Fo() + { + using (var d = new D()) + { + $$ + } + } + } + public class D : IDisposable + { + public void Dispose() + {} + } + """, """ + using System; + public class Bar + { + public void Fo() + { + usi$$ng (va$$r d = new D())$$ + } + } + public class D : IDisposable + { + public void Dispose() + {} + } + """); } [WpfFact] public void TestUsingStatementWithInnerStatement() { - Test(@" -using System; -public class Bar -{ - public void Fo() - { - using (var d = new D()) - { - $$ - } - var c = 10; - } -} -public class D : IDisposable -{ - public void Dispose() - {} -}", @" -using System; -public class Bar -{ - public void Fo() - { - usi$$ng (va$$r d = new D())$$ - var c = 10; - } -} -public class D : IDisposable -{ - public void Dispose() - {} -}"); + Test(""" + using System; + public class Bar + { + public void Fo() + { + using (var d = new D()) + { + $$ + } + var c = 10; + } + } + public class D : IDisposable + { + public void Dispose() + {} + } + """, """ + using System; + public class Bar + { + public void Fo() + { + usi$$ng (va$$r d = new D())$$ + var c = 10; + } + } + public class D : IDisposable + { + public void Dispose() + {} + } + """); } [WpfFact] public void TestUsingInLocalDeclarationStatement() { - Test(@" -using System; -public class Bar -{ - public void Fo() - { - using var d = new D(); - $$ - } -} -public class D : IDisposable -{ - public void Dispose() - {} -}", @" -using System; -public class Bar -{ - public void Fo() - { - usi$$ng v$$ar$$ d = new D() - } -} -public class D : IDisposable -{ - public void Dispose() - {} -}"); + Test(""" + using System; + public class Bar + { + public void Fo() + { + using var d = new D(); + $$ + } + } + public class D : IDisposable + { + public void Dispose() + {} + } + """, """ + using System; + public class Bar + { + public void Fo() + { + usi$$ng v$$ar$$ d = new D() + } + } + public class D : IDisposable + { + public void Dispose() + {} + } + """); } [WpfFact] public void TestWhileStatementWithoutInnerStatement() { - Test(@" -public class Bar -{ - public void Fo() - { - while (true) - { - $$ - } - } -}", @" -public class Bar -{ - public void Fo() - { - wh$$ile (tr$$ue)$$ - } -}"); + Test(""" + public class Bar + { + public void Fo() + { + while (true) + { + $$ + } + } + } + """, """ + public class Bar + { + public void Fo() + { + wh$$ile (tr$$ue)$$ + } + } + """); } [WpfFact] public void TestWhileStatementWithInnerStatement() { - Test(@" -public class Bar -{ - public void Fo() - { - while (true) - { - $$ - } - var c = 10; - } -}", @" -public class Bar -{ - public void Fo() - { - wh$$ile (tr$$ue)$$ - var c = 10; - } -}"); + Test(""" + public class Bar + { + public void Fo() + { + while (true) + { + $$ + } + var c = 10; + } + } + """, """ + public class Bar + { + public void Fo() + { + wh$$ile (tr$$ue)$$ + var c = 10; + } + } + """); } [WpfFact] public void TestSwitchExpression1() { - Test(@" -public class Bar -{ - public void Goo(int c) - { - var d = c switch - { - $$ - } - } -}", - @" -public class Bar -{ - public void Goo(int c) - { - var d = c swi$$tch$$ - } -}"); + Test(""" + public class Bar + { + public void Goo(int c) + { + var d = c switch + { + $$ + } + } + } + """, + """ + public class Bar + { + public void Goo(int c) + { + var d = c swi$$tch$$ + } + } + """); } [WpfFact] public void TestSwitchExpression2() { - Test(@" -public class Bar -{ - public void Goo(int c) - { - var d = (c + 1) switch - { - $$ - } - } -}", - @" -public class Bar -{ - public void Goo(int c) - { - var d = (c + 1) swi$$tch$$ - } -}"); + Test(""" + public class Bar + { + public void Goo(int c) + { + var d = (c + 1) switch + { + $$ + } + } + } + """, + """ + public class Bar + { + public void Goo(int c) + { + var d = (c + 1) swi$$tch$$ + } + } + """); } @@ -2875,363 +3221,389 @@ public void TestSwitchStatementWithOnlyOpenParenthesis() // This test is to make sure {} will be added to the switch statement, // but our formatter now can't format the case when the CloseParenthesis token is missing. // If any future formatter improvement can handle this case, this test can be modified safely - Test(@" -public class bar -{ - public void TT() - { - switch ( -{ - $$ - } - } -}", @" -public class bar -{ - public void TT() - { - swi$$tch ($$ - } -}"); + Test(""" + public class bar + { + public void TT() + { + switch ( + { + $$ + } + } + } + """, """ + public class bar + { + public void TT() + { + swi$$tch ($$ + } + } + """); } [WpfFact] public void TestSwitchStatement() { - Test(@" -public class bar -{ - public void TT() - { - int i = 10; - switch (i) - { - $$ - } - } -}", @" -public class bar -{ - public void TT() - { - int i = 10; - switc$$h ($$i)$$ - } -}"); + Test(""" + public class bar + { + public void TT() + { + int i = 10; + switch (i) + { + $$ + } + } + } + """, """ + public class bar + { + public void TT() + { + int i = 10; + switc$$h ($$i)$$ + } + } + """); } [WpfFact] public void TestValidSwitchStatement() { - Test(@" -public class bar -{ - public void TT() - { - int i = 10; - switch (i) - $$ - { - } - } -}", @" -public class bar -{ - public void TT() - { - int i = 10; - switc$$h ($$i)$$ - { - } - } -}"); + Test(""" + public class bar + { + public void TT() + { + int i = 10; + switch (i) + $$ + { + } + } + } + """, """ + public class bar + { + public void TT() + { + int i = 10; + switc$$h ($$i)$$ + { + } + } + } + """); } [WpfFact] public void TestValidTryStatement() { - Test(@" -public class bar -{ - public void TT() - { - try - $$ - { - } - } -}", @" -public class bar -{ - public void TT() - { - tr$$y$$ - { - } - } -}"); + Test(""" + public class bar + { + public void TT() + { + try + $$ + { + } + } + } + """, """ + public class bar + { + public void TT() + { + tr$$y$$ + { + } + } + } + """); } [WpfFact] public void TestTryStatement() { - Test(@" -public class bar -{ - public void TT() - { - try - { - $$ - } - } -}", @" -public class bar -{ - public void TT() - { - tr$$y$$ - } -}"); - } - - [WpfFact] - public void TestValidCatchClause() - { - Test(@" -public class Bar -{ - public void TT() - { - try - { - } - catch (System.Exception) - $$ - { - } - } -}", @" -public class Bar -{ - public void TT() - { - try - { - } - cat$$ch (Syste$$m.Exception)$$ - { - } - } -}"); + Test(""" + public class bar + { + public void TT() + { + try + { + $$ + } + } + } + """, """ + public class bar + { + public void TT() + { + tr$$y$$ + } + } + """); } [WpfFact] - public void TestCatchClauseWithException() - { - Test(@" -public class Bar -{ - public void TT() + public void TestValidCatchClause() { - try - { - } - catch (System.Exception) - { - $$ - } + Test(""" + public class Bar + { + public void TT() + { + try + { + } + catch (System.Exception) + $$ + { + } + } + } + """, """ + public class Bar + { + public void TT() + { + try + { + } + cat$$ch (Syste$$m.Exception)$$ + { + } + } + } + """); } -}", @" -public class Bar -{ - public void TT() + + [WpfFact] + public void TestCatchClauseWithException() { - try - { - } - cat$$ch (Syste$$m.Exception)$$ - } -}"); + Test(""" + public class Bar + { + public void TT() + { + try + { + } + catch (System.Exception) + { + $$ + } + } + } + """, """ + public class Bar + { + public void TT() + { + try + { + } + cat$$ch (Syste$$m.Exception)$$ + } + } + """); } [WpfFact] public void TestSingleCatchClause() { - Test(@" -public class bar -{ - public void TT() - { - try - { - } - catch - { - $$ - } - } -}", @" -public class bar -{ - public void TT() - { - try - { - } - cat$$ch$$ - } -}"); + Test(""" + public class bar + { + public void TT() + { + try + { + } + catch + { + $$ + } + } + } + """, """ + public class bar + { + public void TT() + { + try + { + } + cat$$ch$$ + } + } + """); } [WpfFact] public void TestCatchClauseWithWhenClause() { - Test(@" -public class bar -{ - public void TT() - { - try - { - } - catch (Exception) when (true) - { - $$ - } - } -}", @" -public class bar -{ - public void TT() - { - try - { - } - c$$atch (Ex$$ception) whe$$n (tru$$e)$$ - } -}"); + Test(""" + public class bar + { + public void TT() + { + try + { + } + catch (Exception) when (true) + { + $$ + } + } + } + """, """ + public class bar + { + public void TT() + { + try + { + } + c$$atch (Ex$$ception) whe$$n (tru$$e)$$ + } + } + """); } [WpfFact] - public void TestFinallyCaluse() - { - Test(@" -public class Bar -{ - public void Bar2() - { - try - { - } - catch (System.Exception) - { - } - finally - { - $$ - } - } -}", @" -public class Bar -{ - public void Bar2() + public void TestFinallyClause() { - try - { - } - catch (System.Exception) - { - } - fin$$ally$$ - } -}"); + Test(""" + public class Bar + { + public void Bar2() + { + try + { + } + catch (System.Exception) + { + } + finally + { + $$ + } + } + } + """, """ + public class Bar + { + public void Bar2() + { + try + { + } + catch (System.Exception) + { + } + fin$$ally$$ + } + } + """); } [WpfFact] - public void TestValidFinallyCaluse() - { - Test(@" -public class Bar -{ - public void Bar2() - { - try - { - } - catch (System.Exception) - { - } - finally - $$ - { - } - } -}", @" -public class Bar -{ - public void Bar2() + public void TestValidFinallyClause() { - try - { - } - catch (System.Exception) - { - } - fin$$ally$$ - { - } - } -}"); + Test(""" + public class Bar + { + public void Bar2() + { + try + { + } + catch (System.Exception) + { + } + finally + $$ + { + } + } + } + """, """ + public class Bar + { + public void Bar2() + { + try + { + } + catch (System.Exception) + { + } + fin$$ally$$ + { + } + } + } + """); } [WpfFact] public void TestObjectCreationExpressionWithMissingType() { - Test(@" -public class Bar -{ - public void Bar2() - { - Bar b = new() - { - $$ - }; - } -}", -@" -public class Bar -{ - public void Bar2() - { - Bar b = new$$ - } -}"); + Test(""" + public class Bar + { + public void Bar2() + { + Bar b = new() + { + $$ + }; + } + } + """, + """ + public class Bar + { + public void Bar2() + { + Bar b = new$$ + } + } + """); } [WpfFact] public void TestRemoveInitializerForImplicitObjectCreationExpression() { - Test(@" -public class Bar -{ - public void Bar2() - { - Bar b = new(); - $$ - } -}", -@" -public class Bar -{ - public void Bar2() - { - Bar b = new() - { - $$ - }; - } -}"); + Test(""" + public class Bar + { + public void Bar2() + { + Bar b = new(); + $$ + } + } + """, + """ + public class Bar + { + public void Bar2() + { + Bar b = new() + { + $$ + }; + } + } + """); } [WpfTheory] @@ -3239,25 +3611,27 @@ public void Bar2() [InlineData("unchecked")] public void TestCheckedStatement(string keywordToken) { - Test($@" -public class Bar -{{ - public void Bar2() - {{ - {keywordToken} - {{ - $$ - }} - }} -}}", -$@" -public class Bar -{{ - public void Bar2() - {{ - {keywordToken}$$ - }} -}}"); + Test($$""" + public class Bar + { + public void Bar2() + { + {{keywordToken}} + { + $$ + } + } + } + """, + $$""" + public class Bar + { + public void Bar2() + { + {{keywordToken}}$$ + } + } + """); } [WpfTheory] @@ -3265,79 +3639,85 @@ public void Bar2() [InlineData("unchecked")] public void TextCheckedExpression(string keywordToken) { - Test($@" -public class Bar -{{ - public void Bar2() - {{ - var i = {keywordToken}(1 + 1); - $$ - }} -}}", -$@" -public class Bar -{{ - public void Bar2() - {{ - var i = {keywordToken}$$(1 +$$ 1)$$ - }} -}}"); + Test($$""" + public class Bar + { + public void Bar2() + { + var i = {{keywordToken}}(1 + 1); + $$ + } + } + """, + $$""" + public class Bar + { + public void Bar2() + { + var i = {{keywordToken}}$$(1 +$$ 1)$$ + } + } + """); } [WpfFact] public void TestConvertFieldToPropertyWithAttributeAndComment() { - Test(@" -public class Bar -{ - public int Property - { - $$ - } + Test(""" + public class Bar + { + public int Property + { + $$ + } - /// - /// - [SomeAttri] - public void Method() { } -}", -@" -public class Bar -{ - public int Property$$ + /// + /// + [SomeAttri] + public void Method() { } + } + """, + """ + public class Bar + { + public int Property$$ - /// - /// - [SomeAttri] - public void Method() { } -}"); + /// + /// + [SomeAttri] + public void Method() { } + } + """); } [WpfFact] public void TestConvertEventFieldToPropertyWithAttributeAndComment() { - Test(@" -public class Bar -{ - public event EventHandler MyEvent - { - $$ - } + Test(""" + public class Bar + { + public event EventHandler MyEvent + { + $$ + } - /// - /// - [SomeAttri] - public void Method() { } -}", -@" -public class Bar -{ - public event EventHandler MyEvent$$ + /// + /// + [SomeAttri] + public void Method() { } + } + """, + """ + public class Bar + { + public event EventHandler MyEvent$$ - /// - /// - [SomeAttri] - public void Method() { } -}"); + /// + /// + [SomeAttri] + public void Method() { } + } + """); } protected override string Language => LanguageNames.CSharp; From 65f6568981ba8c541d7dec8265ef2a8b5c169d7c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 22 Nov 2024 12:19:45 -0800 Subject: [PATCH 501/508] Update work item --- .../CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs index 812a649812878..97355f875dd6f 100644 --- a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs +++ b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs @@ -768,7 +768,7 @@ void Method() """); } - [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530352")] + [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/66102")] public void EmbeddedStatement4() { Test(""" From 1372830f4adb171184cca078d45c6794ab308830 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 22 Nov 2024 12:24:23 -0800 Subject: [PATCH 502/508] Fix --- .../CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs index 97355f875dd6f..284d985fb2803 100644 --- a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs +++ b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs @@ -20,6 +20,7 @@ public sealed class AutomaticLineEnderTests : AbstractAutomaticLineEnderTests public void Creation() { Test(""" + $$ """, "$$"); } From 3248330dfc5cd9c92f81eea26fdc9a797ccc2e15 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 22 Nov 2024 12:25:30 -0800 Subject: [PATCH 503/508] Renames --- .../AutomaticCompletion/AutomaticLineEnderTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs index 284d985fb2803..c3b01029400f0 100644 --- a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs +++ b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs @@ -248,7 +248,7 @@ void Method() } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/57323")] - public void EmbededStatementFollowedByStatement() + public void EmbeddedStatementFollowedByStatement() { Test(""" class C @@ -1079,7 +1079,7 @@ void Method() } [WpfFact] - public void TestMulitpleNamespace() + public void TestMultipleNamespace() { Test($$""" namespace Bar2 @@ -1855,7 +1855,7 @@ public int Foo } [WpfFact] - public void TestMulitpleFields() + public void TestMultipleFields() { Test(""" public class Bar From a124e51ec43399482e299effe4c58a27e336aa27 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 22 Nov 2024 16:35:01 -0800 Subject: [PATCH 504/508] Anonymous types in quick info should not have navigation links --- .../AnonymousTypeDisplayService/StructuralTypeDisplayInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/LanguageServices/AnonymousTypeDisplayService/StructuralTypeDisplayInfo.cs b/src/Features/Core/Portable/LanguageServices/AnonymousTypeDisplayService/StructuralTypeDisplayInfo.cs index 041e3ac58e745..813d2eff75a5c 100644 --- a/src/Features/Core/Portable/LanguageServices/AnonymousTypeDisplayService/StructuralTypeDisplayInfo.cs +++ b/src/Features/Core/Portable/LanguageServices/AnonymousTypeDisplayService/StructuralTypeDisplayInfo.cs @@ -53,7 +53,7 @@ public static bool ReplaceStructuralTypes( if (structuralTypeToName.TryGetValue(type, out var name) && part.ToString() != name) { - newParts.Add(new SymbolDisplayPart(part.Kind, part.Symbol, name)); + newParts.Add(new SymbolDisplayPart(part.Kind, symbol: null, name)); changed = true; continue; } From ee2bc02c24a1574f8470017af0eefe1bb81b28cb Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 22 Nov 2024 16:52:38 -0800 Subject: [PATCH 505/508] use raw strings in tests --- SpellingExclusions.dic | 1 + .../SyncNamespaceTests_ChangeNamespace.cs | 3517 +++++++++-------- 2 files changed, 1806 insertions(+), 1712 deletions(-) diff --git a/SpellingExclusions.dic b/SpellingExclusions.dic index 44098153565f7..17a33d838ea5c 100644 --- a/SpellingExclusions.dic +++ b/SpellingExclusions.dic @@ -2,3 +2,4 @@ awaitable Refactorings Infos +cref diff --git a/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs b/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs index 51b73da12df25..d74102910a0dd 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Threading.Tasks; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; @@ -23,19 +21,20 @@ public async Task ChangeNamespace_InvalidFolderName1() // No change namespace action because the folder name is not valid identifier var (folder, filePath) = CreateDocumentFilePath(["3B", "C"], "File1.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - class Class1 - {{ - }} -}} - - -"; + $$""" + + + + namespace [||]{{declaredNamespace}} + { + class Class1 + { + } + } + + + + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal: null); } @@ -48,19 +47,20 @@ public async Task ChangeNamespace_InvalidFolderName2() // No change namespace action because the folder name is not valid identifier var (folder, filePath) = CreateDocumentFilePath(["B.3C", "D"], "File1.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - class Class1 - {{ - }} -}} - - -"; + $$""" + + + + namespace [||]{{declaredNamespace}} + { + class Class1 + { + } + } + + + + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal: null); } @@ -72,26 +72,29 @@ public async Task ChangeNamespace_SingleDocumentNoReference() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - class Class1 - {{ - }} -}} - -"; + $$""" + + + + namespace [||]{{declaredNamespace}} + { + class Class1 + { + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - class Class1 - { - } -}"; + """ + namespace A.B.C + { + class Class1 + { + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal); } @@ -103,26 +106,26 @@ public async Task ChangeNamespace_SingleDocumentNoReference_FileScopedNamespace( var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace}; - -class Class1 -{{ -}} - - -"; + $$""" + + + namespace [||]{{declaredNamespace}}; + + class Class1 + { + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C; + """ + namespace A.B.C; -class Class1 -{ -} -"; + class Class1 + { + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal); } @@ -134,46 +137,49 @@ public async Task ChangeNamespace_SingleDocumentLocalReference() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - delegate void D1; - - interface Class1 - {{ - void M1(); - }} - - class Class2 : {declaredNamespace}.Class1 - {{ - {declaredNamespace}.D1 d; - - void {declaredNamespace}.Class1.M1(){{}} - }} -}} - -"; + $$""" + + + + namespace [||]{{declaredNamespace}} + { + delegate void D1; + + interface Class1 + { + void M1(); + } + + class Class2 : {{declaredNamespace}}.Class1 + { + {{declaredNamespace}}.D1 d; + + void {{declaredNamespace}}.Class1.M1(){} + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - delegate void D1; - - interface Class1 - { - void M1(); - } - - class Class2 : Class1 - { - D1 d; - - void Class1.M1() { } - } -}"; + """ + namespace A.B.C + { + delegate void D1; + + interface Class1 + { + void M1(); + } + + class Class2 : Class1 + { + D1 d; + + void Class1.M1() { } + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal); } @@ -186,71 +192,73 @@ public async Task ChangeNamespace_WithCrefReference() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - /// <summary> - /// See <see cref=""Class1""/> - /// See <see cref=""{declaredNamespace}.Class1""/> - /// See <see cref=""global::{declaredNamespace}.Class1""/> - /// See <see cref=""global::{declaredNamespace}.Class1.M1""/> - /// </summary> - public class Class1 - {{ - public void M1() {{ }} - }} -}} - -namespace Foo -{{ - using {declaredNamespace}; - - /// <summary> - /// See <see cref=""Class1""/> - /// See <see cref=""{declaredNamespace}.Class1""/> - /// See <see cref=""global::{declaredNamespace}.Class1""/> - /// See <see cref=""global::{declaredNamespace}.Class1.M1""/> - /// </summary> - class RefClass - {{ - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + /// <summary> + /// See <see cref="Class1"/> + /// See <see cref="{{declaredNamespace}}.Class1"/> + /// See <see cref="global::{{declaredNamespace}}.Class1"/> + /// See <see cref="global::{{declaredNamespace}}.Class1.M1"/> + /// </summary> + public class Class1 + { + public void M1() { } + } + } + namespace Foo + { + using {{declaredNamespace}}; + + /// <summary> + /// See <see cref="Class1"/> + /// See <see cref="{{declaredNamespace}}.Class1"/> + /// See <see cref="global::{{declaredNamespace}}.Class1"/> + /// See <see cref="global::{{declaredNamespace}}.Class1.M1"/> + /// </summary> + class RefClass + { + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - /// - /// See - /// See - /// See - /// See - /// - public class Class1 - { - public void M1() { } - } -}"; + """ + namespace A.B.C + { + /// + /// See + /// See + /// See + /// See + /// + public class Class1 + { + public void M1() { } + } + } + """; var expectedSourceReference = -@" -namespace Foo -{ - using A.B.C; - - /// - /// See - /// See - /// See - /// See - /// - class RefClass - { - } -}"; + """ + namespace Foo + { + using A.B.C; + + /// + /// See + /// See + /// See + /// See + /// + class RefClass + { + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -262,57 +270,59 @@ public async Task ChangeNamespace_WithCrefReferencesInVB() var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - /// <summary> - /// See <see cref=""Class1""/> - /// See <see cref=""{declaredNamespace}.Class1""/> - /// </summary> - public class Class1 - {{ - }} -}} - - - -Imports {declaredNamespace} - -''' <summary> -''' See <see cref=""Class1""/> -''' See <see cref=""{declaredNamespace}.Class1""/> -''' </summary> -Public Class VBClass - Public ReadOnly Property C1 As Class1 -End Class - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + /// <summary> + /// See <see cref="Class1"/> + /// See <see cref="{{declaredNamespace}}.Class1"/> + /// </summary> + public class Class1 + { + } + } + + + Imports {{declaredNamespace}} + + ''' <summary> + ''' See <see cref="Class1"/> + ''' See <see cref="{{declaredNamespace}}.Class1"/> + ''' </summary> + Public Class VBClass + Public ReadOnly Property C1 As Class1 + End Class + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - /// - /// See - /// See - /// - public class Class1 - { - } -}"; + """ + namespace A.B.C + { + /// + /// See + /// See + /// + public class Class1 + { + } + } + """; var expectedSourceReference = -@" -Imports A.B.C - -''' -''' See -''' See -''' -Public Class VBClass - Public ReadOnly Property C1 As Class1 -End Class"; + """ + Imports A.B.C + + ''' + ''' See + ''' See + ''' + Public Class VBClass + Public ReadOnly Property C1 As Class1 + End Class + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -325,51 +335,54 @@ public async Task ChangeNamespace_ReferencingTypesDeclaredInOtherDocument() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - class Class1 - {{ - private Class2 c2; - private Class3 c3; - private Class4 c4; - }} -}} - -namespace Foo -{{ - class Class2 {{}} - - namespace Bar - {{ - class Class3 {{}} - - namespace Baz - {{ - class Class4 {{}} - }} - }} -}} - -"; + $$""" + + + + namespace [||]{{declaredNamespace}} + { + class Class1 + { + private Class2 c2; + private Class3 c3; + private Class4 c4; + } + } + + namespace Foo + { + class Class2 {} + + namespace Bar + { + class Class3 {} + + namespace Baz + { + class Class4 {} + } + } + } + + + """; var expectedSourceOriginal = -@"using Foo; -using Foo.Bar; -using Foo.Bar.Baz; - -namespace A.B.C -{ - class Class1 - { - private Class2 c2; - private Class3 c3; - private Class4 c4; - } -}"; + """ + using Foo; + using Foo.Bar; + using Foo.Bar.Baz; + + namespace A.B.C + { + class Class1 + { + private Class2 c2; + private Class3 c3; + private Class4 c4; + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal); } @@ -382,51 +395,54 @@ public async Task ChangeNamespace_ReferencingQualifiedTypesDeclaredInOtherDocume var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - class Class1 - {{ - private Foo.Class2 c2; - private Foo.Bar.Class3 c3; - private Foo.Bar.Baz.Class4 c4; - }} -}} - -namespace Foo -{{ - class Class2 {{}} - - namespace Bar - {{ - class Class3 {{}} - - namespace Baz - {{ - class Class4 {{}} - }} - }} -}} - -"; + $$""" + + + + namespace [||]{{declaredNamespace}} + { + class Class1 + { + private Foo.Class2 c2; + private Foo.Bar.Class3 c3; + private Foo.Bar.Baz.Class4 c4; + } + } + + namespace Foo + { + class Class2 {} + + namespace Bar + { + class Class3 {} + + namespace Baz + { + class Class4 {} + } + } + } + + + """; var expectedSourceOriginal = -@"using Foo; -using Foo.Bar; -using Foo.Bar.Baz; - -namespace A.B.C -{ - class Class1 - { - private Class2 c2; - private Class3 c3; - private Class4 c4; - } -}"; + """ + using Foo; + using Foo.Bar; + using Foo.Bar.Baz; + + namespace A.B.C + { + class Class1 + { + private Class2 c2; + private Class3 c3; + private Class4 c4; + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal); } @@ -439,65 +455,67 @@ public async Task ChangeNamespace_WithReferencesInOtherDocument() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - class Class1 - {{ - }} - - class Class2 - {{ - }} -}} - -using Foo.Bar.Baz; - -namespace Foo -{{ - class RefClass - {{ - private Class1 c1; - - void M1() - {{ - Bar.Baz.Class2 c2 = null; - }} - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + class Class1 + { + } + + class Class2 + { + } + } + using Foo.Bar.Baz; + + namespace Foo + { + class RefClass + { + private Class1 c1; + + void M1() + { + Bar.Baz.Class2 c2 = null; + } + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - class Class1 - { - } - - class Class2 - { - } -}"; + """ + namespace A.B.C + { + class Class1 + { + } + + class Class2 + { + } + } + """; var expectedSourceReference = -@" -using A.B.C; - -namespace Foo -{ - class RefClass - { - private Class1 c1; - - void M1() - { - Class2 c2 = null; - } - } -}"; + """ + using A.B.C; + + namespace Foo + { + class RefClass + { + private Class1 c1; + + void M1() + { + Class2 c2 = null; + } + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -510,49 +528,51 @@ public async Task ChangeNamespace_WithQualifiedReferencesInOtherDocument() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - interface Interface1 - {{ - void M1(Interface1 c1); - }} -}} - -namespace Foo -{{ - using {declaredNamespace}; - - class RefClass : Interface1 - {{ - void {declaredNamespace}.Interface1.M1(Interface1 c1){{}} - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + interface Interface1 + { + void M1(Interface1 c1); + } + } + namespace Foo + { + using {{declaredNamespace}}; + + class RefClass : Interface1 + { + void {{declaredNamespace}}.Interface1.M1(Interface1 c1){} + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - interface Interface1 - { - void M1(Interface1 c1); - } -}"; + """ + namespace A.B.C + { + interface Interface1 + { + void M1(Interface1 c1); + } + } + """; var expectedSourceReference = -@" -namespace Foo -{ - using A.B.C; - - class RefClass : Interface1 - { - void Interface1.M1(Interface1 c1){} - } -}"; + """ + namespace Foo + { + using A.B.C; + + class RefClass : Interface1 + { + void Interface1.M1(Interface1 c1){} + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -565,65 +585,67 @@ public async Task ChangeNamespace_ChangeUsingsInMultipleContainers() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - class Class1 - {{ - }} -}} - -namespace NS1 -{{ - using Foo.Bar.Baz; - - class Class2 - {{ - Class1 c2; - }} - - namespace NS2 - {{ - using Foo.Bar.Baz; - - class Class2 - {{ - Class1 c1; - }} - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + class Class1 + { + } + } + namespace NS1 + { + using Foo.Bar.Baz; + + class Class2 + { + Class1 c2; + } + + namespace NS2 + { + using Foo.Bar.Baz; + + class Class2 + { + Class1 c1; + } + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - class Class1 - { - } -}"; + """ + namespace A.B.C + { + class Class1 + { + } + } + """; var expectedSourceReference = -@" -namespace NS1 -{ - using A.B.C; - - class Class2 - { - Class1 c2; - } - - namespace NS2 - { - class Class2 - { - Class1 c1; - } - } -}"; + """ + namespace NS1 + { + using A.B.C; + + class Class2 + { + Class1 c2; + } + + namespace NS2 + { + class Class2 + { + Class1 c1; + } + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -636,68 +658,70 @@ public async Task ChangeNamespace_WithAliasReferencesInOtherDocument() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - class Class1 - {{ - }} - - class Class2 - {{ - }} -}} - -using System; -using Class1Alias = Foo.Bar.Baz.Class1; - -namespace Foo -{{ - class RefClass - {{ - private Class1Alias c1; - - void M1() - {{ - Bar.Baz.Class2 c2 = null; - }} - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + class Class1 + { + } + + class Class2 + { + } + } + using System; + using Class1Alias = Foo.Bar.Baz.Class1; + + namespace Foo + { + class RefClass + { + private Class1Alias c1; + + void M1() + { + Bar.Baz.Class2 c2 = null; + } + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - class Class1 - { - } - - class Class2 - { - } -}"; + """ + namespace A.B.C + { + class Class1 + { + } + + class Class2 + { + } + } + """; var expectedSourceReference = -@" -using System; -using A.B.C; -using Class1Alias = A.B.C.Class1; - -namespace Foo -{ - class RefClass - { - private Class1Alias c1; - - void M1() - { - Class2 c2 = null; - } - } -}"; + """ + using System; + using A.B.C; + using Class1Alias = A.B.C.Class1; + + namespace Foo + { + class RefClass + { + private Class1Alias c1; + + void M1() + { + Class2 c2 = null; + } + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -709,36 +733,37 @@ public async Task ChangeToGlobalNamespace_SingleDocumentNoRef() var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var code = -$@" - - - -using System; - -// Comments before declaration. -namespace [||]{declaredNamespace} -{{ // Comments after opening brace - class Class1 - {{ - }} - // Comments before closing brace -}} // Comments after declaration. - - -"; + $$""" + + + using System; + + // Comments before declaration. + namespace [||]{{declaredNamespace}} + { // Comments after opening brace + class Class1 + { + } + // Comments before closing brace + } // Comments after declaration. + + + + """; var expectedSourceOriginal = -@" -using System; - -// Comments before declaration. -// Comments after opening brace -class Class1 -{ -} -// Comments before closing brace -// Comments after declaration. -"; + """ + using System; + + // Comments before declaration. + // Comments after opening brace + class Class1 + { + } + // Comments before closing brace + // Comments after declaration. + + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal); } @@ -750,44 +775,46 @@ public async Task ChangeToGlobalNamespace_SingleDocumentLocalRef() var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - delegate void D1; - - interface Class1 - {{ - void M1(); - }} - - class Class2 : {declaredNamespace}.Class1 - {{ - global::{declaredNamespace}.D1 d; - - void {declaredNamespace}.Class1.M1() {{ }} - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + delegate void D1; + + interface Class1 + { + void M1(); + } + + class Class2 : {{declaredNamespace}}.Class1 + { + global::{{declaredNamespace}}.D1 d; + + void {{declaredNamespace}}.Class1.M1() { } + } + } + + + """; var expectedSourceOriginal = -@"delegate void D1; + """ + delegate void D1; -interface Class1 -{ - void M1(); -} + interface Class1 + { + void M1(); + } -class Class2 : Class1 -{ - global::D1 d; + class Class2 : Class1 + { + global::D1 d; - void Class1.M1() { } -} -"; + void Class1.M1() { } + } + + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal); } @@ -800,60 +827,63 @@ public async Task ChangeToGlobalNamespace_WithReferencesInOtherDocument() var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - class Class1 - {{ - }} - - class Class2 - {{ - }} -}} - -using Foo.Bar.Baz; - -namespace Foo -{{ - class RefClass - {{ - private Class1 c1; - - void M1() - {{ - Bar.Baz.Class2 c2 = null; - }} - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + class Class1 + { + } + + class Class2 + { + } + } + using Foo.Bar.Baz; + + namespace Foo + { + class RefClass + { + private Class1 c1; + + void M1() + { + Bar.Baz.Class2 c2 = null; + } + } + } + + + """; var expectedSourceOriginal = -@"class Class1 -{ -} + """ + class Class1 + { + } -class Class2 -{ -} -"; - var expectedSourceReference = -@"namespace Foo -{ - class RefClass - { - private Class1 c1; + class Class2 + { + } - void M1() - { - Class2 c2 = null; - } - } -}"; + """; + var expectedSourceReference = + """ + namespace Foo + { + class RefClass + { + private Class1 c1; + + void M1() + { + Class2 c2 = null; + } + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -866,45 +896,47 @@ public async Task ChangeToGlobalNamespace_WithQualifiedReferencesInOtherDocument var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - interface Interface1 - {{ - void M1(Interface1 c1); - }} -}} - -namespace Foo -{{ - using {declaredNamespace}; - - class RefClass : Interface1 - {{ - void {declaredNamespace}.Interface1.M1(Interface1 c1){{}} - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + interface Interface1 + { + void M1(Interface1 c1); + } + } + namespace Foo + { + using {{declaredNamespace}}; + + class RefClass : Interface1 + { + void {{declaredNamespace}}.Interface1.M1(Interface1 c1){} + } + } + + + """; var expectedSourceOriginal = -@"interface Interface1 -{ - void M1(Interface1 c1); -} -"; + """ + interface Interface1 + { + void M1(Interface1 c1); + } + + """; var expectedSourceReference = -@" -namespace Foo -{ - class RefClass : Interface1 - { - void Interface1.M1(Interface1 c1){} - } -}"; + """ + namespace Foo + { + class RefClass : Interface1 + { + void Interface1.M1(Interface1 c1){} + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -917,51 +949,53 @@ public async Task ChangeToGlobalNamespace_WithReferenceAndConflictDeclarationInO var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - class MyClass - {{ - }} -}} - -namespace Foo -{{ - using {declaredNamespace}; - - class RefClass - {{ - Foo.Bar.Baz.MyClass c; - }} - - class MyClass - {{ - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + class MyClass + { + } + } + namespace Foo + { + using {{declaredNamespace}}; + + class RefClass + { + Foo.Bar.Baz.MyClass c; + } + + class MyClass + { + } + } + + + """; var expectedSourceOriginal = -@"class MyClass -{ -} -"; - var expectedSourceReference = -@" -namespace Foo -{ - class RefClass - { - global::MyClass c; - } + """ + class MyClass + { + } - class MyClass - { - } -}"; + """; + var expectedSourceReference = + """ + namespace Foo + { + class RefClass + { + global::MyClass c; + } + + class MyClass + { + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -974,49 +1008,50 @@ public async Task ChangeToGlobalNamespace_ReferencingTypesDeclaredInOtherDocumen var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - class Class1 - {{ - private Class2 c2; - private Class3 c3; - private Class4 c4; - }} -}} - -namespace Foo -{{ - class Class2 {{}} - - namespace Bar - {{ - class Class3 {{}} - - namespace Baz - {{ - class Class4 {{}} - }} - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + class Class1 + { + private Class2 c2; + private Class3 c3; + private Class4 c4; + } + } + namespace Foo + { + class Class2 {} + + namespace Bar + { + class Class3 {} + + namespace Baz + { + class Class4 {} + } + } + } + + + """; var expectedSourceOriginal = -@"using Foo; -using Foo.Bar; -using Foo.Bar.Baz; - -class Class1 -{ - private Class2 c2; - private Class3 c3; - private Class4 c4; -} -"; + """ + using Foo; + using Foo.Bar; + using Foo.Bar.Baz; + + class Class1 + { + private Class2 c2; + private Class3 c3; + private Class4 c4; + } + + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal); } @@ -1029,61 +1064,63 @@ public async Task ChangeToGlobalNamespace_ChangeUsingsInMultipleContainers() var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - class Class1 - {{ - }} -}} - -namespace NS1 -{{ - using Foo.Bar.Baz; - - class Class2 - {{ - Class1 c2; - }} - - namespace NS2 - {{ - using Foo.Bar.Baz; - - class Class2 - {{ - Class1 c1; - }} - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + class Class1 + { + } + } + namespace NS1 + { + using Foo.Bar.Baz; + + class Class2 + { + Class1 c2; + } + + namespace NS2 + { + using Foo.Bar.Baz; + + class Class2 + { + Class1 c1; + } + } + } + + + """; var expectedSourceOriginal = -@"class Class1 -{ -} -"; - var expectedSourceReference = -@" -namespace NS1 -{ - class Class2 - { - Class1 c2; - } + """ + class Class1 + { + } - namespace NS2 - { - class Class2 - { - Class1 c1; - } - } -}"; + """; + var expectedSourceReference = + """ + namespace NS1 + { + class Class2 + { + Class1 c2; + } + + namespace NS2 + { + class Class2 + { + Class1 c1; + } + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1096,65 +1133,67 @@ public async Task ChangeToGlobalNamespace_WithAliasReferencesInOtherDocument() var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - class Class1 - {{ - }} - - class Class2 - {{ - }} -}} - -using System; -using Class1Alias = Foo.Bar.Baz.Class1; - -namespace Foo -{{ - class RefClass - {{ - private Class1Alias c1; - - void M1() - {{ - Bar.Baz.Class2 c2 = null; - }} - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + class Class1 + { + } + + class Class2 + { + } + } + using System; + using Class1Alias = Foo.Bar.Baz.Class1; + + namespace Foo + { + class RefClass + { + private Class1Alias c1; + + void M1() + { + Bar.Baz.Class2 c2 = null; + } + } + } + + + """; var expectedSourceOriginal = -@"class Class1 -{ -} - -class Class2 -{ -} -"; - var expectedSourceReference = -@" -using System; -using Class1Alias = Class1; + """ + class Class1 + { + } -namespace Foo -{ - class RefClass - { - private Class1Alias c1; + class Class2 + { + } - void M1() - { - Class2 c2 = null; - } - } -}"; + """; + var expectedSourceReference = + """ + using System; + using Class1Alias = Class1; + + namespace Foo + { + class RefClass + { + private Class1Alias c1; + + void M1() + { + Class2 c2 = null; + } + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1164,29 +1203,30 @@ public async Task ChangeFromGlobalNamespace_SingleDocumentNoRef() var defaultNamespace = "A"; var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var code = -$@" - - - -using System; - -class [||]Class1 -{{ -}} - - -"; + $$""" + + + using System; + + class [||]Class1 + { + } + + + + """; var expectedSourceOriginal = -@" -using System; - -namespace A.B.C -{ - class Class1 - { - } -}"; + """ + using System; + + namespace A.B.C + { + class Class1 + { + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal); } @@ -1196,43 +1236,46 @@ public async Task ChangeFromGlobalNamespace_SingleDocumentLocalRef() var defaultNamespace = "A"; var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var code = -$@" - - - -delegate void [||]D1; - -interface Class1 -{{ - void M1(); -}} - -class Class2 : Class1 -{{ - D1 d; - - void Class1.M1() {{ }} -}} - -"; + $$""" + + + + delegate void [||]D1; + + interface Class1 + { + void M1(); + } + + class Class2 : Class1 + { + D1 d; + + void Class1.M1() { } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - delegate void D1; - - interface Class1 - { - void M1(); - } - - class Class2 : Class1 - { - D1 d; - - void Class1.M1() { } - } -}"; + """ + namespace A.B.C + { + delegate void D1; + + interface Class1 + { + void M1(); + } + + class Class2 : Class1 + { + D1 d; + + void Class1.M1() { } + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal); } @@ -1244,60 +1287,62 @@ public async Task ChangeFromGlobalNamespace_WithReferencesInOtherDocument() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -class [||]Class1 -{{ -}} - -class Class2 -{{ -}} - -namespace Foo -{{ - class RefClass - {{ - private Class1 c1; - - void M1() - {{ - Class2 c2 = null; - }} - }} -}} - -"; + $$""" + + + class [||]Class1 + { + } + + class Class2 + { + } + namespace Foo + { + class RefClass + { + private Class1 c1; + + void M1() + { + Class2 c2 = null; + } + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - class Class1 - { - } - - class Class2 - { - } -}"; + """ + namespace A.B.C + { + class Class1 + { + } + + class Class2 + { + } + } + """; var expectedSourceReference = -@" -using A.B.C; - -namespace Foo -{ - class RefClass - { - private Class1 c1; - - void M1() - { - Class2 c2 = null; - } - } -}"; + """ + using A.B.C; + + namespace Foo + { + class RefClass + { + private Class1 c1; + + void M1() + { + Class2 c2 = null; + } + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1308,44 +1353,46 @@ public async Task ChangeFromGlobalNamespace_WithQualifiedReferencesInOtherDocume var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -interface [||]Interface1 -{{ - void M1(Interface1 c1); -}} - -namespace Foo -{{ - class RefClass : Interface1 - {{ - void Interface1.M1(Interface1 c1){{}} - }} -}} - -"; + $$""" + + + interface [||]Interface1 + { + void M1(Interface1 c1); + } + namespace Foo + { + class RefClass : Interface1 + { + void Interface1.M1(Interface1 c1){} + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - interface Interface1 - { - void M1(Interface1 c1); - } -}"; + """ + namespace A.B.C + { + interface Interface1 + { + void M1(Interface1 c1); + } + } + """; var expectedSourceReference = -@" -using A.B.C; - -namespace Foo -{ - class RefClass : Interface1 - { - void Interface1.M1(Interface1 c1){} - } -}"; + """ + using A.B.C; + + namespace Foo + { + class RefClass : Interface1 + { + void Interface1.M1(Interface1 c1){} + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1356,45 +1403,48 @@ public async Task ChangeFromGlobalNamespace_ReferencingQualifiedTypesDeclaredInO var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -class [||]Class1 -{{ - private A.Class2 c2; - private A.B.Class3 c3; - private A.B.C.Class4 c4; -}} - - -namespace A -{{ - class Class2 {{}} - - namespace B - {{ - class Class3 {{}} - - namespace C - {{ - class Class4 {{}} - }} - }} -}} - -"; + $$""" + + + + class [||]Class1 + { + private A.Class2 c2; + private A.B.Class3 c3; + private A.B.C.Class4 c4; + } + + + namespace A + { + class Class2 {} + + namespace B + { + class Class3 {} + + namespace C + { + class Class4 {} + } + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - class Class1 - { - private Class2 c2; - private Class3 c3; - private Class4 c4; - } -}"; + """ + namespace A.B.C + { + class Class1 + { + private Class2 c2; + private Class3 c3; + private Class4 c4; + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal); } @@ -1406,65 +1456,67 @@ public async Task ChangeFromGlobalNamespace_ChangeUsingsInMultipleContainers() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -class [||]Class1 -{{ -}} - -namespace NS1 -{{ - using System; - - class Class2 - {{ - Class1 c2; - }} - - namespace NS2 - {{ - using System; - - class Class2 - {{ - Class1 c1; - }} - }} -}} - -"; + $$""" + + + class [||]Class1 + { + } + namespace NS1 + { + using System; + + class Class2 + { + Class1 c2; + } + + namespace NS2 + { + using System; + + class Class2 + { + Class1 c1; + } + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - class Class1 - { - } -}"; + """ + namespace A.B.C + { + class Class1 + { + } + } + """; var expectedSourceReference = -@" -namespace NS1 -{ - using System; - using A.B.C; - - class Class2 - { - Class1 c2; - } - - namespace NS2 - { - using System; - - class Class2 - { - Class1 c1; - } - } -}"; + """ + namespace NS1 + { + using System; + using A.B.C; + + class Class2 + { + Class1 c2; + } + + namespace NS2 + { + using System; + + class Class2 + { + Class1 c1; + } + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1476,67 +1528,69 @@ public async Task ChangeFromGlobalNamespace_WithAliasReferencesInOtherDocument() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -class [||]Class1 -{{ -}} - -class Class2 -{{ -}} - -using Class1Alias = Class1; - -namespace Foo -{{ - using System; - - class RefClass - {{ - private Class1Alias c1; - - void M1() - {{ - Class2 c2 = null; - }} - }} -}} - -"; + $$""" + + + class [||]Class1 + { + } + + class Class2 + { + } + using Class1Alias = Class1; + + namespace Foo + { + using System; + + class RefClass + { + private Class1Alias c1; + + void M1() + { + Class2 c2 = null; + } + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - class Class1 - { - } - - class Class2 - { - } -}"; + """ + namespace A.B.C + { + class Class1 + { + } + + class Class2 + { + } + } + """; var expectedSourceReference = -@" -using A.B.C; -using Class1Alias = Class1; - -namespace Foo -{ - using System; - - class RefClass - { - private Class1Alias c1; - - void M1() - { - Class2 c2 = null; - } - } -}"; + """ + using A.B.C; + using Class1Alias = Class1; + + namespace Foo + { + using System; + + class RefClass + { + private Class1Alias c1; + + void M1() + { + Class2 c2 = null; + } + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1548,41 +1602,43 @@ public async Task ChangeNamespace_WithReferencesInVBDocument() var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - public class Class1 - {{ - }} -}} - - - -Imports {declaredNamespace} - -Public Class VBClass - Public ReadOnly Property C1 As Class1 -End Class - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + public class Class1 + { + } + } + + + Imports {{declaredNamespace}} + + Public Class VBClass + Public ReadOnly Property C1 As Class1 + End Class + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - public class Class1 - { - } -}"; + """ + namespace A.B.C + { + public class Class1 + { + } + } + """; var expectedSourceReference = -@" -Imports A.B.C + """ + Imports A.B.C -Public Class VBClass - Public ReadOnly Property C1 As Class1 -End Class"; + Public Class VBClass + Public ReadOnly Property C1 As Class1 + End Class + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1594,36 +1650,41 @@ public async Task ChangeNamespace_WithQualifiedReferencesInVBDocument() var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - public class Class1 - {{ - }} -}} - - - -Public Class VBClass - Public ReadOnly Property C1 As A.B.C.D.Class1 -End Class - -"; + $$""" + + + + namespace [||]{{declaredNamespace}} + { + public class Class1 + { + } + } + + + + Public Class VBClass + Public ReadOnly Property C1 As A.B.C.D.Class1 + End Class + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - public class Class1 - { - } -}"; + """ + namespace A.B.C + { + public class Class1 + { + } + } + """; var expectedSourceReference = -@"Public Class VBClass - Public ReadOnly Property C1 As A.B.C.Class1 -End Class"; + """ + Public Class VBClass + Public ReadOnly Property C1 As A.B.C.Class1 + End Class + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1634,37 +1695,39 @@ public async Task ChangeFromGlobalNamespace_WithReferencesInVBDocument() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var code = -$@" - - - -public class [||]Class1 -{{ -}} - - - - -Public Class VBClass - Public ReadOnly Property C1 As Class1 -End Class - -"; + $$""" + + + public class [||]Class1 + { + } + + + + Public Class VBClass + Public ReadOnly Property C1 As Class1 + End Class + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - public class Class1 - { - } -}"; + """ + namespace A.B.C + { + public class Class1 + { + } + } + """; var expectedSourceReference = -@" -Imports A.B.C + """ + Imports A.B.C -Public Class VBClass - Public ReadOnly Property C1 As Class1 -End Class"; + Public Class VBClass + Public ReadOnly Property C1 As Class1 + End Class + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1675,53 +1738,55 @@ public async Task ChangeFromGlobalNamespace_WithCredReferences() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -/// <summary> -/// See <see cref=""Class1""/> -/// </summary> -class [||]Class1 -{{ -}} - -namespace Foo -{{ - /// <summary> - /// See <see cref=""Class1""/> - /// </summary> - class Bar - {{ - }} -}} - -"; + $$""" + + + /// <summary> + /// See <see cref="Class1"/> + /// </summary> + class [||]Class1 + { + } + namespace Foo + { + /// <summary> + /// See <see cref="Class1"/> + /// </summary> + class Bar + { + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - - /// - /// See - /// - class Class1 - { - } -}"; + """ + namespace A.B.C + { + + /// + /// See + /// + class Class1 + { + } + } + """; var expectedSourceReference = -@" -using A.B.C; - -namespace Foo -{ - /// - /// See - /// - class Bar - { - } -}"; + """ + using A.B.C; + + namespace Foo + { + /// + /// See + /// + class Bar + { + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1733,36 +1798,39 @@ public async Task ChangeToGlobalNamespace_WithReferencesInVBDocument() var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - public class Class1 - {{ - }} -}} - - - -Imports {declaredNamespace} - -Public Class VBClass - Public ReadOnly Property C1 As Class1 -End Class - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + public class Class1 + { + } + } + + + Imports {{declaredNamespace}} + + Public Class VBClass + Public ReadOnly Property C1 As Class1 + End Class + + + """; var expectedSourceOriginal = -@"public class Class1 -{ -} -"; + """ + public class Class1 + { + } + + """; var expectedSourceReference = -@"Public Class VBClass - Public ReadOnly Property C1 As Class1 -End Class"; + """ + Public Class VBClass + Public ReadOnly Property C1 As Class1 + End Class + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1774,44 +1842,47 @@ public async Task ChangeToGlobalNamespace_WithReferenceAndConflictDeclarationInV var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - public class MyClass - {{ - }} -}} - - - -Namespace Foo - Public Class VBClass - Public ReadOnly Property C1 As Foo.Bar.Baz.MyClass - End Class - - Public Class MyClass - End Class -End Namespace - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + public class MyClass + { + } + } + + + Namespace Foo + Public Class VBClass + Public ReadOnly Property C1 As Foo.Bar.Baz.MyClass + End Class + + Public Class MyClass + End Class + End Namespace + + + """; var expectedSourceOriginal = -@"public class MyClass -{ -} -"; + """ + public class MyClass + { + } + + """; var expectedSourceReference = -@"Namespace Foo - Public Class VBClass - Public ReadOnly Property C1 As Global.MyClass - End Class - - Public Class MyClass - End Class -End Namespace"; + """ + Namespace Foo + Public Class VBClass + Public ReadOnly Property C1 As Global.MyClass + End Class + + Public Class MyClass + End Class + End Namespace + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1824,57 +1895,59 @@ public async Task ChangeToGlobalNamespace_WithCredReferences() var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - /// <summary> - /// See <see cref=""Class1""/> - /// See <see cref=""{declaredNamespace}.Class1""/> - /// </summary> - public class Class1 - {{ - }} -}} - -namespace Foo -{{ - using {declaredNamespace}; - - /// <summary> - /// See <see cref=""Class1""/> - /// See <see cref=""{declaredNamespace}.Class1""/> - /// </summary> - class RefClass - {{ - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + /// <summary> + /// See <see cref="Class1"/> + /// See <see cref="{{declaredNamespace}}.Class1"/> + /// </summary> + public class Class1 + { + } + } + namespace Foo + { + using {{declaredNamespace}}; + + /// <summary> + /// See <see cref="Class1"/> + /// See <see cref="{{declaredNamespace}}.Class1"/> + /// </summary> + class RefClass + { + } + } + + + """; var expectedSourceOriginal = -@"/// -/// See -/// See -/// -public class Class1 -{ -} -"; + """ + /// + /// See + /// See + /// + public class Class1 + { + } + + """; var expectedSourceReference = -@" -namespace Foo -{ - /// - /// See - /// See - /// - class RefClass - { - } -}"; + """ + namespace Foo + { + /// + /// See + /// See + /// + class RefClass + { + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1886,50 +1959,52 @@ public async Task ChangeNamespace_ExtensionMethodInReducedForm() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{defaultNamespace} -{{ - public static class Extensions - {{ - public static bool Foo(this Class1 c1) => true; - }} -}} - -namespace {defaultNamespace} -{{ - using System; - - public class Class1 - {{ - public bool Bar(Class1 c1) => c1.Foo(); - }} -}} - -"; + $$""" + + + namespace [||]{{defaultNamespace}} + { + public static class Extensions + { + public static bool Foo(this Class1 c1) => true; + } + } + namespace {{defaultNamespace}} + { + using System; + + public class Class1 + { + public bool Bar(Class1 c1) => c1.Foo(); + } + } + + + """; var expectedSourceOriginal = -$@"namespace A.B.C -{{ - public static class Extensions - {{ - public static bool Foo(this Class1 c1) => true; - }} -}}"; + $$""" + namespace A.B.C + { + public static class Extensions + { + public static bool Foo(this Class1 c1) => true; + } + } + """; var expectedSourceReference = -$@" -namespace {defaultNamespace} -{{ - using System; - using A.B.C; - - public class Class1 - {{ - public bool Bar(Class1 c1) => c1.Foo(); - }} -}}"; + $$""" + namespace {{defaultNamespace}} + { + using System; + using A.B.C; + + public class Class1 + { + public bool Bar(Class1 c1) => c1.Foo(); + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1941,50 +2016,52 @@ public async Task ChangeNamespace_ExternsionMethodInRegularForm() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]A -{{ - public static class Extensions - {{ - public static bool Foo(this Class1 c1) => true; - }} -}} - -using System; - -namespace A -{{ - public class Class1 - {{ - public bool Bar(Class1 c1) => Extensions.Foo(c1); - }} -}} - -"; + $$""" + + + namespace [||]A + { + public static class Extensions + { + public static bool Foo(this Class1 c1) => true; + } + } + using System; + + namespace A + { + public class Class1 + { + public bool Bar(Class1 c1) => Extensions.Foo(c1); + } + } + + + """; var expectedSourceOriginal = -$@"namespace A.B.C -{{ - public static class Extensions - {{ - public static bool Foo(this Class1 c1) => true; - }} -}}"; + $$""" + namespace A.B.C + { + public static class Extensions + { + public static bool Foo(this Class1 c1) => true; + } + } + """; var expectedSourceReference = -$@" -using System; -using A.B.C; - -namespace A -{{ - public class Class1 - {{ - public bool Bar(Class1 c1) => Extensions.Foo(c1); - }} -}}"; + $$""" + using System; + using A.B.C; + + namespace A + { + public class Class1 + { + public bool Bar(Class1 c1) => Extensions.Foo(c1); + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1996,56 +2073,58 @@ public async Task ChangeNamespace_ContainsBothTypeAndExternsionMethod() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]A -{{ - public static class Extensions - {{ - public static bool Foo(this Class1 c1) => true; - }} - - public class Class2 - {{ }} -}} - -using System; - -namespace A -{{ - public class Class1 - {{ - public bool Bar(Class1 c1, Class2 c2) => c2 == null ? c1.Foo() : true; - }} -}} - -"; + $$""" + + + namespace [||]A + { + public static class Extensions + { + public static bool Foo(this Class1 c1) => true; + } + + public class Class2 + { } + } + using System; + + namespace A + { + public class Class1 + { + public bool Bar(Class1 c1, Class2 c2) => c2 == null ? c1.Foo() : true; + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - public static class Extensions - { - public static bool Foo(this Class1 c1) => true; - } - - public class Class2 - { } -}"; + """ + namespace A.B.C + { + public static class Extensions + { + public static bool Foo(this Class1 c1) => true; + } + + public class Class2 + { } + } + """; var expectedSourceReference = -@" -using System; -using A.B.C; - -namespace A -{ - public class Class1 - { - public bool Bar(Class1 c1, Class2 c2) => c2 == null ? c1.Foo() : true; - } -}"; + """ + using System; + using A.B.C; + + namespace A + { + public class Class1 + { + public bool Bar(Class1 c1, Class2 c2) => c2 == null ? c1.Foo() : true; + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -2057,52 +2136,53 @@ public async Task ChangeNamespace_WithExtensionMethodReferencesInVBDocument() var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var code = -$@" - - - -using System; - -namespace [||]{declaredNamespace} -{{ - public static class Extensions - {{ - public static bool Foo(this String s) => true; - }} -}} - - - -Imports {declaredNamespace} - -Public Class VBClass - Public Function Foo(s As string) As Boolean - Return s.Foo() - End Function -End Class - -"; + $$""" + + + using System; + + namespace [||]{{declaredNamespace}} + { + public static class Extensions + { + public static bool Foo(this String s) => true; + } + } + + + Imports {{declaredNamespace}} + + Public Class VBClass + Public Function Foo(s As string) As Boolean + Return s.Foo() + End Function + End Class + + + """; var expectedSourceOriginal = -$@" -using System; - -namespace {defaultNamespace} -{{ - public static class Extensions - {{ - public static bool Foo(this string s) => true; - }} -}}"; + $$""" + using System; + + namespace {{defaultNamespace}} + { + public static class Extensions + { + public static bool Foo(this string s) => true; + } + } + """; var expectedSourceReference = -$@" -Imports {defaultNamespace} - -Public Class VBClass - Public Function Foo(s As string) As Boolean - Return s.Foo() - End Function -End Class"; + $""" + Imports {defaultNamespace} + + Public Class VBClass + Public Function Foo(s As string) As Boolean + Return s.Foo() + End Function + End Class + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -2115,57 +2195,59 @@ public async Task ChangeNamespace_WithMemberAccessReferencesInOtherDocument() var documentPath1 = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - enum Enum1 - {{ - A, - B, - C - }} -}} - -namespace Foo -{{ - class RefClass - {{ - Enum1 M1() - {{ - return {declaredNamespace}.Enum1.A; - }} - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + enum Enum1 + { + A, + B, + C + } + } + namespace Foo + { + class RefClass + { + Enum1 M1() + { + return {{declaredNamespace}}.Enum1.A; + } + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - enum Enum1 - { - A, - B, - C - } -}"; + """ + namespace A.B.C + { + enum Enum1 + { + A, + B, + C + } + } + """; var expectedSourceReference = -@" -using A.B.C; - -namespace Foo -{ - class RefClass - { - Enum1 M1() - { - return Enum1.A; - } - } -}"; + """ + using A.B.C; + + namespace Foo + { + class RefClass + { + Enum1 M1() + { + return Enum1.A; + } + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -2178,52 +2260,55 @@ public async Task ChangeToGlobalNamespace_WithMemberAccessReferencesInOtherDocum var documentPath1 = CreateDocumentFilePath([], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - enum Enum1 - {{ - A, - B, - C - }} -}} - -namespace Foo -{{ - class RefClass - {{ - Enum1 M1() - {{ - return {declaredNamespace}.Enum1.A; - }} - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + enum Enum1 + { + A, + B, + C + } + } + namespace Foo + { + class RefClass + { + Enum1 M1() + { + return {{declaredNamespace}}.Enum1.A; + } + } + } + + + """; var expectedSourceOriginal = -@"enum Enum1 -{ - A, - B, - C -} -"; + """ + enum Enum1 + { + A, + B, + C + } + + """; var expectedSourceReference = -@"namespace Foo -{ - class RefClass - { - Enum1 M1() - { - return Enum1.A; - } - } -}"; + """ + namespace Foo + { + class RefClass + { + Enum1 M1() + { + return Enum1.A; + } + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -2235,46 +2320,51 @@ public async Task ChangeNamespace_WithMemberAccessReferencesInVBDocument() var documentPath1 = CreateDocumentFilePath([], "File1.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - public enum Enum1 - {{ - A, - B, - C - }} -}} - - - -Public Class VBClass - Sub M() - Dim x = A.B.C.D.Enum1.A - End Sub -End Class - -"; + $$""" + + + + namespace [||]{{declaredNamespace}} + { + public enum Enum1 + { + A, + B, + C + } + } + + + + Public Class VBClass + Sub M() + Dim x = A.B.C.D.Enum1.A + End Sub + End Class + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - public enum Enum1 - { - A, - B, - C - } -}"; + """ + namespace A.B.C + { + public enum Enum1 + { + A, + B, + C + } + } + """; var expectedSourceReference = -@"Public Class VBClass - Sub M() - Dim x = A.B.C.Enum1.A - End Sub -End Class"; + """ + Public Class VBClass + Sub M() + Dim x = A.B.C.Enum1.A + End Sub + End Class + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -2286,44 +2376,47 @@ public async Task ChangeToGlobalNamespace_WithMemberAccessReferencesInVBDocument var documentPath1 = CreateDocumentFilePath([], "File1.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - public enum Enum1 - {{ - A, - B, - C - }} -}} - - - -Public Class VBClass - Sub M() - Dim x = A.B.C.D.Enum1.A - End Sub -End Class - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + public enum Enum1 + { + A, + B, + C + } + } + + + Public Class VBClass + Sub M() + Dim x = A.B.C.D.Enum1.A + End Sub + End Class + + + """; var expectedSourceOriginal = -@"public enum Enum1 -{ - A, - B, - C -} -"; + """ + public enum Enum1 + { + A, + B, + C + } + + """; var expectedSourceReference = -@"Public Class VBClass - Sub M() - Dim x = Enum1.A - End Sub -End Class"; + """ + Public Class VBClass + Sub M() + Dim x = Enum1.A + End Sub + End Class + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } From 36554b31cc8a361bf75a7493f4d3caa9756c7c15 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 22 Nov 2024 18:11:47 -0800 Subject: [PATCH 506/508] Fix --- .../CodeActions/MoveType/MoveTypeTests.MoveToNewFile.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.MoveToNewFile.cs b/src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.MoveToNewFile.cs index 8461a149443d3..2274838ef545f 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.MoveToNewFile.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.MoveToNewFile.cs @@ -301,7 +301,7 @@ public async Task MoveTypeWithWithContainerNamespace() @"namespace N1 { [||]class Class1 { } - class Class2 { } + class Class2 { } }"; var codeAfterMove = From 3ffa7c77315546465640e86d2c5adc34d01ca981 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 22 Nov 2024 18:13:11 -0800 Subject: [PATCH 507/508] Use raw strings in tests --- .../MoveType/MoveTypeTests.MoveToNewFile.cs | 2326 +++++++++-------- 1 file changed, 1295 insertions(+), 1031 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.MoveToNewFile.cs b/src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.MoveToNewFile.cs index 2274838ef545f..12cfecdc623d2 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.MoveToNewFile.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.MoveToNewFile.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Collections.Immutable; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Formatting; @@ -14,13 +12,13 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeActions.MoveType; [Trait(Traits.Feature, Traits.Features.CodeActionsMoveType)] -public partial class MoveTypeTests : CSharpMoveTypeTestsBase +public sealed partial class MoveTypeTests : CSharpMoveTypeTestsBase { [WpfFact] public async Task TestMissing_OnMatchingFileName() { var code = -@"[||]class test1 { }"; + @"[||]class test1 { }"; await TestMissingInRegularAndScriptAsync(code); } @@ -29,10 +27,12 @@ public async Task TestMissing_OnMatchingFileName() public async Task TestMissing_Nested_OnMatchingFileName_Simple() { var code = -@"class outer -{ - [||]class test1 { } -}"; + """ + class outer + { + [||]class test1 { } + } + """; await TestMissingInRegularAndScriptAsync(code); } @@ -41,7 +41,7 @@ public async Task TestMissing_Nested_OnMatchingFileName_Simple() public async Task TestMatchingFileName_CaseSensitive() { var code = -@"[||]class Test1 { }"; + @"[||]class Test1 { }"; await TestActionCountAsync(code, count: 2); } @@ -50,8 +50,10 @@ public async Task TestMatchingFileName_CaseSensitive() public async Task TestForSpans1() { var code = -@"[|clas|]s Class1 { } - class Class2 { }"; + """ + [|clas|]s Class1 { } + class Class2 { } + """; await TestActionCountAsync(code, count: 3); } @@ -60,13 +62,17 @@ class Class2 { }"; public async Task TestForSpans2() { var code = -@"[||]class Class1 { } - class Class2 { }"; + """ + [||]class Class1 { } + class Class2 { } + """; var codeAfterMove = @"class Class2 { }"; var expectedDocumentName = "Class1.cs"; - var destinationDocumentText = @"class Class1 { } -"; + var destinationDocumentText = """ + class Class1 { } + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -75,21 +81,27 @@ class Class2 { }"; public async Task TestMoveToNewFileWithFolders() { var code = -@" - - - -[||]class Class1 { } -class Class2 { } - - -"; - var codeAfterMove = @"class Class2 { } - "; + """ + + + + + [||]class Class1 { } + class Class2 { } + + + + """; + var codeAfterMove = """ + class Class2 { } + + """; var expectedDocumentName = "Class1.cs"; - var destinationDocumentText = @"class Class1 { } - "; + var destinationDocumentText = """ + class Class1 { } + + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, @@ -100,8 +112,10 @@ await TestMoveTypeToNewFileAsync( public async Task TestForSpans3() { var code = -@"[|class Class1|] { } -class Class2 { }"; + """ + [|class Class1|] { } + class Class2 { } + """; await TestActionCountAsync(code, count: 3); } @@ -110,13 +124,17 @@ class Class2 { }"; public async Task TestForSpans4() { var code = -@"class Class1[||] { } -class Class2 { }"; + """ + class Class1[||] { } + class Class2 { } + """; var codeAfterMove = @"class Class2 { }"; var expectedDocumentName = "Class1.cs"; - var destinationDocumentText = @"class Class1 { } -"; + var destinationDocumentText = """ + class Class1 { } + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -125,13 +143,17 @@ class Class2 { }"; public async Task MoveTypeWithNoContainerNamespace() { var code = -@"[||]class Class1 { } -class Class2 { }"; + """ + [||]class Class1 { } + class Class2 { } + """; var codeAfterMove = @"class Class2 { }"; var expectedDocumentName = "Class1.cs"; - var destinationDocumentText = @"class Class1 { } -"; + var destinationDocumentText = """ + class Class1 { } + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -140,22 +162,28 @@ class Class2 { }"; public async Task MoveTypeWithWithUsingsAndNoContainerNamespace() { var code = -@"// Banner Text -using System; + """ + // Banner Text + using System; -[||]class Class1 { } -class Class2 { }"; + [||]class Class1 { } + class Class2 { } + """; var codeAfterMove = -@"// Banner Text -using System; -class Class2 { }"; + """ + // Banner Text + using System; + class Class2 { } + """; var expectedDocumentName = "Class1.cs"; var destinationDocumentText = -@"// Banner Text -class Class1 { } -"; + """ + // Banner Text + class Class1 { } + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -164,35 +192,41 @@ class Class1 { } public async Task MoveTypeWithWithMembers() { var code = -@"// Banner Text -using System; + """ + // Banner Text + using System; -[||]class Class1 -{ - void Print(int x) - { - Console.WriteLine(x); - } -} -class Class2 { }"; + [||]class Class1 + { + void Print(int x) + { + Console.WriteLine(x); + } + } + class Class2 { } + """; var codeAfterMove = -@"// Banner Text -class Class2 { }"; + """ + // Banner Text + class Class2 { } + """; var expectedDocumentName = "Class1.cs"; var destinationDocumentText = -@"// Banner Text -using System; + """ + // Banner Text + using System; -class Class1 -{ - void Print(int x) - { - Console.WriteLine(x); - } -} -"; + class Class1 + { + void Print(int x) + { + Console.WriteLine(x); + } + } + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -201,50 +235,56 @@ void Print(int x) public async Task MoveTypeWithWithMembers2() { var code = -@"// Banner Text -using System; + """ + // Banner Text + using System; -[||]class Class1 -{ - void Print(int x) - { - Console.WriteLine(x); - } -} + [||]class Class1 + { + void Print(int x) + { + Console.WriteLine(x); + } + } -class Class2 -{ - void Print(int x) - { - Console.WriteLine(x); - } -}"; + class Class2 + { + void Print(int x) + { + Console.WriteLine(x); + } + } + """; var codeAfterMove = -@"// Banner Text -using System; + """ + // Banner Text + using System; -class Class2 -{ - void Print(int x) - { - Console.WriteLine(x); - } -}"; + class Class2 + { + void Print(int x) + { + Console.WriteLine(x); + } + } + """; var expectedDocumentName = "Class1.cs"; var destinationDocumentText = -@"// Banner Text -using System; + """ + // Banner Text + using System; -class Class1 -{ - void Print(int x) - { - Console.WriteLine(x); - } -} -"; + class Class1 + { + void Print(int x) + { + Console.WriteLine(x); + } + } + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -253,13 +293,17 @@ void Print(int x) public async Task MoveAnInterface() { var code = -@"[||]interface IMoveType { } -class Class2 { }"; + """ + [||]interface IMoveType { } + class Class2 { } + """; var codeAfterMove = @"class Class2 { }"; var expectedDocumentName = "IMoveType.cs"; - var destinationDocumentText = @"interface IMoveType { } -"; + var destinationDocumentText = """ + interface IMoveType { } + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -268,13 +312,17 @@ class Class2 { }"; public async Task MoveAStruct() { var code = -@"[||]struct MyStruct { } -class Class2 { }"; + """ + [||]struct MyStruct { } + class Class2 { } + """; var codeAfterMove = @"class Class2 { }"; var expectedDocumentName = "MyStruct.cs"; - var destinationDocumentText = @"struct MyStruct { } -"; + var destinationDocumentText = """ + struct MyStruct { } + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -283,13 +331,17 @@ class Class2 { }"; public async Task MoveAnEnum() { var code = -@"[||]enum MyEnum { } -class Class2 { }"; + """ + [||]enum MyEnum { } + class Class2 { } + """; var codeAfterMove = @"class Class2 { }"; var expectedDocumentName = "MyEnum.cs"; - var destinationDocumentText = @"enum MyEnum { } -"; + var destinationDocumentText = """ + enum MyEnum { } + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -298,25 +350,31 @@ class Class2 { }"; public async Task MoveTypeWithWithContainerNamespace() { var code = -@"namespace N1 -{ - [||]class Class1 { } - class Class2 { } -}"; + """ + namespace N1 + { + [||]class Class1 { } + class Class2 { } + } + """; var codeAfterMove = -@"namespace N1 -{ - class Class2 { } -}"; + """ + namespace N1 + { + class Class2 { } + } + """; var expectedDocumentName = "Class1.cs"; var destinationDocumentText = -@"namespace N1 -{ - class Class1 { } -}"; + """ + namespace N1 + { + class Class1 { } + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -324,24 +382,30 @@ class Class1 { } public async Task MoveTypeWithWithFileScopedNamespace() { var code = -@"namespace N1; + """ + namespace N1; -[||]class Class1 { } -class Class2 { } -"; + [||]class Class1 { } + class Class2 { } + + """; var codeAfterMove = -@"namespace N1; -class Class2 { } -"; + """ + namespace N1; + class Class2 { } + + """; var expectedDocumentName = "Class1.cs"; var destinationDocumentText = -@"namespace N1; + """ + namespace N1; -class Class1 { } -"; + class Class1 { } + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -349,35 +413,41 @@ class Class1 { } public async Task MoveNestedTypeToNewFile_Simple() { var code = -@"namespace N1 -{ - class Class1 - { - [||]class Class2 { } - } - -}"; + """ + namespace N1 + { + class Class1 + { + [||]class Class2 { } + } + + } + """; var codeAfterMove = -@"namespace N1 -{ - partial class Class1 - { - } - -}"; + """ + namespace N1 + { + partial class Class1 + { + } + + } + """; var expectedDocumentName = "Class2.cs"; var destinationDocumentText = -@"namespace N1 -{ - partial class Class1 - { - class Class2 { } - } - -}"; + """ + namespace N1 + { + partial class Class1 + { + class Class2 { } + } + + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -385,35 +455,41 @@ class Class2 { } public async Task MoveNestedTypePreserveModifiers() { var code = -@"namespace N1 -{ - abstract class Class1 - { - [||]class Class2 { } - } - -}"; + """ + namespace N1 + { + abstract class Class1 + { + [||]class Class2 { } + } + + } + """; var codeAfterMove = -@"namespace N1 -{ - abstract partial class Class1 - { - } - -}"; + """ + namespace N1 + { + abstract partial class Class1 + { + } + + } + """; var expectedDocumentName = "Class2.cs"; var destinationDocumentText = -@"namespace N1 -{ - abstract partial class Class1 - { - class Class2 { } - } - -}"; + """ + namespace N1 + { + abstract partial class Class1 + { + class Class2 { } + } + + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -421,39 +497,45 @@ class Class2 { } public async Task MoveNestedTypeToNewFile_Attributes1() { var code = -@"namespace N1 -{ - [Outer] - class Class1 - { - [Inner] - [||]class Class2 { } - } - -}"; + """ + namespace N1 + { + [Outer] + class Class1 + { + [Inner] + [||]class Class2 { } + } + + } + """; var codeAfterMove = -@"namespace N1 -{ - [Outer] - partial class Class1 - { - } - -}"; + """ + namespace N1 + { + [Outer] + partial class Class1 + { + } + + } + """; var expectedDocumentName = "Class2.cs"; var destinationDocumentText = -@"namespace N1 -{ - partial class Class1 - { - [Inner] - class Class2 { } - } - -}"; + """ + namespace N1 + { + partial class Class1 + { + [Inner] + class Class2 { } + } + + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -461,40 +543,46 @@ class Class2 { } public async Task MoveNestedTypeToNewFile_Comments1() { var code = -@"namespace N1 -{ - /// Outer doc comment. - class Class1 - { - /// Inner doc comment - [||]class Class2 - { - } - } -}"; + """ + namespace N1 + { + /// Outer doc comment. + class Class1 + { + /// Inner doc comment + [||]class Class2 + { + } + } + } + """; var codeAfterMove = -@"namespace N1 -{ - /// Outer doc comment. - partial class Class1 - { - } -}"; + """ + namespace N1 + { + /// Outer doc comment. + partial class Class1 + { + } + } + """; var expectedDocumentName = "Class2.cs"; var destinationDocumentText = -@"namespace N1 -{ - partial class Class1 - { - /// Inner doc comment - class Class2 - { - } - } -}"; + """ + namespace N1 + { + partial class Class1 + { + /// Inner doc comment + class Class2 + { + } + } + } + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -503,35 +591,41 @@ await TestMoveTypeToNewFileAsync( public async Task MoveNestedTypeToNewFile_Simple_DottedName() { var code = -@"namespace N1 -{ - class Class1 - { - [||]class Class2 { } - } - -}"; + """ + namespace N1 + { + class Class1 + { + [||]class Class2 { } + } + + } + """; var codeAfterMove = -@"namespace N1 -{ - partial class Class1 - { - } - -}"; + """ + namespace N1 + { + partial class Class1 + { + } + + } + """; var expectedDocumentName = "Class1.Class2.cs"; var destinationDocumentText = -@"namespace N1 -{ - partial class Class1 - { - class Class2 { } - } - -}"; + """ + namespace N1 + { + partial class Class1 + { + class Class2 { } + } + + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText, index: 1); } @@ -539,27 +633,33 @@ class Class2 { } public async Task MoveNestedTypeToNewFile_Simple_DottedName_WithPrimaryConstructor() { var code = -@"internal class Outer() -{ - private class Inner[||] - { - } -}"; + """ + internal class Outer() + { + private class Inner[||] + { + } + } + """; var codeAfterMove = -@"internal partial class Outer() -{ -}"; + """ + internal partial class Outer() + { + } + """; var expectedDocumentName = "Outer.Inner.cs"; var destinationDocumentText = -@"internal partial class Outer -{ - private class Inner - { - } -}"; + """ + internal partial class Outer + { + private class Inner + { + } + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText, index: 1); } @@ -567,42 +667,48 @@ private class Inner public async Task MoveNestedTypeToNewFile_ParentHasOtherMembers() { var code = -@"namespace N1 -{ - class Class1 - { - private int _field1; + """ + namespace N1 + { + class Class1 + { + private int _field1; - [||]class Class2 { } + [||]class Class2 { } - public void Method1() { } - } - -}"; + public void Method1() { } + } + + } + """; var codeAfterMove = -@"namespace N1 -{ - partial class Class1 - { - private int _field1; + """ + namespace N1 + { + partial class Class1 + { + private int _field1; - public void Method1() { } - } - -}"; + public void Method1() { } + } + + } + """; var expectedDocumentName = "Class2.cs"; var destinationDocumentText = -@"namespace N1 -{ - partial class Class1 - { - class Class2 { } - } - -}"; + """ + namespace N1 + { + partial class Class1 + { + class Class2 { } + } + + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -610,49 +716,55 @@ class Class2 { } public async Task MoveNestedTypeToNewFile_HasOtherTopLevelMembers() { var code = -@"namespace N1 -{ - class Class1 - { - private int _field1; + """ + namespace N1 + { + class Class1 + { + private int _field1; - [||]class Class2 { } + [||]class Class2 { } - public void Method1() { } - } + public void Method1() { } + } - internal class Class3 - { - private void Method1() { } - } -}"; + internal class Class3 + { + private void Method1() { } + } + } + """; var codeAfterMove = -@"namespace N1 -{ - partial class Class1 - { - private int _field1; + """ + namespace N1 + { + partial class Class1 + { + private int _field1; - public void Method1() { } - } + public void Method1() { } + } - internal class Class3 - { - private void Method1() { } - } -}"; + internal class Class3 + { + private void Method1() { } + } + } + """; var expectedDocumentName = "Class2.cs"; var destinationDocumentText = -@"namespace N1 -{ - partial class Class1 - { - class Class2 { } - } -}"; + """ + namespace N1 + { + partial class Class1 + { + class Class2 { } + } + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -660,161 +772,173 @@ class Class2 { } public async Task MoveNestedTypeToNewFile_HasMembers() { var code = -@"namespace N1 -{ - class Class1 - { - private int _field1; + """ + namespace N1 + { + class Class1 + { + private int _field1; - [||]class Class2 - { - private string _field1; - public void InnerMethod() { } - } + [||]class Class2 + { + private string _field1; + public void InnerMethod() { } + } - public void Method1() { } - } -}"; + public void Method1() { } + } + } + """; var codeAfterMove = -@"namespace N1 -{ - partial class Class1 - { - private int _field1; + """ + namespace N1 + { + partial class Class1 + { + private int _field1; - public void Method1() { } - } -}"; + public void Method1() { } + } + } + """; var expectedDocumentName = "Class2.cs"; var destinationDocumentText = -@"namespace N1 -{ - partial class Class1 - { - class Class2 - { - private string _field1; - public void InnerMethod() { } - } - } -}"; - await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); - } + """ + namespace N1 + { + partial class Class1 + { + class Class2 + { + private string _field1; + public void InnerMethod() { } + } + } + } + """; + await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); + } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/13969")] public async Task MoveTypeInFileWithComplexHierarchy() { var code = -@"namespace OuterN1.N1 -{ - namespace InnerN2.N2 - { - class OuterClass1 - { - class InnerClass2 + """ + namespace OuterN1.N1 { - } - } - } + namespace InnerN2.N2 + { + class OuterClass1 + { + class InnerClass2 + { + } + } + } - namespace InnerN3.N3 - { - class OuterClass2 - { - [||]class InnerClass2 - { - class InnerClass3 + namespace InnerN3.N3 { + class OuterClass2 + { + [||]class InnerClass2 + { + class InnerClass3 + { + } + } + + class InnerClass4 + { + } + } + + class OuterClass3 + { + } } } - class InnerClass4 + namespace OuterN2.N2 { + namespace InnerN3.N3 + { + class OuterClass5 { + class InnerClass6 { + } + } + } } - } - class OuterClass3 - { - } - } -} - -namespace OuterN2.N2 -{ - namespace InnerN3.N3 - { - class OuterClass5 { - class InnerClass6 { - } - } - } -} -"; + """; var codeAfterMove = -@"namespace OuterN1.N1 -{ - namespace InnerN2.N2 - { - class OuterClass1 - { - class InnerClass2 + """ + namespace OuterN1.N1 { - } - } - } + namespace InnerN2.N2 + { + class OuterClass1 + { + class InnerClass2 + { + } + } + } - namespace InnerN3.N3 - { - partial class OuterClass2 - { + namespace InnerN3.N3 + { + partial class OuterClass2 + { - class InnerClass4 - { - } - } + class InnerClass4 + { + } + } - class OuterClass3 - { - } - } -} + class OuterClass3 + { + } + } + } -namespace OuterN2.N2 -{ - namespace InnerN3.N3 - { - class OuterClass5 { - class InnerClass6 { + namespace OuterN2.N2 + { + namespace InnerN3.N3 + { + class OuterClass5 { + class InnerClass6 { + } + } + } } - } - } -} -"; + + """; var expectedDocumentName = "InnerClass2.cs"; var destinationDocumentText = -@"namespace OuterN1.N1 -{ - - namespace InnerN3.N3 - { - partial class OuterClass2 - { - class InnerClass2 + """ + namespace OuterN1.N1 { - class InnerClass3 + + namespace InnerN3.N3 { + partial class OuterClass2 + { + class InnerClass2 + { + class InnerClass3 + { + } + } + } } } - } - } -} -"; + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -822,40 +946,46 @@ class InnerClass3 public async Task MoveTypeUsings1() { var code = -@" -// Only used by inner type. -using System; + """ -// Unused by both types. -using System.Collections; + // Only used by inner type. + using System; -class Outer { - [||]class Inner { - DateTime d; - } -}"; - var codeAfterMove = @" -// Only used by inner type. + // Unused by both types. + using System.Collections; + + class Outer { + [||]class Inner { + DateTime d; + } + } + """; + var codeAfterMove = """ -// Unused by both types. -using System.Collections; + // Only used by inner type. -partial class Outer { -}"; + // Unused by both types. + using System.Collections; + + partial class Outer { + } + """; var expectedDocumentName = "Inner.cs"; var destinationDocumentText = -@" -// Only used by inner type. -using System; + """ -// Unused by both types. + // Only used by inner type. + using System; -partial class Outer { - class Inner { - DateTime d; - } -}"; + // Unused by both types. + + partial class Outer { + class Inner { + DateTime d; + } + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -864,33 +994,39 @@ class Inner { public async Task TestLeadingTrivia1() { var code = -@" -class Outer -{ - class Inner1 - { - } + """ - [||]class Inner2 - { - } -}"; - var codeAfterMove = @" -partial class Outer -{ - class Inner1 - { - } -}"; + class Outer + { + class Inner1 + { + } + + [||]class Inner2 + { + } + } + """; + var codeAfterMove = """ + + partial class Outer + { + class Inner1 + { + } + } + """; var expectedDocumentName = "Inner2.cs"; - var destinationDocumentText = @" -partial class Outer -{ - class Inner2 - { - } -}"; + var destinationDocumentText = """ + + partial class Outer + { + class Inner2 + { + } + } + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, destinationDocumentText); @@ -900,34 +1036,40 @@ await TestMoveTypeToNewFileAsync( public async Task TestInsertFinalNewLine() { var code = -@" -class Outer -{ - class Inner1 - { - } + """ - [||]class Inner2 - { - } -}"; - var codeAfterMove = @" -partial class Outer -{ - class Inner1 - { - } -}"; + class Outer + { + class Inner1 + { + } + + [||]class Inner2 + { + } + } + """; + var codeAfterMove = """ + + partial class Outer + { + class Inner1 + { + } + } + """; var expectedDocumentName = "Inner2.cs"; - var destinationDocumentText = @" -partial class Outer -{ - class Inner2 - { - } -} -"; + var destinationDocumentText = """ + + partial class Outer + { + class Inner2 + { + } + } + + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, destinationDocumentText, @@ -938,33 +1080,39 @@ await TestMoveTypeToNewFileAsync( public async Task TestInsertFinalNewLine2() { var code = -@" -class Outer -{ - class Inner1 - { - } + """ - [||]class Inner2 - { - } -}"; - var codeAfterMove = @" -partial class Outer -{ - class Inner1 - { - } -}"; + class Outer + { + class Inner1 + { + } + + [||]class Inner2 + { + } + } + """; + var codeAfterMove = """ + + partial class Outer + { + class Inner1 + { + } + } + """; var expectedDocumentName = "Inner2.cs"; - var destinationDocumentText = @" -partial class Outer -{ - class Inner2 - { - } -}"; + var destinationDocumentText = """ + + partial class Outer + { + class Inner2 + { + } + } + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, destinationDocumentText, @@ -975,26 +1123,32 @@ await TestMoveTypeToNewFileAsync( public async Task MoveTypeRemoveOuterInheritanceTypes() { var code = -@" -class Outer : IComparable { - [||]class Inner : IWhatever { - DateTime d; - } -}"; + """ + + class Outer : IComparable { + [||]class Inner : IWhatever { + DateTime d; + } + } + """; var codeAfterMove = -@" -partial class Outer : IComparable { -}"; + """ + + partial class Outer : IComparable { + } + """; var expectedDocumentName = "Inner.cs"; var destinationDocumentText = -@" -partial class Outer -{ - class Inner : IWhatever { - DateTime d; - } -}"; + """ + + partial class Outer + { + class Inner : IWhatever { + DateTime d; + } + } + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, destinationDocumentText); @@ -1004,49 +1158,55 @@ await TestMoveTypeToNewFileAsync( public async Task MoveTypeWithDirectives1() { var code = -@"using System; + """ + using System; -namespace N -{ - class Program - { - static void Main() - { - } - } -} + namespace N + { + class Program + { + static void Main() + { + } + } + } -#if true -public class [||]Inner -{ + #if true + public class [||]Inner + { -} -#endif"; + } + #endif + """; var codeAfterMove = - @"using System; + """ + using System; -namespace N -{ - class Program - { - static void Main() + namespace N { + class Program + { + static void Main() + { + } + } } - } -} -#if true -#endif"; + #if true + #endif + """; var expectedDocumentName = "Inner.cs"; var destinationDocumentText = -@" -#if true -public class Inner -{ + """ -} -#endif"; + #if true + public class Inner + { + + } + #endif + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, destinationDocumentText); @@ -1056,54 +1216,60 @@ await TestMoveTypeToNewFileAsync( public async Task MoveTypeWithDirectives2() { var code = -@"using System; + """ + using System; -namespace N -{ - class Program - { - static void Main() - { - } + namespace N + { + class Program + { + static void Main() + { + } -#if true - public class [||]Inner - { + #if true + public class [||]Inner + { - } -#endif - } -}"; + } + #endif + } + } + """; var codeAfterMove = - @"using System; + """ + using System; -namespace N -{ - partial class Program - { - static void Main() + namespace N { - } + partial class Program + { + static void Main() + { + } -#if true -#endif - } -}"; + #if true + #endif + } + } + """; var expectedDocumentName = "Inner.cs"; var destinationDocumentText = -@"namespace N -{ - partial class Program - { -#if true - public class Inner - { + """ + namespace N + { + partial class Program + { + #if true + public class Inner + { - } -#endif - } -}"; + } + #endif + } + } + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, destinationDocumentText); @@ -1113,49 +1279,55 @@ await TestMoveTypeToNewFileAsync( public async Task TestLeadingBlankLines1() { var code = -@"// Banner Text -using System; + """ + // Banner Text + using System; -[||]class Class1 -{ - void Foo() - { - Console.WriteLine(); - } -} + [||]class Class1 + { + void Foo() + { + Console.WriteLine(); + } + } -class Class2 -{ - void Foo() - { - Console.WriteLine(); - } -} -"; - var codeAfterMove = @"// Banner Text -using System; + class Class2 + { + void Foo() + { + Console.WriteLine(); + } + } -class Class2 -{ - void Foo() - { - Console.WriteLine(); - } -} -"; + """; + var codeAfterMove = """ + // Banner Text + using System; + + class Class2 + { + void Foo() + { + Console.WriteLine(); + } + } + + """; var expectedDocumentName = "Class1.cs"; - var destinationDocumentText = @"// Banner Text -using System; + var destinationDocumentText = """ + // Banner Text + using System; -class Class1 -{ - void Foo() - { - Console.WriteLine(); - } -} -"; + class Class1 + { + void Foo() + { + Console.WriteLine(); + } + } + + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, destinationDocumentText); @@ -1165,49 +1337,55 @@ await TestMoveTypeToNewFileAsync( public async Task TestLeadingBlankLines2() { var code = -@"// Banner Text -using System; + """ + // Banner Text + using System; -class Class1 -{ - void Foo() - { - Console.WriteLine(); - } -} + class Class1 + { + void Foo() + { + Console.WriteLine(); + } + } -[||]class Class2 -{ - void Foo() - { - Console.WriteLine(); - } -} -"; - var codeAfterMove = @"// Banner Text -using System; + [||]class Class2 + { + void Foo() + { + Console.WriteLine(); + } + } -class Class1 -{ - void Foo() - { - Console.WriteLine(); - } -} -"; + """; + var codeAfterMove = """ + // Banner Text + using System; + + class Class1 + { + void Foo() + { + Console.WriteLine(); + } + } + + """; var expectedDocumentName = "Class2.cs"; - var destinationDocumentText = @"// Banner Text -using System; + var destinationDocumentText = """ + // Banner Text + using System; -class Class2 -{ - void Foo() - { - Console.WriteLine(); - } -} -"; + class Class2 + { + void Foo() + { + Console.WriteLine(); + } + } + + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, destinationDocumentText); @@ -1217,49 +1395,55 @@ await TestMoveTypeToNewFileAsync( public async Task TestLeadingCommentInContainer() { var code = -@"// Banner Text -using System; + """ + // Banner Text + using System; + + class Class1 + // Leading comment + { + class [||]Class2 + { + } -class Class1 -// Leading comment -{ - class [||]Class2 - { - } + void Foo() + { + Console.WriteLine(); + } - void Foo() - { - Console.WriteLine(); - } + public int I() => 5; + } - public int I() => 5; -} -"; - var codeAfterMove = @"// Banner Text -using System; + """; + var codeAfterMove = """ + // Banner Text + using System; -partial class Class1 -// Leading comment -{ + partial class Class1 + // Leading comment + { - void Foo() - { - Console.WriteLine(); - } + void Foo() + { + Console.WriteLine(); + } - public int I() => 5; -} -"; + public int I() => 5; + } + + """; var expectedDocumentName = "Class2.cs"; - var destinationDocumentText = @"// Banner Text -partial class Class1 -{ - class Class2 - { - } -} -"; + var destinationDocumentText = """ + // Banner Text + partial class Class1 + { + class Class2 + { + } + } + + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, destinationDocumentText); @@ -1269,47 +1453,53 @@ await TestMoveTypeToNewFileAsync( public async Task TestLeadingCommentInContainer2() { var code = -@"// Banner Text -using System; + """ + // Banner Text + using System; -class Class1 -{ // Leading comment - class [||]Class2 - { - } + class Class1 + { // Leading comment + class [||]Class2 + { + } - void Foo() - { - Console.WriteLine(); - } + void Foo() + { + Console.WriteLine(); + } - public int I() => 5; -} -"; - var codeAfterMove = @"// Banner Text -using System; + public int I() => 5; + } -partial class Class1 -{ // Leading comment + """; + var codeAfterMove = """ + // Banner Text + using System; - void Foo() - { - Console.WriteLine(); - } + partial class Class1 + { // Leading comment - public int I() => 5; -} -"; + void Foo() + { + Console.WriteLine(); + } + + public int I() => 5; + } + + """; var expectedDocumentName = "Class2.cs"; - var destinationDocumentText = @"// Banner Text -partial class Class1 -{ - class Class2 - { - } -} -"; + var destinationDocumentText = """ + // Banner Text + partial class Class1 + { + class Class2 + { + } + } + + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, destinationDocumentText); @@ -1319,49 +1509,55 @@ await TestMoveTypeToNewFileAsync( public async Task TestTrailingCommentInContainer() { var code = -@"// Banner Text -using System; + """ + // Banner Text + using System; -class Class1 -{ - class [||]Class2 - { - } + class Class1 + { + class [||]Class2 + { + } - void Foo() - { - Console.WriteLine(); - } + void Foo() + { + Console.WriteLine(); + } - public int I() => 5; - // End of class document -} -"; - var codeAfterMove = @"// Banner Text -using System; + public int I() => 5; + // End of class document + } -partial class Class1 -{ + """; + var codeAfterMove = """ + // Banner Text + using System; - void Foo() - { - Console.WriteLine(); - } + partial class Class1 + { - public int I() => 5; - // End of class document -} -"; + void Foo() + { + Console.WriteLine(); + } + + public int I() => 5; + // End of class document + } + + """; var expectedDocumentName = "Class2.cs"; - var destinationDocumentText = @"// Banner Text -partial class Class1 -{ - class Class2 - { - } -} -"; + var destinationDocumentText = """ + // Banner Text + partial class Class1 + { + class Class2 + { + } + } + + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, destinationDocumentText); @@ -1371,46 +1567,52 @@ await TestMoveTypeToNewFileAsync( public async Task TestTrailingCommentInContainer2() { var code = -@"// Banner Text -using System; + """ + // Banner Text + using System; -class Class1 -{ - class [||]Class2 - { - } + class Class1 + { + class [||]Class2 + { + } - void Foo() - { - Console.WriteLine(); - } + void Foo() + { + Console.WriteLine(); + } - public int I() => 5; -} // End of class document -"; - var codeAfterMove = @"// Banner Text -using System; + public int I() => 5; + } // End of class document -partial class Class1 -{ + """; + var codeAfterMove = """ + // Banner Text + using System; - void Foo() - { - Console.WriteLine(); - } + partial class Class1 + { + + void Foo() + { + Console.WriteLine(); + } + + public int I() => 5; + } // End of class document - public int I() => 5; -} // End of class document -"; + """; var expectedDocumentName = "Class2.cs"; - var destinationDocumentText = @"// Banner Text -partial class Class1 -{ - class Class2 - { - } -}"; + var destinationDocumentText = """ + // Banner Text + partial class Class1 + { + class Class2 + { + } + } + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, destinationDocumentText); @@ -1420,18 +1622,22 @@ await TestMoveTypeToNewFileAsync( public async Task MoveRecordToNewFilePreserveUsings() { var code = -@"using System; + """ + using System; -[||]record CacheContext(String Message); + [||]record CacheContext(String Message); -class Program { }"; + class Program { } + """; var codeAfterMove = @"class Program { }"; var expectedDocumentName = "CacheContext.cs"; - var destinationDocumentText = @"using System; + var destinationDocumentText = """ + using System; + + record CacheContext(String Message); -record CacheContext(String Message); -"; + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -1440,18 +1646,22 @@ record CacheContext(String Message); public async Task MoveClassToNewFilePreserveUsings_PrimaryConstructor() { var code = -@"using System; + """ + using System; -[||]class CacheContext(String Message); + [||]class CacheContext(String Message); -class Program { }"; + class Program { } + """; var codeAfterMove = @"class Program { }"; var expectedDocumentName = "CacheContext.cs"; - var destinationDocumentText = @"using System; + var destinationDocumentText = """ + using System; -class CacheContext(String Message); -"; + class CacheContext(String Message); + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -1460,18 +1670,22 @@ class CacheContext(String Message); public async Task MoveStructToNewFilePreserveUsings_PrimaryConstructor() { var code = -@"using System; + """ + using System; -[||]struct CacheContext(String Message); + [||]struct CacheContext(String Message); -class Program { }"; + class Program { } + """; var codeAfterMove = @"class Program { }"; var expectedDocumentName = "CacheContext.cs"; - var destinationDocumentText = @"using System; + var destinationDocumentText = """ + using System; + + struct CacheContext(String Message); -struct CacheContext(String Message); -"; + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -1480,18 +1694,24 @@ struct CacheContext(String Message); public async Task MoveInterfaceToNewFilePreserveUsings_PrimaryConstructor() { var code = -@"using System; + """ + using System; -[||]interface CacheContext(String Message); + [||]interface CacheContext(String Message); -class Program { }"; - var codeAfterMove = @"using System; + class Program { } + """; + var codeAfterMove = """ + using System; -class Program { }"; + class Program { } + """; var expectedDocumentName = "CacheContext.cs"; - var destinationDocumentText = @"interface CacheContext(String Message); -"; + var destinationDocumentText = """ + interface CacheContext(String Message); + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -1499,31 +1719,37 @@ class Program { }"; [WpfFact] public async Task MoveClassInTopLevelStatements() { - var code = @" -using ConsoleApp1; -using System; + var code = """ -var c = new C(); -Console.WriteLine(c.Hello); + using ConsoleApp1; + using System; -class [||]C -{ - public string Hello => ""Hello""; -}"; + var c = new C(); + Console.WriteLine(c.Hello); + + class [||]C + { + public string Hello => "Hello"; + } + """; + + var codeAfterMove = """ - var codeAfterMove = @" -using ConsoleApp1; -using System; + using ConsoleApp1; + using System; -var c = new C(); -Console.WriteLine(c.Hello); -"; + var c = new C(); + Console.WriteLine(c.Hello); + + """; var expectedDocumentName = "C.cs"; - var destinationDocumentText = @"class C -{ - public string Hello => ""Hello""; -}"; + var destinationDocumentText = """ + class C + { + public string Hello => "Hello"; + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -1531,13 +1757,15 @@ class [||]C [WpfFact] public async Task MissingInTopLevelStatementsOnly() { - var code = @" -using ConsoleApp1; -using System; + var code = """ -var c = new object(); -[||]Console.WriteLine(c.ToString()); -"; + using ConsoleApp1; + using System; + + var c = new object(); + [||]Console.WriteLine(c.ToString()); + + """; await TestMissingAsync(code); } @@ -1545,39 +1773,45 @@ public async Task MissingInTopLevelStatementsOnly() [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/55544")] public async Task MoveInNamespace_WithAttributes1() { - var code = @" -using Sytem.Reflection; + var code = """ -[assembly: AssemblyCompany("")] -namespace N -{ - class A - { - } + using Sytem.Reflection; - class [||]B - { - } -}"; + [assembly: AssemblyCompany(")] + namespace N + { + class A + { + } + + class [||]B + { + } + } + """; - var codeAfterMove = @" -using Sytem.Reflection; + var codeAfterMove = """ -[assembly: AssemblyCompany("")] -namespace N -{ - class A - { - } -}"; + using Sytem.Reflection; + + [assembly: AssemblyCompany(")] + namespace N + { + class A + { + } + } + """; var expectedDocumentName = "B.cs"; - var destinationDocumentText = @"namespace N -{ - class B - { - } -}"; + var destinationDocumentText = """ + namespace N + { + class B + { + } + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -1585,41 +1819,47 @@ class B [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/55544")] public async Task MoveInNamespace_WithAttributes2() { - var code = @" -using Sytem.Reflection; + var code = """ -[assembly: AssemblyCompany("")] -namespace N -{ - class A - { - } + using Sytem.Reflection; - [Test] - class [||]B - { - } -}"; + [assembly: AssemblyCompany(")] + namespace N + { + class A + { + } + + [Test] + class [||]B + { + } + } + """; - var codeAfterMove = @" -using Sytem.Reflection; + var codeAfterMove = """ -[assembly: AssemblyCompany("")] -namespace N -{ - class A - { - } -}"; + using Sytem.Reflection; + + [assembly: AssemblyCompany(")] + namespace N + { + class A + { + } + } + """; var expectedDocumentName = "B.cs"; - var destinationDocumentText = @"namespace N -{ - [Test] - class B - { - } -}"; + var destinationDocumentText = """ + namespace N + { + [Test] + class B + { + } + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -1627,36 +1867,42 @@ class B [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/55544")] public async Task MoveInNamespace_WithAttributes3() { - var code = @" -namespace N -{ - class A - { - } + var code = """ - [Test] - class [||]B - { - } -}"; + namespace N + { + class A + { + } - var codeAfterMove = @" -namespace N -{ - class A - { - } -}"; + [Test] + class [||]B + { + } + } + """; + + var codeAfterMove = """ + + namespace N + { + class A + { + } + } + """; var expectedDocumentName = "B.cs"; - var destinationDocumentText = @" -namespace N -{ - [Test] - class B - { - } -}"; + var destinationDocumentText = """ + + namespace N + { + [Test] + class B + { + } + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -1664,27 +1910,33 @@ class B [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/55544")] public async Task MoveTopLevel_WithAttributes1() { - var code = @" -[Test] -class [||]A -{ -} + var code = """ -class B -{ -}"; + [Test] + class [||]A + { + } - var codeAfterMove = @" -class B -{ -}"; + class B + { + } + """; + + var codeAfterMove = """ + + class B + { + } + """; var expectedDocumentName = "A.cs"; - var destinationDocumentText = @"[Test] -class A -{ -} -"; + var destinationDocumentText = """ + [Test] + class A + { + } + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -1692,29 +1944,35 @@ class A [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/55544")] public async Task MoveTopLevel_WithAttributes2() { - var code = @" -[Test] -class [||]A -{ -} + var code = """ -[Test] -class B -{ -}"; + [Test] + class [||]A + { + } - var codeAfterMove = @" -[Test] -class B -{ -}"; + [Test] + class B + { + } + """; + + var codeAfterMove = """ + + [Test] + class B + { + } + """; var expectedDocumentName = "A.cs"; - var destinationDocumentText = @"[Test] -class A -{ -} -"; + var destinationDocumentText = """ + [Test] + class A + { + } + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -1727,25 +1985,31 @@ class A [InlineData("record")] public async Task MoveNestedTypeFromInterface(string memberType) { - var code = $@" -interface I -{{ - {memberType} [||]Member - {{ - }} -}}"; - var codeAfterMove = @" -partial interface I -{ -}"; + var code = $$""" + + interface I + { + {{memberType}} [||]Member + { + } + } + """; + var codeAfterMove = """ + + partial interface I + { + } + """; var expectedDocumentName = "Member.cs"; - var destinationDocumentText = $@" -partial interface I -{{ - {memberType} Member - {{ - }} -}}"; + var destinationDocumentText = $$""" + + partial interface I + { + {{memberType}} Member + { + } + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } } From dc541923507181b6d0d0646e3fe0ab8e4b5b6a54 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 22 Nov 2024 21:08:15 -0800 Subject: [PATCH 508/508] Reapply --- .../InlineTemporaryCodeRefactoringProvider.cs | 8 ++++-- .../InlineTemporary/InlineTemporaryTests.cs | 27 +++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs index af667cac512ef..624dfbb5bf2e4 100644 --- a/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs @@ -408,10 +408,14 @@ ExpressionSyntax CreateExpressionToInline() return SyntaxFactory.ArrayCreationExpression(arrayType, arrayInitializer); } - else if (isVar && expression is ObjectCreationExpressionSyntax or ArrayCreationExpressionSyntax or CastExpressionSyntax) + else if (isVar && expression is + ObjectCreationExpressionSyntax or + ArrayCreationExpressionSyntax or + CastExpressionSyntax or + InvocationExpressionSyntax) { // if we have `var x = new Y();` there's no need to do any casting as the type is indicated - // directly in the existing code. The same holds for `new Y[]` or `(Y)...` + // directly in the existing code. The same holds for `new Y[]` or `(Y)...` or `Y(...)` return expression; } else diff --git a/src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs b/src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs index c73157fd375d2..33b14d1cc7e88 100644 --- a/src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs +++ b/src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs @@ -5884,4 +5884,31 @@ public string M() await TestInRegularAndScriptAsync(code, expected); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69869")] + public async Task InlineTemporaryNoNeededVariable() + { + await TestInRegularAndScriptAsync( + """ + using System; + class A + { + void M(string[] args) + { + var [||]a = Math.Round(1.1D); + var b = a; + } + } + """, + """ + using System; + class A + { + void M(string[] args) + { + var b = Math.Round(1.1D); + } + } + """); + } }