diff --git a/docs/features/incremental-generators.md b/docs/features/incremental-generators.md index d4106e8b56a26..df259799d1e75 100644 --- a/docs/features/incremental-generators.md +++ b/docs/features/incremental-generators.md @@ -169,12 +169,12 @@ The simplest transformation is `Select`. This maps the value in one provider into a new provider by applying a transform to it. ```ascii - IValueProvider IValueProvider - ┌─────────────┐ ┌─────────────┐ - │ │ Select │ │ - │ TSource ├───────────────────────────►│ TResult │ - │ │ │ │ - └─────────────┘ └─────────────┘ + IValueProvider IValueProvider + ┌─────────────┐ ┌─────────────┐ + │ │ Select │ │ + │ TSource ├──────────────────────────►│ TResult │ + │ │ │ │ + └─────────────┘ └─────────────┘ ``` Generator transformations can be thought of as being conceptually somewhat similar to diff --git a/src/Analyzers/CSharp/Tests/UseExpressionBody/UseExpressionBodyForAccessorsAnalyzerTests.cs b/src/Analyzers/CSharp/Tests/UseExpressionBody/UseExpressionBodyForAccessorsAnalyzerTests.cs index 605a49847e8f7..d67dcf4b239bf 100644 --- a/src/Analyzers/CSharp/Tests/UseExpressionBody/UseExpressionBodyForAccessorsAnalyzerTests.cs +++ b/src/Analyzers/CSharp/Tests/UseExpressionBody/UseExpressionBodyForAccessorsAnalyzerTests.cs @@ -732,7 +732,7 @@ public async Task TestOfferToConvertToBlockEvenIfExpressionBodyPreferredIfPriorT using System; class C { - int Goo { {|IDE0027:get {|CS8059:=> {|CS8059:throw|} new NotImplementedException()|};|} } + int Goo { {|IDE0027:get {|CS8059:=>|} {|CS8059:throw|} new NotImplementedException();|} } }"; var fixedCode = @" using System; @@ -756,8 +756,8 @@ public async Task TestOfferToConvertToBlockEvenIfExpressionBodyPreferredIfPriorT using System; class C { - int Goo { {|IDE0027:get {|CS8059:=> {|CS8059:throw|} new NotImplementedException()|};|} } - int Bar { {|IDE0027:get {|CS8059:=> {|CS8059:throw|} new NotImplementedException()|};|} } + int Goo { {|IDE0027:get {|CS8059:=>|} {|CS8059:throw|} new NotImplementedException();|} } + int Bar { {|IDE0027:get {|CS8059:=>|} {|CS8059:throw|} new NotImplementedException();|} } }"; var fixedCode = @" using System; diff --git a/src/Analyzers/CSharp/Tests/UseExpressionBody/UseExpressionBodyForConstructorsAnalyzerTests.cs b/src/Analyzers/CSharp/Tests/UseExpressionBody/UseExpressionBodyForConstructorsAnalyzerTests.cs index d3382c59e5273..c58bdf0dd8845 100644 --- a/src/Analyzers/CSharp/Tests/UseExpressionBody/UseExpressionBodyForConstructorsAnalyzerTests.cs +++ b/src/Analyzers/CSharp/Tests/UseExpressionBody/UseExpressionBodyForConstructorsAnalyzerTests.cs @@ -241,7 +241,7 @@ public async Task TestOfferToConvertToBlockEvenIfExpressionBodyPreferredIfPriorT using System; class C { - {|IDE0021:public C() {|CS8059:=> {|CS8059:throw|} new NotImplementedException()|};|} + {|IDE0021:public C() {|CS8059:=>|} {|CS8059:throw|} new NotImplementedException();|} }"; var fixedCode = @" using System; @@ -262,8 +262,8 @@ public async Task TestOfferToConvertToBlockEvenIfExpressionBodyPreferredIfPriorT using System; class C { - {|IDE0021:public C() {|CS8059:=> {|CS8059:throw|} new NotImplementedException()|};|} - {|IDE0021:public C(int i) {|CS8059:=> {|CS8059:throw|} new NotImplementedException()|};|} + {|IDE0021:public C() {|CS8059:=>|} {|CS8059:throw|} new NotImplementedException();|} + {|IDE0021:public C(int i) {|CS8059:=>|} {|CS8059:throw|} new NotImplementedException();|} }"; var fixedCode = @" using System; diff --git a/src/Analyzers/CSharp/Tests/UseExpressionBody/UseExpressionBodyForMethodsAnalyzerTests.cs b/src/Analyzers/CSharp/Tests/UseExpressionBody/UseExpressionBodyForMethodsAnalyzerTests.cs index c2ceaf5a0805c..a2b7b6bdc826c 100644 --- a/src/Analyzers/CSharp/Tests/UseExpressionBody/UseExpressionBodyForMethodsAnalyzerTests.cs +++ b/src/Analyzers/CSharp/Tests/UseExpressionBody/UseExpressionBodyForMethodsAnalyzerTests.cs @@ -534,7 +534,7 @@ public async Task TestOfferToConvertToBlockEvenIfExpressionBodyPreferredIfPriorT using System; class C { - {|IDE0022:void M() {|CS8026:=> {|CS8026:throw|} new NotImplementedException()|};|} + {|IDE0022:void M() {|CS8026:=>|} {|CS8026:throw|} new NotImplementedException();|} }"; var fixedCode = @" using System; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 50f3d36596875..ab01f3e82118d 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -4707,6 +4707,9 @@ private BoundObjectInitializerExpression BindObjectInitializerExpression( initializerSyntax.Kind() == SyntaxKind.WithInitializerExpression); Debug.Assert((object)initializerType != null); + if (initializerSyntax.Kind() == SyntaxKind.ObjectInitializerExpression) + MessageID.IDS_FeatureObjectInitializer.CheckFeatureAvailability(diagnostics, initializerSyntax, initializerSyntax.OpenBraceToken.GetLocation()); + // We use a location specific binder for binding object initializer field/property access to generate object initializer specific diagnostics: // 1) CS1914 (ERR_StaticMemberInObjectInitializer) // 2) CS1917 (ERR_ReadonlyValueTypeInObjectInitializer) @@ -5205,6 +5208,8 @@ private BoundCollectionInitializerExpression BindCollectionInitializerExpression Debug.Assert(initializerSyntax.Expressions.Any()); Debug.Assert((object)initializerType != null); + MessageID.IDS_FeatureCollectionInitializer.CheckFeatureAvailability(diagnostics, initializerSyntax, initializerSyntax.OpenBraceToken.GetLocation()); + var initializerBuilder = ArrayBuilder.GetInstance(); // SPEC: The collection object to which a collection initializer is applied must be of a type that implements System.Collections.IEnumerable or diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs index 93f35d4bfad1f..d7175d37fdaee 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs @@ -184,6 +184,11 @@ private UnboundLambda AnalyzeAnonymousFunction( if (isLastParameter && paramsKeyword.Kind() != SyntaxKind.None) { hasParamsArray = true; + + ReportUseSiteDiagnosticForSynthesizedAttribute(Compilation, + WellKnownMember.System_ParamArrayAttribute__ctor, + diagnostics, + paramsKeyword.GetLocation()); } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 65335b508be40..a07696b6f1026 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -3466,9 +3466,28 @@ private static bool IsValidExpressionBody(SyntaxNode expressionSyntax, BoundExpr /// /// Binds an expression-bodied member with expression e as either { return e; } or { e; }. /// - internal virtual BoundBlock BindExpressionBodyAsBlock(ArrowExpressionClauseSyntax expressionBody, - BindingDiagnosticBag diagnostics) + internal virtual BoundBlock BindExpressionBodyAsBlock( + ArrowExpressionClauseSyntax expressionBody, + BindingDiagnosticBag diagnostics) { + var messageId = expressionBody.Parent switch + { + ConstructorDeclarationSyntax or DestructorDeclarationSyntax => MessageID.IDS_FeatureExpressionBodiedDeOrConstructor, + AccessorDeclarationSyntax => MessageID.IDS_FeatureExpressionBodiedAccessor, + BaseMethodDeclarationSyntax => MessageID.IDS_FeatureExpressionBodiedMethod, + IndexerDeclarationSyntax => MessageID.IDS_FeatureExpressionBodiedIndexer, + PropertyDeclarationSyntax => MessageID.IDS_FeatureExpressionBodiedProperty, + // No need to check if expression bodies are allowed if we have a local function. Local functions + // themselves are checked for availability, and if they are available then expression bodies must + // also be available. + LocalFunctionStatementSyntax => (MessageID?)null, + // null in speculative scenarios. + null => null, + _ => throw ExceptionUtilities.UnexpectedValue(expressionBody.Parent.Kind()), + }; + + messageId?.CheckFeatureAvailability(diagnostics, expressionBody, expressionBody.ArrowToken.GetLocation()); + Binder bodyBinder = this.GetBinder(expressionBody); Debug.Assert(bodyBinder != null); @@ -3560,10 +3579,17 @@ private BoundNode BindSimpleProgram(CompilationUnitSyntax compilationUnit, Bindi private BoundNode BindSimpleProgramCompilationUnit(CompilationUnitSyntax compilationUnit, BindingDiagnosticBag diagnostics) { ArrayBuilder boundStatements = ArrayBuilder.GetInstance(); + var first = true; foreach (var statement in compilationUnit.Members) { if (statement is GlobalStatementSyntax topLevelStatement) { + if (first) + { + first = false; + MessageID.IDS_TopLevelStatements.CheckFeatureAvailability(diagnostics, topLevelStatement); + } + var boundStatement = BindStatement(topLevelStatement.Statement, diagnostics); boundStatements.Add(boundStatement); } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs index 86862a1471a71..838d50d7f630c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs @@ -57,6 +57,15 @@ public override Conversion GetMethodGroupDelegateConversion(BoundMethodGroup sou Debug.Assert(methodSymbol == ((NamedTypeSymbol)destination).DelegateInvokeMethod); + // If synthesizing a delegate with `params` array, check that `ParamArrayAttribute` is available. + if (methodSymbol.OriginalDefinition is SynthesizedDelegateInvokeMethod { Parameters: [.., { IsParams: true }] }) + { + Binder.GetWellKnownTypeMember(Compilation, + WellKnownMember.System_ParamArrayAttribute__ctor, + out var memberUseSiteInfo); + useSiteInfo.Add(memberUseSiteInfo); + } + var resolution = ResolveDelegateOrFunctionPointerMethodGroup(_binder, source, methodSymbol, isFunctionPointer, callingConventionInfo, ref useSiteInfo); var conversion = (resolution.IsEmpty || resolution.HasAnyErrors) ? Conversion.NoConversion : diff --git a/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs b/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs index a6f58df436b19..bb88cc6b69efc 100644 --- a/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs +++ b/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs @@ -593,9 +593,9 @@ public override SingleNamespaceOrTypeDeclaration VisitRecordDeclaration(RecordDe private SingleNamespaceOrTypeDeclaration VisitTypeDeclaration(TypeDeclarationSyntax node, DeclarationKind kind) { - SingleTypeDeclaration.TypeDeclarationFlags declFlags = node.AttributeLists.Any() ? - SingleTypeDeclaration.TypeDeclarationFlags.HasAnyAttributes : - SingleTypeDeclaration.TypeDeclarationFlags.None; + var declFlags = node.AttributeLists.Any() + ? SingleTypeDeclaration.TypeDeclarationFlags.HasAnyAttributes + : SingleTypeDeclaration.TypeDeclarationFlags.None; if (node.BaseList != null) { @@ -618,6 +618,16 @@ private SingleNamespaceOrTypeDeclaration VisitTypeDeclaration(TypeDeclarationSyn declFlags |= SingleTypeDeclaration.TypeDeclarationFlags.HasAnyNontypeMembers; } + // If we have `record class` or `record struct` check that this is supported in the language. Note: we don't + // have to do any check for the simple `record` case as the parser itself would never produce such a node + // unless the language version was sufficient (since it actually will not produce the node at all on + // previous versions). + if (node is RecordDeclarationSyntax record && + record.ClassOrStructKeyword.Kind() != SyntaxKind.None) + { + MessageID.IDS_FeatureRecordStructs.CheckFeatureAvailability(diagnostics, record, record.ClassOrStructKeyword.GetLocation()); + } + var modifiers = node.Modifiers.ToDeclarationModifiers(diagnostics: diagnostics); var quickAttributes = GetQuickAttributes(node.AttributeLists); diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index c84b44a54fe05..f49dac01e31bf 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -32,7 +32,6 @@ internal partial class LanguageParser : SyntaxParser private int _recursionDepth; private TerminatorState _termState; // Resettable - private bool _checkedTopLevelStatementsFeatureAvailability; // Resettable // NOTE: If you add new state, you should probably add it to ResetPoint as well. @@ -171,11 +170,11 @@ internal CompilationUnitSyntax ParseCompilationUnit() return ParseWithStackGuard( ParseCompilationUnitCore, () => SyntaxFactory.CompilationUnit( - new SyntaxList(), - new SyntaxList(), - new SyntaxList(), - new SyntaxList(), - SyntaxFactory.Token(SyntaxKind.EndOfFileToken))); + new SyntaxList(), + new SyntaxList(), + new SyntaxList(), + new SyntaxList(), + SyntaxFactory.Token(SyntaxKind.EndOfFileToken))); } internal CompilationUnitSyntax ParseCompilationUnitCore() @@ -656,17 +655,6 @@ void parseUsingDirective(ref SyntaxToken openBrace, ref NamespaceBodyBuilder bod } } - private GlobalStatementSyntax CheckTopLevelStatementsFeatureAvailability(GlobalStatementSyntax globalStatementSyntax) - { - if (IsScript || _checkedTopLevelStatementsFeatureAvailability) - { - return globalStatementSyntax; - } - - _checkedTopLevelStatementsFeatureAvailability = true; - return CheckFeatureAvailability(globalStatementSyntax, MessageID.IDS_TopLevelStatements); - } - private static void AddIncompleteMembers(ref SyntaxListBuilder incompleteMembers, ref NamespaceBodyBuilder body) { if (incompleteMembers.Count > 0) @@ -865,25 +853,18 @@ private bool IsPossibleAttributeDeclaration() private SyntaxList ParseAttributeDeclarations() { var attributes = _pool.Allocate(); - try - { - var saveTerm = _termState; - _termState |= TerminatorState.IsAttributeDeclarationTerminator; - - while (this.IsPossibleAttributeDeclaration()) - { - var attribute = this.ParseAttributeDeclaration(); - attributes.Add(attribute); - } - - _termState = saveTerm; + var saveTerm = _termState; + _termState |= TerminatorState.IsAttributeDeclarationTerminator; - return attributes.ToList(); - } - finally + while (this.IsPossibleAttributeDeclaration()) { - _pool.Free(attributes); + var attribute = this.ParseAttributeDeclaration(); + attributes.Add(attribute); } + + _termState = saveTerm; + + return _pool.ToListAndFree(attributes); } private bool IsAttributeDeclarationTerminator() @@ -911,23 +892,12 @@ private AttributeListSyntax ParseAttributeDeclaration() } var attributes = _pool.AllocateSeparated(); - try - { - if (attrLocation != null && attrLocation.Identifier.ToAttributeLocation() == AttributeLocation.Module) - { - attrLocation = CheckFeatureAvailability(attrLocation, MessageID.IDS_FeatureModuleAttrLoc); - } - - this.ParseAttributes(attributes); - var closeBracket = this.EatToken(SyntaxKind.CloseBracketToken); - var declaration = _syntaxFactory.AttributeList(openBracket, attrLocation, attributes, closeBracket); - - return declaration; - } - finally - { - _pool.Free(attributes); - } + this.ParseAttributes(attributes); + return _syntaxFactory.AttributeList( + openBracket, + attrLocation, + _pool.ToListAndFree(attributes), + this.EatToken(SyntaxKind.CloseBracketToken)); } private void ParseAttributes(SeparatedSyntaxListBuilder nodes) @@ -986,10 +956,9 @@ private AttributeSyntax ParseAttribute() return (AttributeSyntax)this.EatNode(); } - var name = this.ParseQualifiedName(); - - var argList = this.ParseAttributeArgumentList(); - return _syntaxFactory.Attribute(name, argList); + return _syntaxFactory.Attribute( + this.ParseQualifiedName(), + this.ParseAttributeArgumentList()); } internal AttributeArgumentListSyntax ParseAttributeArgumentList() @@ -1078,17 +1047,17 @@ private AttributeArgumentSyntax ParseAttributeArgument() { case SyntaxKind.EqualsToken: { - var name = this.ParseIdentifierToken(); - var equals = this.EatToken(SyntaxKind.EqualsToken); - nameEquals = _syntaxFactory.NameEquals(_syntaxFactory.IdentifierName(name), equals); + nameEquals = _syntaxFactory.NameEquals( + _syntaxFactory.IdentifierName(this.ParseIdentifierToken()), + this.EatToken(SyntaxKind.EqualsToken)); } break; case SyntaxKind.ColonToken: { - var name = this.ParseIdentifierName(); - var colonToken = this.EatToken(SyntaxKind.ColonToken); - nameColon = _syntaxFactory.NameColon(name, colonToken); + nameColon = _syntaxFactory.NameColon( + this.ParseIdentifierName(), + this.EatToken(SyntaxKind.ColonToken)); } break; @@ -1612,7 +1581,13 @@ private TypeDeclarationSyntax ParseClassOrStructOrInterfaceDeclaration(SyntaxLis SyntaxToken semicolon; SyntaxToken? openBrace; SyntaxToken? closeBrace; - if (!(keyword.Kind == SyntaxKind.RecordKeyword) || CurrentToken.Kind != SyntaxKind.SemicolonToken) + if (keyword.Kind == SyntaxKind.RecordKeyword && CurrentToken.Kind == SyntaxKind.SemicolonToken) + { + semicolon = EatToken(SyntaxKind.SemicolonToken); + openBrace = null; + closeBrace = null; + } + else { openBrace = this.EatToken(SyntaxKind.OpenBraceToken); @@ -1674,14 +1649,9 @@ private TypeDeclarationSyntax ParseClassOrStructOrInterfaceDeclaration(SyntaxLis { closeBrace = this.EatToken(SyntaxKind.CloseBraceToken); } + semicolon = TryEatToken(SyntaxKind.SemicolonToken); } - else - { - semicolon = CheckFeatureAvailability(EatToken(SyntaxKind.SemicolonToken), MessageID.IDS_FeatureRecords); - openBrace = null; - closeBrace = null; - } return constructTypeDeclaration(_syntaxFactory, attributes, modifiers, keyword, recordModifier, name, typeParameters, paramList, baseList, constraints, openBrace, members, closeBrace, semicolon); } @@ -1704,7 +1674,7 @@ bool tryScanRecordStart([NotNullWhen(true)] out SyntaxToken? keyword, out Syntax { keyword = ConvertToKeyword(this.EatToken()); recordModifier = this.CurrentToken.Kind is SyntaxKind.ClassKeyword or SyntaxKind.StructKeyword - ? CheckFeatureAvailability(EatToken(), MessageID.IDS_FeatureRecordStructs) + ? EatToken() : null; return true; @@ -1712,7 +1682,6 @@ bool tryScanRecordStart([NotNullWhen(true)] out SyntaxToken? keyword, out Syntax if (this.CurrentToken.Kind is SyntaxKind.StructKeyword or SyntaxKind.ClassKeyword && this.PeekToken(1).ContextualKind == SyntaxKind.RecordKeyword && - IsFeatureEnabled(MessageID.IDS_FeatureRecordStructs) && PeekToken(2).Kind is SyntaxKind.IdentifierToken) { // Provide a specific diagnostic on `struct record S` or `class record C` @@ -1923,46 +1892,40 @@ private BaseListSyntax ParseBaseList() var colon = this.EatToken(); var list = _pool.AllocateSeparated(); - try + + // first type + TypeSyntax firstType = this.ParseType(); + + ArgumentListSyntax argumentList = null; + if (this.CurrentToken.Kind == SyntaxKind.OpenParenToken) { - // first type - TypeSyntax firstType = this.ParseType(); + argumentList = this.ParseParenthesizedArgumentList(); + } - ArgumentListSyntax argumentList = null; - if (this.CurrentToken.Kind == SyntaxKind.OpenParenToken) + list.Add(argumentList is object ? _syntaxFactory.PrimaryConstructorBaseType(firstType, argumentList) : (BaseTypeSyntax)_syntaxFactory.SimpleBaseType(firstType)); + + // any additional types + while (true) + { + if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken || + ((_termState & TerminatorState.IsEndOfRecordSignature) != 0 && this.CurrentToken.Kind == SyntaxKind.SemicolonToken) || + this.IsCurrentTokenWhereOfConstraintClause()) { - argumentList = this.ParseParenthesizedArgumentList(); + break; } - - list.Add(argumentList is object ? _syntaxFactory.PrimaryConstructorBaseType(firstType, argumentList) : (BaseTypeSyntax)_syntaxFactory.SimpleBaseType(firstType)); - - // any additional types - while (true) + else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleType()) { - if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken || - ((_termState & TerminatorState.IsEndOfRecordSignature) != 0 && this.CurrentToken.Kind == SyntaxKind.SemicolonToken) || - this.IsCurrentTokenWhereOfConstraintClause()) - { - break; - } - else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleType()) - { - list.AddSeparator(this.EatToken(SyntaxKind.CommaToken)); - list.Add(_syntaxFactory.SimpleBaseType(this.ParseType())); - continue; - } - else if (this.SkipBadBaseListTokens(ref colon, list, SyntaxKind.CommaToken) == PostSkipAction.Abort) - { - break; - } + list.AddSeparator(this.EatToken(SyntaxKind.CommaToken)); + list.Add(_syntaxFactory.SimpleBaseType(this.ParseType())); + continue; + } + else if (this.SkipBadBaseListTokens(ref colon, list, SyntaxKind.CommaToken) == PostSkipAction.Abort) + { + break; } - - return _syntaxFactory.BaseList(colon, list); - } - finally - { - _pool.Free(list); } + + return _syntaxFactory.BaseList(colon, _pool.ToListAndFree(list)); } private PostSkipAction SkipBadBaseListTokens(ref SyntaxToken colon, SeparatedSyntaxListBuilder list, SyntaxKind expected) @@ -1999,53 +1962,51 @@ private TypeParameterConstraintClauseSyntax ParseTypeParameterConstraintClause() var colon = this.EatToken(SyntaxKind.ColonToken); var bounds = _pool.AllocateSeparated(); - try + + // first bound + if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken || this.IsCurrentTokenWhereOfConstraintClause()) { - // first bound - if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken || this.IsCurrentTokenWhereOfConstraintClause()) - { - bounds.Add(_syntaxFactory.TypeConstraint(this.AddError(this.CreateMissingIdentifierName(), ErrorCode.ERR_TypeExpected))); - } - else - { - bounds.Add(this.ParseTypeParameterConstraint()); + bounds.Add(_syntaxFactory.TypeConstraint(this.AddError(this.CreateMissingIdentifierName(), ErrorCode.ERR_TypeExpected))); + } + else + { + bounds.Add(this.ParseTypeParameterConstraint()); - // remaining bounds - while (true) + // remaining bounds + while (true) + { + if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken + || ((_termState & TerminatorState.IsEndOfRecordSignature) != 0 && this.CurrentToken.Kind == SyntaxKind.SemicolonToken) + || this.CurrentToken.Kind == SyntaxKind.EqualsGreaterThanToken + || this.CurrentToken.ContextualKind == SyntaxKind.WhereKeyword) + { + break; + } + else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleTypeParameterConstraint()) { - if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken - || ((_termState & TerminatorState.IsEndOfRecordSignature) != 0 && this.CurrentToken.Kind == SyntaxKind.SemicolonToken) - || this.CurrentToken.Kind == SyntaxKind.EqualsGreaterThanToken - || this.CurrentToken.ContextualKind == SyntaxKind.WhereKeyword) + bounds.AddSeparator(this.EatToken(SyntaxKind.CommaToken)); + if (this.IsCurrentTokenWhereOfConstraintClause()) { + bounds.Add(_syntaxFactory.TypeConstraint(this.AddError(this.CreateMissingIdentifierName(), ErrorCode.ERR_TypeExpected))); break; } - else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleTypeParameterConstraint()) - { - bounds.AddSeparator(this.EatToken(SyntaxKind.CommaToken)); - if (this.IsCurrentTokenWhereOfConstraintClause()) - { - bounds.Add(_syntaxFactory.TypeConstraint(this.AddError(this.CreateMissingIdentifierName(), ErrorCode.ERR_TypeExpected))); - break; - } - else - { - bounds.Add(this.ParseTypeParameterConstraint()); - } - } - else if (this.SkipBadTypeParameterConstraintTokens(bounds, SyntaxKind.CommaToken) == PostSkipAction.Abort) + else { - break; + bounds.Add(this.ParseTypeParameterConstraint()); } } + else if (this.SkipBadTypeParameterConstraintTokens(bounds, SyntaxKind.CommaToken) == PostSkipAction.Abort) + { + break; + } } - - return _syntaxFactory.TypeParameterConstraintClause(where, name, colon, bounds); - } - finally - { - _pool.Free(bounds); } + + return _syntaxFactory.TypeParameterConstraintClause( + where, + name, + colon, + _pool.ToListAndFree(bounds)); } private bool IsPossibleTypeParameterConstraint() @@ -2266,8 +2227,7 @@ MemberDeclarationSyntax createEmptyNodeFunc() return _syntaxFactory.IncompleteMember( new SyntaxList(), new SyntaxList(), - CreateMissingIdentifierName() - ); + CreateMissingIdentifierName()); } } @@ -2343,14 +2303,14 @@ private MemberDeclarationSyntax ParseMemberDeclarationOrStatementCore(SyntaxKind case SyntaxKind.UnsafeKeyword: if (this.PeekToken(1).Kind == SyntaxKind.OpenBraceToken) { - return CheckTopLevelStatementsFeatureAvailability(_syntaxFactory.GlobalStatement(ParseUnsafeStatement(attributes))); + return _syntaxFactory.GlobalStatement(ParseUnsafeStatement(attributes)); } break; case SyntaxKind.FixedKeyword: if (this.PeekToken(1).Kind == SyntaxKind.OpenParenToken) { - return CheckTopLevelStatementsFeatureAvailability(_syntaxFactory.GlobalStatement(ParseFixedStatement(attributes))); + return _syntaxFactory.GlobalStatement(ParseFixedStatement(attributes)); } break; @@ -2359,14 +2319,14 @@ private MemberDeclarationSyntax ParseMemberDeclarationOrStatementCore(SyntaxKind { case SyntaxKind.OpenParenToken: case SyntaxKind.OpenBraceToken: - return CheckTopLevelStatementsFeatureAvailability(_syntaxFactory.GlobalStatement(ParseExpressionStatement(attributes))); + return _syntaxFactory.GlobalStatement(ParseExpressionStatement(attributes)); } break; case SyntaxKind.NewKeyword: if (IsPossibleNewExpression()) { - return CheckTopLevelStatementsFeatureAvailability(_syntaxFactory.GlobalStatement(ParseExpressionStatement(attributes))); + return _syntaxFactory.GlobalStatement(ParseExpressionStatement(attributes)); } break; } @@ -2493,7 +2453,7 @@ private MemberDeclarationSyntax ParseMemberDeclarationOrStatementCore(SyntaxKind if (isAcceptableNonDeclarationStatement(statement, IsScript)) { - return CheckTopLevelStatementsFeatureAvailability(_syntaxFactory.GlobalStatement(statement)); + return _syntaxFactory.GlobalStatement(statement); } } @@ -2643,7 +2603,7 @@ bool tryParseLocalDeclarationStatement(SyntaxList attributes, ref ResetPoin if (statement is not null) { - result = CheckTopLevelStatementsFeatureAvailability(_syntaxFactory.GlobalStatement(statement)); + result = _syntaxFactory.GlobalStatement(statement); return true; } } @@ -3119,10 +3079,7 @@ private bool IsFieldDeclaration(bool isEvent, bool isGlobalScriptLevel) private bool IsOperatorKeyword() { - return - this.CurrentToken.Kind == SyntaxKind.ImplicitKeyword || - this.CurrentToken.Kind == SyntaxKind.ExplicitKeyword || - this.CurrentToken.Kind == SyntaxKind.OperatorKeyword; + return this.CurrentToken.Kind is SyntaxKind.ImplicitKeyword or SyntaxKind.ExplicitKeyword or SyntaxKind.OperatorKeyword; } public static bool IsComplete(CSharpSyntaxNode node) @@ -3171,8 +3128,7 @@ private ConstructorDeclarationSyntax ParseConstructorDeclaration( : null; this.ParseBlockAndExpressionBodiesWithSemicolon( - out BlockSyntax body, out ArrowExpressionClauseSyntax expressionBody, out SyntaxToken semicolon, - requestedExpressionBodyFeature: MessageID.IDS_FeatureExpressionBodiedDeOrConstructor); + out BlockSyntax body, out ArrowExpressionClauseSyntax expressionBody, out SyntaxToken semicolon); return _syntaxFactory.ConstructorDeclaration(attributes, modifiers.ToList(), name, paramList, initializer, body, expressionBody, semicolon); } @@ -3225,14 +3181,13 @@ private DestructorDeclarationSyntax ParseDestructorDeclaration(SyntaxList), + this.EatToken(SyntaxKind.CloseParenToken)); this.ParseBlockAndExpressionBodiesWithSemicolon( - out BlockSyntax body, out ArrowExpressionClauseSyntax expressionBody, out SyntaxToken semicolon, - requestedExpressionBodyFeature: MessageID.IDS_FeatureExpressionBodiedDeOrConstructor); - - var parameterList = _syntaxFactory.ParameterList(openParen, default(SeparatedSyntaxList), closeParen); + out BlockSyntax body, out ArrowExpressionClauseSyntax expressionBody, out SyntaxToken semicolon); return _syntaxFactory.DestructorDeclaration(attributes, modifiers.ToList(), tilde, name, parameterList, body, expressionBody, semicolon); } @@ -3245,8 +3200,7 @@ private void ParseBlockAndExpressionBodiesWithSemicolon( out BlockSyntax blockBody, out ArrowExpressionClauseSyntax expressionBody, out SyntaxToken semicolon, - bool parseSemicolonAfterBlock = true, - MessageID requestedExpressionBodyFeature = MessageID.IDS_FeatureExpressionBodiedMethod) + bool parseSemicolonAfterBlock = true) { // Check for 'forward' declarations with no block of any kind if (this.CurrentToken.Kind == SyntaxKind.SemicolonToken) @@ -3257,25 +3211,16 @@ private void ParseBlockAndExpressionBodiesWithSemicolon( return; } - blockBody = null; - expressionBody = null; - - if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken) - { - blockBody = this.ParseMethodOrAccessorBodyBlock(attributes: default, isAccessorBody: false); - } + blockBody = this.CurrentToken.Kind == SyntaxKind.OpenBraceToken + ? this.ParseMethodOrAccessorBodyBlock(attributes: default, isAccessorBody: false) + : null; - if (this.CurrentToken.Kind == SyntaxKind.EqualsGreaterThanToken) - { - Debug.Assert(requestedExpressionBodyFeature == MessageID.IDS_FeatureExpressionBodiedMethod - || requestedExpressionBodyFeature == MessageID.IDS_FeatureExpressionBodiedAccessor - || requestedExpressionBodyFeature == MessageID.IDS_FeatureExpressionBodiedDeOrConstructor, - "Only IDS_FeatureExpressionBodiedMethod, IDS_FeatureExpressionBodiedAccessor or IDS_FeatureExpressionBodiedDeOrConstructor can be requested"); - expressionBody = this.ParseArrowExpressionClause(); - expressionBody = CheckFeatureAvailability(expressionBody, requestedExpressionBodyFeature); - } + expressionBody = this.CurrentToken.Kind == SyntaxKind.EqualsGreaterThanToken + ? this.ParseArrowExpressionClause() + : null; semicolon = null; + // Expression-bodies need semicolons and native behavior // expects a semicolon if there is no body if (expressionBody != null || blockBody == null) @@ -3320,22 +3265,24 @@ private bool IsEndOfTypeParameterList() private bool IsEndOfMethodSignature() { - return this.CurrentToken.Kind == SyntaxKind.SemicolonToken || this.CurrentToken.Kind == SyntaxKind.OpenBraceToken; + return this.CurrentToken.Kind is SyntaxKind.SemicolonToken or SyntaxKind.OpenBraceToken; } private bool IsEndOfRecordSignature() { - return this.CurrentToken.Kind == SyntaxKind.SemicolonToken || this.CurrentToken.Kind == SyntaxKind.OpenBraceToken; + return this.CurrentToken.Kind is SyntaxKind.SemicolonToken or SyntaxKind.OpenBraceToken; } private bool IsEndOfNameInExplicitInterface() { - return this.CurrentToken.Kind == SyntaxKind.DotToken || this.CurrentToken.Kind == SyntaxKind.ColonColonToken; + return this.CurrentToken.Kind is SyntaxKind.DotToken or SyntaxKind.ColonColonToken; } - private bool IsEndOfFunctionPointerParameterList(bool errored) => this.CurrentToken.Kind == (errored ? SyntaxKind.CloseParenToken : SyntaxKind.GreaterThanToken); + private bool IsEndOfFunctionPointerParameterList(bool errored) + => this.CurrentToken.Kind == (errored ? SyntaxKind.CloseParenToken : SyntaxKind.GreaterThanToken); - private bool IsEndOfFunctionPointerCallingConvention() => this.CurrentToken.Kind == SyntaxKind.CloseBracketToken; + private bool IsEndOfFunctionPointerCallingConvention() + => this.CurrentToken.Kind == SyntaxKind.CloseBracketToken; private MethodDeclarationSyntax ParseMethodDeclaration( SyntaxList attributes, @@ -3687,7 +3634,7 @@ ExplicitInterfaceSpecifierSyntax tryParseExplicitInterfaceSpecifier() separator = ConvertToMissingWithTrailingTrivia(separator, SyntaxKind.DotToken); } - return CheckFeatureAvailability(_syntaxFactory.ExplicitInterfaceSpecifier(explicitInterfaceName, separator), MessageID.IDS_FeatureStaticAbstractMembersInInterfaces); + return _syntaxFactory.ExplicitInterfaceSpecifier(explicitInterfaceName, separator); } } @@ -3984,42 +3931,39 @@ private AccessorListSyntax ParseAccessorList(bool isEvent) { // parse property accessors var builder = _pool.Allocate(); - try + + while (true) { - while (true) + if (this.CurrentToken.Kind == SyntaxKind.CloseBraceToken) { - if (this.CurrentToken.Kind == SyntaxKind.CloseBraceToken) - { - break; - } - else if (this.IsPossibleAccessor()) - { - var acc = this.ParseAccessorDeclaration(isEvent); - builder.Add(acc); - } - else if (this.SkipBadAccessorListTokens(ref openBrace, builder, - isEvent ? ErrorCode.ERR_AddOrRemoveExpected : ErrorCode.ERR_GetOrSetExpected) == PostSkipAction.Abort) - { - break; - } + break; + } + else if (this.IsPossibleAccessor()) + { + var acc = this.ParseAccessorDeclaration(isEvent); + builder.Add(acc); + } + else if (this.SkipBadAccessorListTokens(ref openBrace, builder, + isEvent ? ErrorCode.ERR_AddOrRemoveExpected : ErrorCode.ERR_GetOrSetExpected) == PostSkipAction.Abort) + { + break; } - - accessors = builder.ToList(); - } - finally - { - _pool.Free(builder); } + + accessors = _pool.ToListAndFree(builder); } - var closeBrace = this.EatToken(SyntaxKind.CloseBraceToken); - return _syntaxFactory.AccessorList(openBrace, accessors, closeBrace); + return _syntaxFactory.AccessorList( + openBrace, + accessors, + this.EatToken(SyntaxKind.CloseBraceToken)); } private ArrowExpressionClauseSyntax ParseArrowExpressionClause() { - var arrowToken = this.EatToken(SyntaxKind.EqualsGreaterThanToken); - return _syntaxFactory.ArrowExpressionClause(arrowToken, ParsePossibleRefExpression()); + return _syntaxFactory.ArrowExpressionClause( + this.EatToken(SyntaxKind.EqualsGreaterThanToken), + ParsePossibleRefExpression()); } private ExpressionSyntax ParsePossibleRefExpression() @@ -4230,7 +4174,7 @@ private PostSkipAction SkipBadTokensWithExpectedKind( nodes.Add(token); } - trailingTrivia = (nodes.Count > 0) ? nodes.ToListNode() : null; + trailingTrivia = nodes.ToListNode(); return action; } finally @@ -4263,7 +4207,7 @@ private PostSkipAction SkipBadTokensWithErrorCode( nodes.Add(token); } - trailingTrivia = (nodes.Count > 0) ? nodes.ToListNode() : null; + trailingTrivia = nodes.ToListNode(); return action; } finally @@ -4288,14 +4232,6 @@ private AccessorDeclarationSyntax ParseAccessorDeclaration(bool isEvent) // check availability of readonly members feature for accessors CheckForVersionSpecificModifiers(accMods, SyntaxKind.ReadOnlyKeyword, MessageID.IDS_FeatureReadOnlyMembers); - if (!isEvent) - { - if (accMods != null && accMods.Count > 0) - { - accMods[0] = CheckFeatureAvailability(accMods[0], MessageID.IDS_FeaturePropertyAccessorMods); - } - } - var accessorName = this.EatToken(SyntaxKind.IdentifierToken, isEvent ? ErrorCode.ERR_AddOrRemoveExpected : ErrorCode.ERR_GetOrSetExpected); var accessorKind = GetAccessorKind(accessorName); @@ -4336,8 +4272,7 @@ private AccessorDeclarationSyntax ParseAccessorDeclaration(bool isEvent) if (currentTokenIsOpenBraceToken || currentTokenIsArrow) { this.ParseBlockAndExpressionBodiesWithSemicolon( - out blockBody, out expressionBody, out semicolon, - requestedExpressionBodyFeature: MessageID.IDS_FeatureExpressionBodiedAccessor); + out blockBody, out expressionBody, out semicolon); } else if (currentTokenIsSemicolon) { @@ -4411,20 +4346,8 @@ internal ParameterListSyntax ParseParenthesizedParameterList() var parameters = _pool.AllocateSeparated(); - try - { - var openKind = SyntaxKind.OpenParenToken; - var closeKind = SyntaxKind.CloseParenToken; - - SyntaxToken open; - SyntaxToken close; - this.ParseParameterList(out open, parameters, out close, openKind, closeKind); - return _syntaxFactory.ParameterList(open, parameters, close); - } - finally - { - _pool.Free(parameters); - } + this.ParseParameterList(out var open, parameters, out var close, SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken); + return _syntaxFactory.ParameterList(open, _pool.ToListAndFree(parameters), close); } internal BracketedParameterListSyntax ParseBracketedParameterList() @@ -4436,20 +4359,8 @@ internal BracketedParameterListSyntax ParseBracketedParameterList() var parameters = _pool.AllocateSeparated(); - try - { - var openKind = SyntaxKind.OpenBracketToken; - var closeKind = SyntaxKind.CloseBracketToken; - - SyntaxToken open; - SyntaxToken close; - this.ParseParameterList(out open, parameters, out close, openKind, closeKind); - return _syntaxFactory.BracketedParameterList(open, parameters, close); - } - finally - { - _pool.Free(parameters); - } + this.ParseParameterList(out var open, parameters, out var close, SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken); + return _syntaxFactory.BracketedParameterList(open, _pool.ToListAndFree(parameters), close); } private static bool CanReuseParameterList(CSharp.Syntax.ParameterListSyntax list) @@ -4829,17 +4740,14 @@ private FieldDeclarationSyntax ParseFixedSizeBufferDeclaration( { this.ParseVariableDeclarators(type, VariableFlags.Fixed, variables, parentKind); - var semicolon = this.EatToken(SyntaxKind.SemicolonToken); - return _syntaxFactory.FieldDeclaration( attributes, modifiers.ToList(), - _syntaxFactory.VariableDeclaration(type, variables), - semicolon); + _syntaxFactory.VariableDeclaration(type, _pool.ToListAndFree(variables)), + this.EatToken(SyntaxKind.SemicolonToken)); } finally { _termState = saveTerm; - _pool.Free(variables); } } @@ -4998,13 +4906,12 @@ private FieldDeclarationSyntax ParseNormalFieldDeclaration( return _syntaxFactory.FieldDeclaration( attributes, modifiers.ToList(), - _syntaxFactory.VariableDeclaration(type, variables), + _syntaxFactory.VariableDeclaration(type, _pool.ToListAndFree(variables)), semicolon); } finally { _termState = saveTerm; - _pool.Free(variables); } } @@ -5045,13 +4952,12 @@ private EventFieldDeclarationSyntax ParseEventFieldDeclaration( attributes, modifiers.ToList(), eventToken, - _syntaxFactory.VariableDeclaration(type, variables), + _syntaxFactory.VariableDeclaration(type, _pool.ToListAndFree(variables)), semicolon); } finally { _termState = saveTerm; - _pool.Free(variables); } } @@ -5442,42 +5348,35 @@ private VariableDeclaratorSyntax ParseVariableDeclarator( } var args = _pool.AllocateSeparated(); - try + var withSeps = sizes.GetWithSeparators(); + foreach (var item in withSeps) { - var withSeps = sizes.GetWithSeparators(); - foreach (var item in withSeps) + var expression = item as ExpressionSyntax; + if (expression != null) { - var expression = item as ExpressionSyntax; - if (expression != null) - { - bool isOmitted = expression.Kind == SyntaxKind.OmittedArraySizeExpression; - if (!isFixed && !isOmitted) - { - expression = this.AddError(expression, ErrorCode.ERR_ArraySizeInDeclaration); - } - - args.Add(_syntaxFactory.Argument(null, refKindKeyword: null, expression)); - } - else + bool isOmitted = expression.Kind == SyntaxKind.OmittedArraySizeExpression; + if (!isFixed && !isOmitted) { - args.AddSeparator((SyntaxToken)item); + expression = this.AddError(expression, ErrorCode.ERR_ArraySizeInDeclaration); } - } - argumentList = _syntaxFactory.BracketedArgumentList(open, args, close); - if (!isFixed) + args.Add(_syntaxFactory.Argument(null, refKindKeyword: null, expression)); + } + else { - argumentList = this.AddError(argumentList, ErrorCode.ERR_CStyleArray); - // If we have "int x[] = new int[10];" then parse the initializer. - if (this.CurrentToken.Kind == SyntaxKind.EqualsToken) - { - goto case SyntaxKind.EqualsToken; - } + args.AddSeparator((SyntaxToken)item); } } - finally + + argumentList = _syntaxFactory.BracketedArgumentList(open, _pool.ToListAndFree(args), close); + if (!isFixed) { - _pool.Free(args); + argumentList = this.AddError(argumentList, ErrorCode.ERR_CStyleArray); + // If we have "int x[] = new int[10];" then parse the initializer. + if (this.CurrentToken.Kind == SyntaxKind.EqualsToken) + { + goto case SyntaxKind.EqualsToken; + } } break; @@ -5572,20 +5471,12 @@ private FieldDeclarationSyntax ParseConstantFieldDeclaration(SyntaxList(); - try - { - this.ParseVariableDeclarators(type, VariableFlags.Const, variables, parentKind); - var semicolon = this.EatToken(SyntaxKind.SemicolonToken); - return _syntaxFactory.FieldDeclaration( - attributes, - modifiers.ToList(), - _syntaxFactory.VariableDeclaration(type, variables), - semicolon); - } - finally - { - _pool.Free(variables); - } + this.ParseVariableDeclarators(type, VariableFlags.Const, variables, parentKind); + return _syntaxFactory.FieldDeclaration( + attributes, + modifiers.ToList(), + _syntaxFactory.VariableDeclaration(type, _pool.ToListAndFree(variables)), + this.EatToken(SyntaxKind.SemicolonToken)); } private DelegateDeclarationSyntax ParseDelegateDeclaration(SyntaxList attributes, SyntaxListBuilder modifiers) @@ -5610,8 +5501,16 @@ private DelegateDeclarationSyntax ParseDelegateDeclaration(SyntaxList(); - try - { - this.ParseEnumMemberDeclarations(ref openBrace, builder); - members = builder.ToList(); - } - finally - { - _pool.Free(builder); - } + this.ParseEnumMemberDeclarations(ref openBrace, builder); + members = _pool.ToListAndFree(builder); } - var closeBrace = this.EatToken(SyntaxKind.CloseBraceToken); - var semicolon = TryEatToken(SyntaxKind.SemicolonToken); - return _syntaxFactory.EnumDeclaration( attributes, modifiers.ToList(), @@ -5677,8 +5566,8 @@ private EnumDeclarationSyntax ParseEnumDeclaration(SyntaxList x' as a pattern-matching operation and a declaration expression in a tuple. return ScanTypeArgumentListKind.DefiniteTypeArgumentList; @@ -6746,8 +6631,7 @@ private bool IsOperatorStart(out ExplicitInterfaceSpecifierSyntax explicitInterf separator = ConvertToMissingWithTrailingTrivia(separator, SyntaxKind.DotToken); } - explicitInterfaceOpt = CheckFeatureAvailability(_syntaxFactory.ExplicitInterfaceSpecifier(explicitInterfaceName, separator), MessageID.IDS_FeatureStaticAbstractMembersInInterfaces); - + explicitInterfaceOpt = _syntaxFactory.ExplicitInterfaceSpecifier(explicitInterfaceName, separator); return true; } finally @@ -6933,8 +6817,7 @@ private ScanTypeFlags ScanType(out SyntaxToken lastTokenOfType, bool forPattern private void ScanNamedTypePart() { - SyntaxToken lastTokenOfType; - ScanNamedTypePart(out lastTokenOfType); + ScanNamedTypePart(out _); } private ScanTypeFlags ScanNamedTypePart(out SyntaxToken lastTokenOfType) @@ -7440,20 +7323,13 @@ bool canBeNullableType() // Now check for arrays. { var ranks = _pool.Allocate(); - try - { - do - { - ranks.Add(this.ParseArrayRankSpecifier(out _)); - } - while (this.CurrentToken.Kind == SyntaxKind.OpenBracketToken); - - type = _syntaxFactory.ArrayType(type, ranks); - } - finally + do { - _pool.Free(ranks); + ranks.Add(this.ParseArrayRankSpecifier(out _)); } + while (this.CurrentToken.Kind == SyntaxKind.OpenBracketToken); + + type = _syntaxFactory.ArrayType(type, _pool.ToListAndFree(ranks)); continue; } default: @@ -7537,126 +7413,109 @@ private ArrayRankSpecifierSyntax ParseArrayRankSpecifier(out bool sawNonOmittedS bool sawOmittedSize = false; var open = this.EatToken(SyntaxKind.OpenBracketToken); var list = _pool.AllocateSeparated(); - try - { - var omittedArraySizeExpressionInstance = _syntaxFactory.OmittedArraySizeExpression(SyntaxFactory.Token(SyntaxKind.OmittedArraySizeExpressionToken)); - int lastTokenPosition = -1; - while (IsMakingProgress(ref lastTokenPosition) && this.CurrentToken.Kind != SyntaxKind.CloseBracketToken) - { - if (this.CurrentToken.Kind == SyntaxKind.CommaToken) - { - // NOTE: trivia will be attached to comma, not omitted array size - sawOmittedSize = true; - list.Add(omittedArraySizeExpressionInstance); - list.AddSeparator(this.EatToken()); - } - else if (this.IsPossibleExpression()) - { - var size = this.ParseExpressionCore(); - sawNonOmittedSize = true; - list.Add(size); - - if (this.CurrentToken.Kind != SyntaxKind.CloseBracketToken) - { - list.AddSeparator(this.EatToken(SyntaxKind.CommaToken)); - } - } - else if (this.SkipBadArrayRankSpecifierTokens(ref open, list, SyntaxKind.CommaToken) == PostSkipAction.Abort) - { - break; - } - } - // Don't end on a comma. - // If the omitted size would be the only element, then skip it unless sizes were expected. - if (((list.Count & 1) == 0)) + var omittedArraySizeExpressionInstance = _syntaxFactory.OmittedArraySizeExpression(SyntaxFactory.Token(SyntaxKind.OmittedArraySizeExpressionToken)); + int lastTokenPosition = -1; + while (IsMakingProgress(ref lastTokenPosition) && this.CurrentToken.Kind != SyntaxKind.CloseBracketToken) + { + if (this.CurrentToken.Kind == SyntaxKind.CommaToken) { + // NOTE: trivia will be attached to comma, not omitted array size sawOmittedSize = true; list.Add(omittedArraySizeExpressionInstance); + list.AddSeparator(this.EatToken()); } - - // Never mix omitted and non-omitted array sizes. If there were non-omitted array sizes, - // then convert all of the omitted array sizes to missing identifiers. - if (sawOmittedSize && sawNonOmittedSize) + else if (this.IsPossibleExpression()) { - for (int i = 0; i < list.Count; i++) + var size = this.ParseExpressionCore(); + sawNonOmittedSize = true; + list.Add(size); + + if (this.CurrentToken.Kind != SyntaxKind.CloseBracketToken) { - if (list[i].RawKind == (int)SyntaxKind.OmittedArraySizeExpression) - { - int width = list[i].Width; - int offset = list[i].GetLeadingTriviaWidth(); - list[i] = this.AddError(this.CreateMissingIdentifierName(), offset, width, ErrorCode.ERR_ValueExpected); - } + list.AddSeparator(this.EatToken(SyntaxKind.CommaToken)); } } + else if (this.SkipBadArrayRankSpecifierTokens(ref open, list, SyntaxKind.CommaToken) == PostSkipAction.Abort) + { + break; + } + } - // Eat the close brace and we're done. - var close = this.EatToken(SyntaxKind.CloseBracketToken); - return _syntaxFactory.ArrayRankSpecifier(open, list, close); + // Don't end on a comma. + // If the omitted size would be the only element, then skip it unless sizes were expected. + if (((list.Count & 1) == 0)) + { + sawOmittedSize = true; + list.Add(omittedArraySizeExpressionInstance); } - finally + + // Never mix omitted and non-omitted array sizes. If there were non-omitted array sizes, + // then convert all of the omitted array sizes to missing identifiers. + if (sawOmittedSize && sawNonOmittedSize) { - _pool.Free(list); + for (int i = 0; i < list.Count; i++) + { + if (list[i].RawKind == (int)SyntaxKind.OmittedArraySizeExpression) + { + int width = list[i].Width; + int offset = list[i].GetLeadingTriviaWidth(); + list[i] = this.AddError(this.CreateMissingIdentifierName(), offset, width, ErrorCode.ERR_ValueExpected); + } + } } + + return _syntaxFactory.ArrayRankSpecifier( + open, + _pool.ToListAndFree(list), + this.EatToken(SyntaxKind.CloseBracketToken)); } private TupleTypeSyntax ParseTupleType() { var open = this.EatToken(SyntaxKind.OpenParenToken); var list = _pool.AllocateSeparated(); - try + + if (this.CurrentToken.Kind != SyntaxKind.CloseParenToken) { - if (this.CurrentToken.Kind != SyntaxKind.CloseParenToken) - { - var element = ParseTupleElement(); - list.Add(element); + var element = ParseTupleElement(); + list.Add(element); - while (this.CurrentToken.Kind == SyntaxKind.CommaToken) - { - var comma = this.EatToken(SyntaxKind.CommaToken); - list.AddSeparator(comma); + while (this.CurrentToken.Kind == SyntaxKind.CommaToken) + { + var comma = this.EatToken(SyntaxKind.CommaToken); + list.AddSeparator(comma); - element = ParseTupleElement(); - list.Add(element); - } + element = ParseTupleElement(); + list.Add(element); } + } - if (list.Count < 2) + if (list.Count < 2) + { + if (list.Count < 1) { - if (list.Count < 1) - { - list.Add(_syntaxFactory.TupleElement(this.CreateMissingIdentifierName(), identifier: null)); - } - - list.AddSeparator(SyntaxFactory.MissingToken(SyntaxKind.CommaToken)); - var missing = this.AddError(this.CreateMissingIdentifierName(), ErrorCode.ERR_TupleTooFewElements); - list.Add(_syntaxFactory.TupleElement(missing, identifier: null)); + list.Add(_syntaxFactory.TupleElement(this.CreateMissingIdentifierName(), identifier: null)); } - var close = this.EatToken(SyntaxKind.CloseParenToken); - var result = _syntaxFactory.TupleType(open, list, close); + list.AddSeparator(SyntaxFactory.MissingToken(SyntaxKind.CommaToken)); + var missing = this.AddError(this.CreateMissingIdentifierName(), ErrorCode.ERR_TupleTooFewElements); + list.Add(_syntaxFactory.TupleElement(missing, identifier: null)); + } - result = CheckFeatureAvailability(result, MessageID.IDS_FeatureTuples); + var result = _syntaxFactory.TupleType( + open, + _pool.ToListAndFree(list), + this.EatToken(SyntaxKind.CloseParenToken)); - return result; - } - finally - { - _pool.Free(list); - } + return CheckFeatureAvailability(result, MessageID.IDS_FeatureTuples); } private TupleElementSyntax ParseTupleElement() { - var type = ParseType(); - SyntaxToken name = null; - - if (IsTrueIdentifier()) - { - name = this.ParseIdentifierToken(); - } - - return _syntaxFactory.TupleElement(type, name); + return _syntaxFactory.TupleElement( + ParseType(), + IsTrueIdentifier() ? this.ParseIdentifierToken() : null); } private PostSkipAction SkipBadArrayRankSpecifierTokens(ref SyntaxToken openBracket, SeparatedSyntaxListBuilder list, SyntaxKind expected) @@ -7763,7 +7622,7 @@ private FunctionPointerTypeSyntax ParseFunctionPointerTypeSyntax() callingConvention, SyntaxFactory.FunctionPointerParameterList( lessThanToken, - types, + _pool.ToListAndFree(types), lessThanToken.IsMissing && CurrentToken.Kind == SyntaxKind.CloseParenToken ? EatTokenAsKind(SyntaxKind.GreaterThanToken) : EatToken(SyntaxKind.GreaterThanToken))); @@ -7771,7 +7630,6 @@ private FunctionPointerTypeSyntax ParseFunctionPointerTypeSyntax() finally { _termState = saveTerm; - _pool.Free(types); } PostSkipAction skipBadFunctionPointerTokens(SeparatedSyntaxListBuilder list) where T : CSharpSyntaxNode @@ -7839,12 +7697,13 @@ PostSkipAction skipBadFunctionPointerTokens(SeparatedSyntaxListBuilder lis var closeBracket = EatToken(SyntaxKind.CloseBracketToken); - unmanagedCallingConventions = SyntaxFactory.FunctionPointerUnmanagedCallingConventionList(openBracket, callingConventionModifiers, closeBracket); + unmanagedCallingConventions = SyntaxFactory.FunctionPointerUnmanagedCallingConventionList( + openBracket, + _pool.ToListAndFree(callingConventionModifiers), closeBracket); } finally { _termState = saveTerm; - _pool.Free(callingConventionModifiers); } } @@ -7972,7 +7831,6 @@ private StatementSyntax ParseStatementCore(SyntaxList attri { _recursionDepth--; this.Release(ref resetPointBeforeStatement); - } bool canReuseStatement(SyntaxList attributes, bool isGlobal) @@ -8624,7 +8482,9 @@ private BlockSyntax ParseMethodOrAccessorBodyBlock(SyntaxList var decl = ParseVariableDeclaration(); _termState = saveTerm; - var closeParen = this.EatToken(SyntaxKind.CloseParenToken); - StatementSyntax statement = this.ParseEmbeddedStatement(); - return _syntaxFactory.FixedStatement(attributes, @fixed, openParen, decl, closeParen, statement); + return _syntaxFactory.FixedStatement( + attributes, + @fixed, + openParen, + decl, + this.EatToken(SyntaxKind.CloseParenToken), + this.ParseEmbeddedStatement()); } private bool IsEndOfFixedStatement() { - return this.CurrentToken.Kind == SyntaxKind.CloseParenToken - || this.CurrentToken.Kind == SyntaxKind.OpenBraceToken - || this.CurrentToken.Kind == SyntaxKind.SemicolonToken; + return this.CurrentToken.Kind is SyntaxKind.CloseParenToken or SyntaxKind.OpenBraceToken or SyntaxKind.SemicolonToken; } private StatementSyntax ParseEmbeddedStatement() @@ -8880,16 +8742,18 @@ StatementSyntax parseEmbeddedStatementRest(StatementSyntax statement) private BreakStatementSyntax ParseBreakStatement(SyntaxList attributes) { - var breakKeyword = this.EatToken(SyntaxKind.BreakKeyword); - var semicolon = this.EatToken(SyntaxKind.SemicolonToken); - return _syntaxFactory.BreakStatement(attributes, breakKeyword, semicolon); + return _syntaxFactory.BreakStatement( + attributes, + this.EatToken(SyntaxKind.BreakKeyword), + this.EatToken(SyntaxKind.SemicolonToken)); } private ContinueStatementSyntax ParseContinueStatement(SyntaxList attributes) { - var continueKeyword = this.EatToken(SyntaxKind.ContinueKeyword); - var semicolon = this.EatToken(SyntaxKind.SemicolonToken); - return _syntaxFactory.ContinueStatement(attributes, continueKeyword, semicolon); + return _syntaxFactory.ContinueStatement( + attributes, + this.EatToken(SyntaxKind.ContinueKeyword), + this.EatToken(SyntaxKind.SemicolonToken)); } private TryStatementSyntax ParseTryStatement(SyntaxList attributes) @@ -8968,9 +8832,7 @@ BlockSyntax missingBlock() private bool IsEndOfTryBlock() { - return this.CurrentToken.Kind == SyntaxKind.CloseBraceToken - || this.CurrentToken.Kind == SyntaxKind.CatchKeyword - || this.CurrentToken.Kind == SyntaxKind.FinallyKeyword; + return this.CurrentToken.Kind is SyntaxKind.CloseBraceToken or SyntaxKind.CatchKeyword or SyntaxKind.FinallyKeyword; } private CatchClauseSyntax ParseCatchClause() @@ -9034,31 +8896,31 @@ private CatchClauseSyntax ParseCatchClause() private bool IsEndOfCatchClause() { - return this.CurrentToken.Kind == SyntaxKind.CloseParenToken - || this.CurrentToken.Kind == SyntaxKind.OpenBraceToken - || this.CurrentToken.Kind == SyntaxKind.CloseBraceToken - || this.CurrentToken.Kind == SyntaxKind.CatchKeyword - || this.CurrentToken.Kind == SyntaxKind.FinallyKeyword; + return this.CurrentToken.Kind is SyntaxKind.CloseParenToken + or SyntaxKind.OpenBraceToken + or SyntaxKind.CloseBraceToken + or SyntaxKind.CatchKeyword + or SyntaxKind.FinallyKeyword; } private bool IsEndOfFilterClause() { - return this.CurrentToken.Kind == SyntaxKind.CloseParenToken - || this.CurrentToken.Kind == SyntaxKind.OpenBraceToken - || this.CurrentToken.Kind == SyntaxKind.CloseBraceToken - || this.CurrentToken.Kind == SyntaxKind.CatchKeyword - || this.CurrentToken.Kind == SyntaxKind.FinallyKeyword; + return this.CurrentToken.Kind is SyntaxKind.CloseParenToken + or SyntaxKind.OpenBraceToken + or SyntaxKind.CloseBraceToken + or SyntaxKind.CatchKeyword + or SyntaxKind.FinallyKeyword; } private bool IsEndOfCatchBlock() { - return this.CurrentToken.Kind == SyntaxKind.CloseBraceToken - || this.CurrentToken.Kind == SyntaxKind.CatchKeyword - || this.CurrentToken.Kind == SyntaxKind.FinallyKeyword; + return this.CurrentToken.Kind is SyntaxKind.CloseBraceToken + or SyntaxKind.CatchKeyword + or SyntaxKind.FinallyKeyword; } private StatementSyntax ParseCheckedStatement(SyntaxList attributes) { - Debug.Assert(this.CurrentToken.Kind == SyntaxKind.CheckedKeyword || this.CurrentToken.Kind == SyntaxKind.UncheckedKeyword); + Debug.Assert(this.CurrentToken.Kind is SyntaxKind.CheckedKeyword or SyntaxKind.UncheckedKeyword); if (this.PeekToken(1).Kind == SyntaxKind.OpenParenToken) { @@ -9066,8 +8928,11 @@ private StatementSyntax ParseCheckedStatement(SyntaxList at } var spec = this.EatToken(); - var block = this.ParsePossiblyAttributedBlock(); - return _syntaxFactory.CheckedStatement(SyntaxFacts.GetCheckStatement(spec.Kind), attributes, spec, block); + return _syntaxFactory.CheckedStatement( + SyntaxFacts.GetCheckStatement(spec.Kind), + attributes, + spec, + this.ParsePossiblyAttributedBlock()); } private DoStatementSyntax ParseDoStatement(SyntaxList attributes) @@ -9083,15 +8948,20 @@ private DoStatementSyntax ParseDoStatement(SyntaxList attri var expression = this.ParseExpressionCore(); _termState = saveTerm; - var closeParen = this.EatToken(SyntaxKind.CloseParenToken); - var semicolon = this.EatToken(SyntaxKind.SemicolonToken); - return _syntaxFactory.DoStatement(attributes, @do, statement, @while, openParen, expression, closeParen, semicolon); + return _syntaxFactory.DoStatement( + attributes, + @do, + statement, + @while, + openParen, + expression, + this.EatToken(SyntaxKind.CloseParenToken), + this.EatToken(SyntaxKind.SemicolonToken)); } private bool IsEndOfDoWhileExpression() { - return this.CurrentToken.Kind == SyntaxKind.CloseParenToken - || this.CurrentToken.Kind == SyntaxKind.SemicolonToken; + return this.CurrentToken.Kind is SyntaxKind.CloseParenToken or SyntaxKind.SemicolonToken; } private StatementSyntax ParseForOrForEachStatement(SyntaxList attributes) @@ -9225,24 +9095,29 @@ private ForStatementSyntax ParseForStatement(SyntaxList att this.ParseForStatementExpressionList(ref semi2, incrementors); } - var closeParen = this.EatToken(SyntaxKind.CloseParenToken); - var statement = ParseEmbeddedStatement(); - return _syntaxFactory.ForStatement(attributes, forToken, openParen, decl, initializers, semi, condition, semi2, incrementors, closeParen, statement); + return _syntaxFactory.ForStatement( + attributes, + forToken, + openParen, + decl, + _pool.ToListAndFree(initializers), + semi, + condition, + semi2, + _pool.ToListAndFree(incrementors), + this.EatToken(SyntaxKind.CloseParenToken), + ParseEmbeddedStatement()); } finally { _termState = saveTerm; this.Release(ref resetPoint); - _pool.Free(incrementors); - _pool.Free(initializers); } } private bool IsEndOfForStatementArgument() { - return this.CurrentToken.Kind == SyntaxKind.SemicolonToken - || this.CurrentToken.Kind == SyntaxKind.CloseParenToken - || this.CurrentToken.Kind == SyntaxKind.OpenBraceToken; + return this.CurrentToken.Kind is SyntaxKind.SemicolonToken or SyntaxKind.CloseParenToken or SyntaxKind.OpenBraceToken; } private void ParseForStatementExpressionList(ref SyntaxToken startToken, SeparatedSyntaxListBuilder list) @@ -9546,8 +9421,8 @@ private GotoStatementSyntax ParseGotoStatement(SyntaxList a arg = this.ParseIdentifierName(); } - var semicolon = this.EatToken(SyntaxKind.SemicolonToken); - return _syntaxFactory.GotoStatement(kind, attributes, @goto, caseOrDefault, arg, semicolon); + return _syntaxFactory.GotoStatement( + kind, attributes, @goto, caseOrDefault, arg, this.EatToken(SyntaxKind.SemicolonToken)); } private IfStatementSyntax ParseIfStatement(SyntaxList attributes) @@ -9585,20 +9460,21 @@ private ElseClauseSyntax ParseElseClauseOpt() return null; } - var elseToken = this.EatToken(SyntaxKind.ElseKeyword); - var elseStatement = this.ParseEmbeddedStatement(); - return _syntaxFactory.ElseClause(elseToken, elseStatement); + return _syntaxFactory.ElseClause( + this.EatToken(SyntaxKind.ElseKeyword), + this.ParseEmbeddedStatement()); } private LockStatementSyntax ParseLockStatement(SyntaxList attributes) { Debug.Assert(this.CurrentToken.Kind == SyntaxKind.LockKeyword); - var @lock = this.EatToken(SyntaxKind.LockKeyword); - var openParen = this.EatToken(SyntaxKind.OpenParenToken); - var expression = this.ParseExpressionCore(); - var closeParen = this.EatToken(SyntaxKind.CloseParenToken); - var statement = this.ParseEmbeddedStatement(); - return _syntaxFactory.LockStatement(attributes, @lock, openParen, expression, closeParen, statement); + return _syntaxFactory.LockStatement( + attributes, + this.EatToken(SyntaxKind.LockKeyword), + this.EatToken(SyntaxKind.OpenParenToken), + this.ParseExpressionCore(), + this.EatToken(SyntaxKind.CloseParenToken), + this.ParseEmbeddedStatement()); } private ReturnStatementSyntax ParseReturnStatement(SyntaxList attributes) @@ -9611,8 +9487,11 @@ private ReturnStatementSyntax ParseReturnStatement(SyntaxList attributes) @@ -9643,8 +9522,13 @@ private YieldStatementSyntax ParseYieldStatement(SyntaxList } } - var semi = this.EatToken(SyntaxKind.SemicolonToken); - return _syntaxFactory.YieldStatement(kind, attributes, yieldToken, returnOrBreak, arg, semi); + return _syntaxFactory.YieldStatement( + kind, + attributes, + yieldToken, + returnOrBreak, + arg, + this.EatToken(SyntaxKind.SemicolonToken)); } private SwitchStatementSyntax ParseSwitchStatement(SyntaxList attributes) @@ -9681,21 +9565,21 @@ private SwitchStatementSyntax ParseSwitchStatement(SyntaxList(); - try - { - while (this.IsPossibleSwitchSection()) - { - var swcase = this.ParseSwitchSection(); - sections.Add(swcase); - } - var closeBrace = this.EatToken(SyntaxKind.CloseBraceToken); - return _syntaxFactory.SwitchStatement(attributes, @switch, openParen, expression, closeParen, openBrace, sections, closeBrace); - } - finally + while (this.IsPossibleSwitchSection()) { - _pool.Free(sections); + sections.Add(this.ParseSwitchSection()); } + + return _syntaxFactory.SwitchStatement( + attributes, + @switch, + openParen, + expression, + closeParen, + openBrace, + _pool.ToListAndFree(sections), + this.EatToken(SyntaxKind.CloseBraceToken)); } private bool IsPossibleSwitchSection() @@ -9711,96 +9595,88 @@ private SwitchSectionSyntax ParseSwitchSection() // First, parse case label(s) var labels = _pool.Allocate(); var statements = _pool.Allocate(); - try + + do { - do + SwitchLabelSyntax label; + if (this.CurrentToken.Kind == SyntaxKind.CaseKeyword) { - SyntaxToken specifier; - SwitchLabelSyntax label; - SyntaxToken colon; - if (this.CurrentToken.Kind == SyntaxKind.CaseKeyword) + var caseKeyword = this.EatToken(); + + if (this.CurrentToken.Kind == SyntaxKind.ColonToken) { - ExpressionSyntax expression; - specifier = this.EatToken(); + label = _syntaxFactory.CaseSwitchLabel( + caseKeyword, + ParseIdentifierName(ErrorCode.ERR_ConstantExpected), + this.EatToken(SyntaxKind.ColonToken)); + } + else + { + var node = ParseExpressionOrPatternForSwitchStatement(); + + // if there is a 'when' token, we treat a case expression as a constant pattern. + if (this.CurrentToken.ContextualKind == SyntaxKind.WhenKeyword && node is ExpressionSyntax ex) + node = _syntaxFactory.ConstantPattern(ex); - if (this.CurrentToken.Kind == SyntaxKind.ColonToken) + if (node.Kind == SyntaxKind.DiscardPattern) + node = this.AddError(node, ErrorCode.ERR_DiscardPatternInSwitchStatement); + + if (node is PatternSyntax pat) { - expression = ParseIdentifierName(ErrorCode.ERR_ConstantExpected); - colon = this.EatToken(SyntaxKind.ColonToken); - label = _syntaxFactory.CaseSwitchLabel(specifier, expression, colon); + label = _syntaxFactory.CasePatternSwitchLabel( + caseKeyword, + pat, + ParseWhenClause(Precedence.Expression), + this.EatToken(SyntaxKind.ColonToken)); } else { - var node = ParseExpressionOrPatternForSwitchStatement(); - - // if there is a 'when' token, we treat a case expression as a constant pattern. - if (this.CurrentToken.ContextualKind == SyntaxKind.WhenKeyword && node is ExpressionSyntax ex) - node = _syntaxFactory.ConstantPattern(ex); - - if (node.Kind == SyntaxKind.DiscardPattern) - node = this.AddError(node, ErrorCode.ERR_DiscardPatternInSwitchStatement); - - if (node is PatternSyntax pat) - { - label = _syntaxFactory.CasePatternSwitchLabel( - specifier, - pat, - ParseWhenClause(Precedence.Expression), - this.EatToken(SyntaxKind.ColonToken)); - } - else - { - colon = this.EatToken(SyntaxKind.ColonToken); - label = _syntaxFactory.CaseSwitchLabel(specifier, (ExpressionSyntax)node, colon); - } + label = _syntaxFactory.CaseSwitchLabel( + caseKeyword, + (ExpressionSyntax)node, + this.EatToken(SyntaxKind.ColonToken)); } } - else - { - Debug.Assert(this.CurrentToken.Kind == SyntaxKind.DefaultKeyword); - specifier = this.EatToken(SyntaxKind.DefaultKeyword); - colon = this.EatToken(SyntaxKind.ColonToken); - label = _syntaxFactory.DefaultSwitchLabel(specifier, colon); - } - - labels.Add(label); } - while (IsPossibleSwitchSection()); - - // Next, parse statement list stopping for new sections - CSharpSyntaxNode tmp = labels[labels.Count - 1]; - this.ParseStatements(ref tmp, statements, true); - labels[labels.Count - 1] = (SwitchLabelSyntax)tmp; + else + { + Debug.Assert(this.CurrentToken.Kind == SyntaxKind.DefaultKeyword); + label = _syntaxFactory.DefaultSwitchLabel( + this.EatToken(SyntaxKind.DefaultKeyword), + this.EatToken(SyntaxKind.ColonToken)); + } - return _syntaxFactory.SwitchSection(labels, statements); - } - finally - { - _pool.Free(statements); - _pool.Free(labels); + labels.Add(label); } + while (IsPossibleSwitchSection()); + + // Next, parse statement list stopping for new sections + CSharpSyntaxNode tmp = labels[labels.Count - 1]; + this.ParseStatements(ref tmp, statements, true); + labels[labels.Count - 1] = (SwitchLabelSyntax)tmp; + + return _syntaxFactory.SwitchSection( + _pool.ToListAndFree(labels), + _pool.ToListAndFree(statements)); } private ThrowStatementSyntax ParseThrowStatement(SyntaxList attributes) { Debug.Assert(this.CurrentToken.Kind == SyntaxKind.ThrowKeyword); - var @throw = this.EatToken(SyntaxKind.ThrowKeyword); - ExpressionSyntax arg = null; - if (this.CurrentToken.Kind != SyntaxKind.SemicolonToken) - { - arg = this.ParseExpressionCore(); - } - - var semi = this.EatToken(SyntaxKind.SemicolonToken); - return _syntaxFactory.ThrowStatement(attributes, @throw, arg, semi); + return _syntaxFactory.ThrowStatement( + attributes, + this.EatToken(SyntaxKind.ThrowKeyword), + this.CurrentToken.Kind != SyntaxKind.SemicolonToken ? this.ParseExpressionCore() : null, + this.EatToken(SyntaxKind.SemicolonToken)); } private UnsafeStatementSyntax ParseUnsafeStatement(SyntaxList attributes) { Debug.Assert(this.CurrentToken.Kind == SyntaxKind.UnsafeKeyword); - var @unsafe = this.EatToken(SyntaxKind.UnsafeKeyword); - var block = this.ParsePossiblyAttributedBlock(); - return _syntaxFactory.UnsafeStatement(attributes, @unsafe, block); + return _syntaxFactory.UnsafeStatement( + attributes, + this.EatToken(SyntaxKind.UnsafeKeyword), + this.ParsePossiblyAttributedBlock()); } private UsingStatementSyntax ParseUsingStatement(SyntaxList attributes, SyntaxToken awaitTokenOpt = null) @@ -9815,10 +9691,15 @@ private UsingStatementSyntax ParseUsingStatement(SyntaxList ParseUsingExpression(ref declaration, ref expression, ref resetPoint); this.Release(ref resetPoint); - var closeParen = this.EatToken(SyntaxKind.CloseParenToken); - var statement = this.ParseEmbeddedStatement(); - - return _syntaxFactory.UsingStatement(attributes, awaitTokenOpt, @using, openParen, declaration, expression, closeParen, statement); + return _syntaxFactory.UsingStatement( + attributes, + awaitTokenOpt, + @using, + openParen, + declaration, + expression, + this.EatToken(SyntaxKind.CloseParenToken), + this.ParseEmbeddedStatement()); } private void ParseUsingExpression(ref VariableDeclarationSyntax declaration, ref ExpressionSyntax expression, ref ResetPoint resetPoint) @@ -9929,12 +9810,13 @@ private bool IsUsingStatementVariableDeclaration(ScanTypeFlags st) private WhileStatementSyntax ParseWhileStatement(SyntaxList attributes) { Debug.Assert(this.CurrentToken.Kind == SyntaxKind.WhileKeyword); - var @while = this.EatToken(SyntaxKind.WhileKeyword); - var openParen = this.EatToken(SyntaxKind.OpenParenToken); - var condition = this.ParseExpressionCore(); - var closeParen = this.EatToken(SyntaxKind.CloseParenToken); - var statement = this.ParseEmbeddedStatement(); - return _syntaxFactory.WhileStatement(attributes, @while, openParen, condition, closeParen, statement); + return _syntaxFactory.WhileStatement( + attributes, + this.EatToken(SyntaxKind.WhileKeyword), + this.EatToken(SyntaxKind.OpenParenToken), + this.ParseExpressionCore(), + this.EatToken(SyntaxKind.CloseParenToken), + this.ParseEmbeddedStatement()); } private LabeledStatementSyntax ParseLabeledStatement(SyntaxList attributes) @@ -10039,12 +9921,11 @@ private StatementSyntax ParseLocalDeclarationStatement(SyntaxList @@ -11561,8 +11442,7 @@ private ExpressionSyntax ParsePostFixExpression(ExpressionSyntax expr) private static bool CanStartConsequenceExpression(SyntaxKind kind) { - return kind == SyntaxKind.DotToken || - kind == SyntaxKind.OpenBracketToken; + return kind is SyntaxKind.DotToken or SyntaxKind.OpenBracketToken; } internal ExpressionSyntax ParseConsequenceSyntax() @@ -11787,8 +11667,7 @@ private PostSkipAction SkipBadArgumentListTokens(ref SyntaxToken open, Separated private bool IsEndOfArgumentList() { - return this.CurrentToken.Kind == SyntaxKind.CloseParenToken - || this.CurrentToken.Kind == SyntaxKind.CloseBracketToken; + return this.CurrentToken.Kind is SyntaxKind.CloseParenToken or SyntaxKind.CloseBracketToken; } private bool IsPossibleArgumentExpression() @@ -11856,12 +11735,11 @@ private ArgumentSyntax ParseArgumentExpression(bool isIndexer) private TypeOfExpressionSyntax ParseTypeOfExpression() { - var keyword = this.EatToken(); - var openParen = this.EatToken(SyntaxKind.OpenParenToken); - var type = this.ParseTypeOrVoid(); - var closeParen = this.EatToken(SyntaxKind.CloseParenToken); - - return _syntaxFactory.TypeOfExpression(keyword, openParen, type, closeParen); + return _syntaxFactory.TypeOfExpression( + this.EatToken(), + this.EatToken(SyntaxKind.OpenParenToken), + this.ParseTypeOrVoid(), + this.EatToken(SyntaxKind.CloseParenToken)); } private ExpressionSyntax ParseDefaultExpression() @@ -11883,32 +11761,29 @@ private ExpressionSyntax ParseDefaultExpression() private SizeOfExpressionSyntax ParseSizeOfExpression() { - var keyword = this.EatToken(); - var openParen = this.EatToken(SyntaxKind.OpenParenToken); - var type = this.ParseType(); - var closeParen = this.EatToken(SyntaxKind.CloseParenToken); - - return _syntaxFactory.SizeOfExpression(keyword, openParen, type, closeParen); + return _syntaxFactory.SizeOfExpression( + this.EatToken(), + this.EatToken(SyntaxKind.OpenParenToken), + this.ParseType(), + this.EatToken(SyntaxKind.CloseParenToken)); } private MakeRefExpressionSyntax ParseMakeRefExpression() { - var keyword = this.EatToken(); - var openParen = this.EatToken(SyntaxKind.OpenParenToken); - var expr = this.ParseSubExpression(Precedence.Expression); - var closeParen = this.EatToken(SyntaxKind.CloseParenToken); - - return _syntaxFactory.MakeRefExpression(keyword, openParen, expr, closeParen); + return _syntaxFactory.MakeRefExpression( + this.EatToken(), + this.EatToken(SyntaxKind.OpenParenToken), + this.ParseSubExpression(Precedence.Expression), + this.EatToken(SyntaxKind.CloseParenToken)); } private RefTypeExpressionSyntax ParseRefTypeExpression() { - var keyword = this.EatToken(); - var openParen = this.EatToken(SyntaxKind.OpenParenToken); - var expr = this.ParseSubExpression(Precedence.Expression); - var closeParen = this.EatToken(SyntaxKind.CloseParenToken); - - return _syntaxFactory.RefTypeExpression(keyword, openParen, expr, closeParen); + return _syntaxFactory.RefTypeExpression( + this.EatToken(), + this.EatToken(SyntaxKind.OpenParenToken), + this.ParseSubExpression(Precedence.Expression), + this.EatToken(SyntaxKind.CloseParenToken)); } private CheckedExpressionSyntax ParseCheckedOrUncheckedExpression() @@ -11917,23 +11792,23 @@ private CheckedExpressionSyntax ParseCheckedOrUncheckedExpression() Debug.Assert(checkedOrUnchecked.Kind == SyntaxKind.CheckedKeyword || checkedOrUnchecked.Kind == SyntaxKind.UncheckedKeyword); var kind = (checkedOrUnchecked.Kind == SyntaxKind.CheckedKeyword) ? SyntaxKind.CheckedExpression : SyntaxKind.UncheckedExpression; - var openParen = this.EatToken(SyntaxKind.OpenParenToken); - var expr = this.ParseSubExpression(Precedence.Expression); - var closeParen = this.EatToken(SyntaxKind.CloseParenToken); - - return _syntaxFactory.CheckedExpression(kind, checkedOrUnchecked, openParen, expr, closeParen); + return _syntaxFactory.CheckedExpression( + kind, + checkedOrUnchecked, + this.EatToken(SyntaxKind.OpenParenToken), + this.ParseSubExpression(Precedence.Expression), + this.EatToken(SyntaxKind.CloseParenToken)); } private RefValueExpressionSyntax ParseRefValueExpression() { - var @refvalue = this.EatToken(SyntaxKind.RefValueKeyword); - var openParen = this.EatToken(SyntaxKind.OpenParenToken); - var expr = this.ParseSubExpression(Precedence.Expression); - var comma = this.EatToken(SyntaxKind.CommaToken); - var type = this.ParseType(); - var closeParen = this.EatToken(SyntaxKind.CloseParenToken); - - return _syntaxFactory.RefValueExpression(@refvalue, openParen, expr, comma, type, closeParen); + return _syntaxFactory.RefValueExpression( + this.EatToken(SyntaxKind.RefValueKeyword), + this.EatToken(SyntaxKind.OpenParenToken), + this.ParseSubExpression(Precedence.Expression), + this.EatToken(SyntaxKind.CommaToken), + this.ParseType(), + this.EatToken(SyntaxKind.CloseParenToken)); } private bool ScanParenthesizedLambda(Precedence precedence) @@ -12180,49 +12055,43 @@ private ExpressionSyntax ParseCastOrParenExpressionOrTuple() private TupleExpressionSyntax ParseTupleExpressionTail(SyntaxToken openParen, ArgumentSyntax firstArg) { var list = _pool.AllocateSeparated(); - try - { - list.Add(firstArg); + list.Add(firstArg); - while (this.CurrentToken.Kind == SyntaxKind.CommaToken) - { - var comma = this.EatToken(SyntaxKind.CommaToken); - list.AddSeparator(comma); - - ArgumentSyntax arg; + while (this.CurrentToken.Kind == SyntaxKind.CommaToken) + { + var comma = this.EatToken(SyntaxKind.CommaToken); + list.AddSeparator(comma); - var expression = ParseExpressionOrDeclaration(ParseTypeMode.AfterTupleComma, feature: 0, permitTupleDesignation: true); - if (expression.Kind == SyntaxKind.IdentifierName && this.CurrentToken.Kind == SyntaxKind.ColonToken) - { - var nameColon = _syntaxFactory.NameColon((IdentifierNameSyntax)expression, EatToken()); - expression = ParseExpressionOrDeclaration(ParseTypeMode.AfterTupleComma, feature: 0, permitTupleDesignation: true); - arg = _syntaxFactory.Argument(nameColon, refKindKeyword: null, expression: expression); - } - else - { - arg = _syntaxFactory.Argument(nameColon: null, refKindKeyword: null, expression: expression); - } + ArgumentSyntax arg; - list.Add(arg); + var expression = ParseExpressionOrDeclaration(ParseTypeMode.AfterTupleComma, feature: 0, permitTupleDesignation: true); + if (expression.Kind == SyntaxKind.IdentifierName && this.CurrentToken.Kind == SyntaxKind.ColonToken) + { + var nameColon = _syntaxFactory.NameColon((IdentifierNameSyntax)expression, EatToken()); + expression = ParseExpressionOrDeclaration(ParseTypeMode.AfterTupleComma, feature: 0, permitTupleDesignation: true); + arg = _syntaxFactory.Argument(nameColon, refKindKeyword: null, expression: expression); } - - if (list.Count < 2) + else { - list.AddSeparator(SyntaxFactory.MissingToken(SyntaxKind.CommaToken)); - var missing = this.AddError(this.CreateMissingIdentifierName(), ErrorCode.ERR_TupleTooFewElements); - list.Add(_syntaxFactory.Argument(nameColon: null, refKindKeyword: null, expression: missing)); + arg = _syntaxFactory.Argument(nameColon: null, refKindKeyword: null, expression: expression); } - var closeParen = this.EatToken(SyntaxKind.CloseParenToken); - var result = _syntaxFactory.TupleExpression(openParen, list, closeParen); - - result = CheckFeatureAvailability(result, MessageID.IDS_FeatureTuples); - return result; + list.Add(arg); } - finally + + if (list.Count < 2) { - _pool.Free(list); + list.AddSeparator(SyntaxFactory.MissingToken(SyntaxKind.CommaToken)); + var missing = this.AddError(this.CreateMissingIdentifierName(), ErrorCode.ERR_TupleTooFewElements); + list.Add(_syntaxFactory.Argument(nameColon: null, refKindKeyword: null, expression: missing)); } + + var result = _syntaxFactory.TupleExpression( + openParen, + _pool.ToListAndFree(list), + this.EatToken(SyntaxKind.CloseParenToken)); + + return CheckFeatureAvailability(result, MessageID.IDS_FeatureTuples); } private bool ScanCast(bool forPattern = false) @@ -12569,11 +12438,11 @@ private AnonymousObjectCreationExpressionSyntax ParseAnonymousTypeExpression() var openBrace = this.EatToken(SyntaxKind.OpenBraceToken); var expressions = _pool.AllocateSeparated(); this.ParseAnonymousTypeMemberInitializers(ref openBrace, ref expressions); - var closeBrace = this.EatToken(SyntaxKind.CloseBraceToken); - var result = _syntaxFactory.AnonymousObjectCreationExpression(@new, openBrace, expressions, closeBrace); - _pool.Free(expressions); - - return result; + return _syntaxFactory.AnonymousObjectCreationExpression( + @new, + openBrace, + _pool.ToListAndFree(expressions), + this.EatToken(SyntaxKind.CloseBraceToken)); } private void ParseAnonymousTypeMemberInitializers(ref SyntaxToken openBrace, ref SeparatedSyntaxListBuilder list) @@ -12626,12 +12495,9 @@ private void ParseAnonymousTypeMemberInitializers(ref SyntaxToken openBrace, ref private AnonymousObjectMemberDeclaratorSyntax ParseAnonymousTypeMemberInitializer() { - var nameEquals = this.IsNamedAssignment() - ? ParseNameEquals() - : null; - - var expression = this.ParseExpressionCore(); - return _syntaxFactory.AnonymousObjectMemberDeclarator(nameEquals, expression); + return _syntaxFactory.AnonymousObjectMemberDeclarator( + this.IsNamedAssignment() ? ParseNameEquals() : null, + this.ParseExpressionCore()); } private bool IsInitializerMember() @@ -12788,20 +12654,14 @@ private ExpressionSyntax ParseWithExpression(ExpressionSyntax receiverExpression } } - var closeBrace = this.EatToken(SyntaxKind.CloseBraceToken); - - var initializer = _syntaxFactory.InitializerExpression( - SyntaxKind.WithInitializerExpression, - openBrace, - _pool.ToListAndFree(list), - closeBrace); - - var result = _syntaxFactory.WithExpression( + return _syntaxFactory.WithExpression( receiverExpression, withKeyword, - initializer); - - return result; + _syntaxFactory.InitializerExpression( + SyntaxKind.WithInitializerExpression, + openBrace, + _pool.ToListAndFree(list), + this.EatToken(SyntaxKind.CloseBraceToken))); } #nullable disable @@ -12811,33 +12671,23 @@ private InitializerExpressionSyntax ParseObjectOrCollectionInitializer() var openBrace = this.EatToken(SyntaxKind.OpenBraceToken); var initializers = _pool.AllocateSeparated(); - try - { - bool isObjectInitializer; - this.ParseObjectOrCollectionInitializerMembers(ref openBrace, initializers, out isObjectInitializer); - Debug.Assert(initializers.Count > 0 || isObjectInitializer); - - openBrace = CheckFeatureAvailability(openBrace, isObjectInitializer ? MessageID.IDS_FeatureObjectInitializer : MessageID.IDS_FeatureCollectionInitializer); + this.ParseObjectOrCollectionInitializerMembers(ref openBrace, initializers, out var kind); + Debug.Assert(initializers.Count > 0 || kind == SyntaxKind.ObjectInitializerExpression); - var closeBrace = this.EatToken(SyntaxKind.CloseBraceToken); - return _syntaxFactory.InitializerExpression( - isObjectInitializer ? - SyntaxKind.ObjectInitializerExpression : - SyntaxKind.CollectionInitializerExpression, - openBrace, - initializers, - closeBrace); - } - finally - { - _pool.Free(initializers); - } + return _syntaxFactory.InitializerExpression( + kind, + openBrace, + _pool.ToListAndFree(initializers), + this.EatToken(SyntaxKind.CloseBraceToken)); } - private void ParseObjectOrCollectionInitializerMembers(ref SyntaxToken startToken, SeparatedSyntaxListBuilder list, out bool isObjectInitializer) + private void ParseObjectOrCollectionInitializerMembers( + ref SyntaxToken startToken, + SeparatedSyntaxListBuilder list, + out SyntaxKind kind) { // Empty initializer list must be parsed as an object initializer. - isObjectInitializer = true; + kind = SyntaxKind.ObjectInitializerExpression; if (this.CurrentToken.Kind != SyntaxKind.CloseBraceToken) { @@ -12847,10 +12697,10 @@ private void ParseObjectOrCollectionInitializerMembers(ref SyntaxToken startToke // We have at least one initializer expression. // If at least one initializer expression is a named assignment, this is an object initializer. // Otherwise, this is a collection initializer. - isObjectInitializer = false; + kind = SyntaxKind.CollectionInitializerExpression; // first argument - list.Add(this.ParseObjectOrCollectionInitializerMember(ref isObjectInitializer)); + list.Add(this.ParseObjectOrCollectionInitializerMember(ref kind)); // additional arguments int lastTokenPosition = -1; @@ -12870,7 +12720,7 @@ private void ParseObjectOrCollectionInitializerMembers(ref SyntaxToken startToke break; } - list.Add(this.ParseObjectOrCollectionInitializerMember(ref isObjectInitializer)); + list.Add(this.ParseObjectOrCollectionInitializerMember(ref kind)); continue; } else if (this.SkipBadInitializerListTokens(ref startToken, list, SyntaxKind.CommaToken) == PostSkipAction.Abort) @@ -12888,7 +12738,7 @@ private void ParseObjectOrCollectionInitializerMembers(ref SyntaxToken startToke // We may have invalid initializer elements. These will be reported during binding. } - private ExpressionSyntax ParseObjectOrCollectionInitializerMember(ref bool isObjectInitializer) + private ExpressionSyntax ParseObjectOrCollectionInitializerMember(ref SyntaxKind kind) { if (this.IsComplexElementInitializer()) { @@ -12900,7 +12750,7 @@ private ExpressionSyntax ParseObjectOrCollectionInitializerMember(ref bool isObj // [...] = { ... } // [...] = ref // [...] = - isObjectInitializer = true; + kind = SyntaxKind.ObjectInitializerExpression; return this.ParseDictionaryInitializer(); } else if (this.IsNamedAssignment()) @@ -12908,7 +12758,7 @@ private ExpressionSyntax ParseObjectOrCollectionInitializerMember(ref bool isObj // Name = { ... } // Name = ref // Name = - isObjectInitializer = true; + kind = SyntaxKind.ObjectInitializerExpression; return this.ParseObjectInitializerNamedAssignment(); } else @@ -12930,53 +12780,44 @@ private PostSkipAction SkipBadInitializerListTokens(ref SyntaxToken startToke private ExpressionSyntax ParseObjectInitializerNamedAssignment() { - var identifier = this.ParseIdentifierName(); - var equal = this.EatToken(SyntaxKind.EqualsToken); - ExpressionSyntax expression; - if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken) - { - expression = this.ParseObjectOrCollectionInitializer(); - } - else - { - expression = this.ParsePossibleRefExpression(); - } - - return _syntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, identifier, equal, expression); + return _syntaxFactory.AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + this.ParseIdentifierName(), + this.EatToken(SyntaxKind.EqualsToken), + this.CurrentToken.Kind == SyntaxKind.OpenBraceToken + ? this.ParseObjectOrCollectionInitializer() + : this.ParsePossibleRefExpression()); } private AssignmentExpressionSyntax ParseDictionaryInitializer() { - var arguments = this.ParseBracketedArgumentList(); - var equal = this.EatToken(SyntaxKind.EqualsToken); - var expression = this.CurrentToken.Kind == SyntaxKind.OpenBraceToken - ? this.ParseObjectOrCollectionInitializer() - : this.ParsePossibleRefExpression(); - - var elementAccess = _syntaxFactory.ImplicitElementAccess(arguments); return _syntaxFactory.AssignmentExpression( - SyntaxKind.SimpleAssignmentExpression, elementAccess, equal, expression); + SyntaxKind.SimpleAssignmentExpression, + _syntaxFactory.ImplicitElementAccess(this.ParseBracketedArgumentList()), + this.EatToken(SyntaxKind.EqualsToken), + this.CurrentToken.Kind == SyntaxKind.OpenBraceToken + ? this.ParseObjectOrCollectionInitializer() + : this.ParsePossibleRefExpression()); } private InitializerExpressionSyntax ParseComplexElementInitializer() { var openBrace = this.EatToken(SyntaxKind.OpenBraceToken); var initializers = _pool.AllocateSeparated(); - try - { - DiagnosticInfo closeBraceError; - this.ParseExpressionsForComplexElementInitializer(ref openBrace, initializers, out closeBraceError); - var closeBrace = this.EatToken(SyntaxKind.CloseBraceToken); - if (closeBraceError != null) - { - closeBrace = WithAdditionalDiagnostics(closeBrace, closeBraceError); - } - return _syntaxFactory.InitializerExpression(SyntaxKind.ComplexElementInitializerExpression, openBrace, initializers, closeBrace); - } - finally + + DiagnosticInfo closeBraceError; + this.ParseExpressionsForComplexElementInitializer(ref openBrace, initializers, out closeBraceError); + var closeBrace = this.EatToken(SyntaxKind.CloseBraceToken); + if (closeBraceError != null) { - _pool.Free(initializers); + closeBrace = WithAdditionalDiagnostics(closeBrace, closeBraceError); } + + return _syntaxFactory.InitializerExpression( + SyntaxKind.ComplexElementInitializerExpression, + openBrace, + _pool.ToListAndFree(initializers), + closeBrace); } private void ParseExpressionsForComplexElementInitializer(ref SyntaxToken openBrace, SeparatedSyntaxListBuilder list, out DiagnosticInfo closeBraceError) @@ -13025,7 +12866,7 @@ private void ParseExpressionsForComplexElementInitializer(ref SyntaxToken openBr private bool IsImplicitlyTypedArray() { - Debug.Assert(this.CurrentToken.Kind == SyntaxKind.NewKeyword || this.CurrentToken.Kind == SyntaxKind.StackAllocKeyword); + Debug.Assert(this.CurrentToken.Kind is SyntaxKind.NewKeyword or SyntaxKind.StackAllocKeyword); return this.PeekToken(1).Kind == SyntaxKind.OpenBracketToken; } @@ -13079,59 +12920,57 @@ private InitializerExpressionSyntax ParseArrayInitializer() // NOTE: This loop allows " { , } " but not " { , } " var list = _pool.AllocateSeparated(); - try + + if (this.CurrentToken.Kind != SyntaxKind.CloseBraceToken) { - if (this.CurrentToken.Kind != SyntaxKind.CloseBraceToken) - { tryAgain: - if (this.IsPossibleVariableInitializer() || this.CurrentToken.Kind == SyntaxKind.CommaToken) - { - list.Add(this.ParseVariableInitializer()); + if (this.IsPossibleVariableInitializer() || this.CurrentToken.Kind == SyntaxKind.CommaToken) + { + list.Add(this.ParseVariableInitializer()); - int lastTokenPosition = -1; - while (IsMakingProgress(ref lastTokenPosition)) + int lastTokenPosition = -1; + while (IsMakingProgress(ref lastTokenPosition)) + { + if (this.CurrentToken.Kind == SyntaxKind.CloseBraceToken) + { + break; + } + else if (this.IsPossibleVariableInitializer() || this.CurrentToken.Kind == SyntaxKind.CommaToken) { + list.AddSeparator(this.EatToken(SyntaxKind.CommaToken)); + + // check for exit case after legal trailing comma if (this.CurrentToken.Kind == SyntaxKind.CloseBraceToken) { break; } - else if (this.IsPossibleVariableInitializer() || this.CurrentToken.Kind == SyntaxKind.CommaToken) + else if (!this.IsPossibleVariableInitializer()) { - list.AddSeparator(this.EatToken(SyntaxKind.CommaToken)); - - // check for exit case after legal trailing comma - if (this.CurrentToken.Kind == SyntaxKind.CloseBraceToken) - { - break; - } - else if (!this.IsPossibleVariableInitializer()) - { - goto tryAgain; - } - - list.Add(this.ParseVariableInitializer()); - continue; - } - else if (SkipBadArrayInitializerTokens(ref openBrace, list, SyntaxKind.CommaToken) == PostSkipAction.Abort) - { - break; + goto tryAgain; } + + list.Add(this.ParseVariableInitializer()); + continue; + } + else if (SkipBadArrayInitializerTokens(ref openBrace, list, SyntaxKind.CommaToken) == PostSkipAction.Abort) + { + break; } } - else if (SkipBadArrayInitializerTokens(ref openBrace, list, SyntaxKind.CommaToken) == PostSkipAction.Continue) - { - goto tryAgain; - } } + else if (SkipBadArrayInitializerTokens(ref openBrace, list, SyntaxKind.CommaToken) == PostSkipAction.Continue) + { + goto tryAgain; + } + } - var closeBrace = this.EatToken(SyntaxKind.CloseBraceToken); + var closeBrace = this.EatToken(SyntaxKind.CloseBraceToken); - return _syntaxFactory.InitializerExpression(SyntaxKind.ArrayInitializerExpression, openBrace, list, closeBrace); - } - finally - { - _pool.Free(list); - } + return _syntaxFactory.InitializerExpression( + SyntaxKind.ArrayInitializerExpression, + openBrace, + _pool.ToListAndFree(list), + closeBrace); } private PostSkipAction SkipBadArrayInitializerTokens(ref SyntaxToken openBrace, SeparatedSyntaxListBuilder list, SyntaxKind expected) @@ -13178,9 +13017,11 @@ private ExpressionSyntax ParseImplicitlyTypedStackAllocExpression() break; } - var closeBracket = this.EatToken(SyntaxKind.CloseBracketToken); - var initializer = this.ParseArrayInitializer(); - return _syntaxFactory.ImplicitStackAllocArrayCreationExpression(@stackalloc, openBracket, closeBracket, initializer); + return _syntaxFactory.ImplicitStackAllocArrayCreationExpression( + @stackalloc, + openBracket, + this.EatToken(SyntaxKind.CloseBracketToken), + this.ParseArrayInitializer()); } private ExpressionSyntax ParseRegularStackAllocExpression() @@ -13238,9 +13079,12 @@ AnonymousMethodExpressionSyntax parseAnonymousMethodExpressionWorker() expressionBody: null); } - var body = this.ParseBlock(attributes: default); return _syntaxFactory.AnonymousMethodExpression( - modifiers, @delegate, parameterList, body, expressionBody: null); + modifiers, + @delegate, + parameterList, + this.ParseBlock(attributes: default), + expressionBody: null); } } @@ -13394,8 +13238,8 @@ LambdaExpressionSyntax parseLambdaExpressionWorker() private (BlockSyntax, ExpressionSyntax) ParseLambdaBody() => CurrentToken.Kind == SyntaxKind.OpenBraceToken - ? (ParseBlock(attributes: default), (ExpressionSyntax)null) - : ((BlockSyntax)null, ParsePossibleRefExpression()); + ? (ParseBlock(attributes: default), null) + : (null, ParsePossibleRefExpression()); private ParameterListSyntax ParseLambdaParameterList() { @@ -13404,53 +13248,48 @@ private ParameterListSyntax ParseLambdaParameterList() _termState |= TerminatorState.IsEndOfParameterList; var nodes = _pool.AllocateSeparated(); - try + if (this.CurrentToken.Kind != SyntaxKind.CloseParenToken) { - if (this.CurrentToken.Kind != SyntaxKind.CloseParenToken) - { tryAgain: - if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleLambdaParameter()) - { - // first parameter - var parameter = this.ParseLambdaParameter(); - nodes.Add(parameter); + if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleLambdaParameter()) + { + // first parameter + var parameter = this.ParseLambdaParameter(); + nodes.Add(parameter); - // additional parameters - int tokenProgress = -1; - while (IsMakingProgress(ref tokenProgress)) + // additional parameters + int tokenProgress = -1; + while (IsMakingProgress(ref tokenProgress)) + { + if (this.CurrentToken.Kind == SyntaxKind.CloseParenToken) { - if (this.CurrentToken.Kind == SyntaxKind.CloseParenToken) - { - break; - } - else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleLambdaParameter()) - { - nodes.AddSeparator(this.EatToken(SyntaxKind.CommaToken)); - parameter = this.ParseLambdaParameter(); - nodes.Add(parameter); - continue; - } - else if (this.SkipBadLambdaParameterListTokens(ref openParen, nodes, SyntaxKind.CommaToken, SyntaxKind.CloseParenToken) == PostSkipAction.Abort) - { - break; - } + break; + } + else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleLambdaParameter()) + { + nodes.AddSeparator(this.EatToken(SyntaxKind.CommaToken)); + parameter = this.ParseLambdaParameter(); + nodes.Add(parameter); + continue; + } + else if (this.SkipBadLambdaParameterListTokens(ref openParen, nodes, SyntaxKind.CommaToken, SyntaxKind.CloseParenToken) == PostSkipAction.Abort) + { + break; } } - else if (this.SkipBadLambdaParameterListTokens(ref openParen, nodes, SyntaxKind.IdentifierToken, SyntaxKind.CloseParenToken) == PostSkipAction.Continue) - { - goto tryAgain; - } } + else if (this.SkipBadLambdaParameterListTokens(ref openParen, nodes, SyntaxKind.IdentifierToken, SyntaxKind.CloseParenToken) == PostSkipAction.Continue) + { + goto tryAgain; + } + } - _termState = saveTerm; - var closeParen = this.EatToken(SyntaxKind.CloseParenToken); + _termState = saveTerm; - return _syntaxFactory.ParameterList(openParen, nodes, closeParen); - } - finally - { - _pool.Free(nodes); - } + return _syntaxFactory.ParameterList( + openParen, + _pool.ToListAndFree(nodes), + this.EatToken(SyntaxKind.CloseParenToken)); } private bool IsPossibleLambdaParameter() @@ -13569,12 +13408,7 @@ private bool ShouldParseLambdaParameterType() } private bool IsCurrentTokenQueryContextualKeyword - { - get - { - return IsTokenQueryContextualKeyword(this.CurrentToken); - } - } + => IsTokenQueryContextualKeyword(this.CurrentToken); private static bool IsTokenQueryContextualKeyword(SyntaxToken token) { @@ -13616,12 +13450,8 @@ private static bool IsTokenStartOfNewQueryClause(SyntaxToken token) private bool IsQueryExpression(bool mayBeVariableDeclaration, bool mayBeMemberDeclaration) { - if (this.CurrentToken.ContextualKind == SyntaxKind.FromKeyword) - { - return this.IsQueryExpressionAfterFrom(mayBeVariableDeclaration, mayBeMemberDeclaration); - } - - return false; + return this.CurrentToken.ContextualKind == SyntaxKind.FromKeyword && + this.IsQueryExpressionAfterFrom(mayBeVariableDeclaration, mayBeMemberDeclaration); } // from_clause ::= from ? in expression @@ -13708,65 +13538,61 @@ private QueryExpressionSyntax ParseQueryExpression(Precedence precedence) private QueryBodySyntax ParseQueryBody() { var clauses = _pool.Allocate(); - try - { - SelectOrGroupClauseSyntax selectOrGroupBy = null; - QueryContinuationSyntax continuation = null; - - // from, join, let, where and orderby - while (true) - { - switch (this.CurrentToken.ContextualKind) - { - case SyntaxKind.FromKeyword: - var fc = this.ParseFromClause(); - clauses.Add(fc); - continue; - case SyntaxKind.JoinKeyword: - clauses.Add(this.ParseJoinClause()); - continue; - case SyntaxKind.LetKeyword: - clauses.Add(this.ParseLetClause()); - continue; - case SyntaxKind.WhereKeyword: - clauses.Add(this.ParseWhereClause()); - continue; - case SyntaxKind.OrderByKeyword: - clauses.Add(this.ParseOrderByClause()); - continue; - } - - break; - } + SelectOrGroupClauseSyntax selectOrGroupBy = null; + QueryContinuationSyntax continuation = null; - // select or group clause + // from, join, let, where and orderby + while (true) + { switch (this.CurrentToken.ContextualKind) { - case SyntaxKind.SelectKeyword: - selectOrGroupBy = this.ParseSelectClause(); - break; - case SyntaxKind.GroupKeyword: - selectOrGroupBy = this.ParseGroupClause(); - break; - default: - selectOrGroupBy = _syntaxFactory.SelectClause( - this.EatToken(SyntaxKind.SelectKeyword, ErrorCode.ERR_ExpectedSelectOrGroup), - this.CreateMissingIdentifierName()); - break; + case SyntaxKind.FromKeyword: + var fc = this.ParseFromClause(); + clauses.Add(fc); + continue; + case SyntaxKind.JoinKeyword: + clauses.Add(this.ParseJoinClause()); + continue; + case SyntaxKind.LetKeyword: + clauses.Add(this.ParseLetClause()); + continue; + case SyntaxKind.WhereKeyword: + clauses.Add(this.ParseWhereClause()); + continue; + case SyntaxKind.OrderByKeyword: + clauses.Add(this.ParseOrderByClause()); + continue; } - // optional query continuation clause - if (this.CurrentToken.ContextualKind == SyntaxKind.IntoKeyword) - { - continuation = this.ParseQueryContinuation(); - } + break; + } - return _syntaxFactory.QueryBody(clauses, selectOrGroupBy, continuation); + // select or group clause + switch (this.CurrentToken.ContextualKind) + { + case SyntaxKind.SelectKeyword: + selectOrGroupBy = this.ParseSelectClause(); + break; + case SyntaxKind.GroupKeyword: + selectOrGroupBy = this.ParseGroupClause(); + break; + default: + selectOrGroupBy = _syntaxFactory.SelectClause( + this.EatToken(SyntaxKind.SelectKeyword, ErrorCode.ERR_ExpectedSelectOrGroup), + this.CreateMissingIdentifierName()); + break; } - finally + + // optional query continuation clause + if (this.CurrentToken.ContextualKind == SyntaxKind.IntoKeyword) { - _pool.Free(clauses); + continuation = this.ParseQueryContinuation(); } + + return _syntaxFactory.QueryBody( + _pool.ToListAndFree(clauses), + selectOrGroupBy, + continuation); } private FromClauseSyntax ParseFromClause() @@ -13795,9 +13621,13 @@ private FromClauseSyntax ParseFromClause() { name = this.ParseIdentifierToken(); } - var @in = this.EatToken(SyntaxKind.InKeyword); - var expression = this.ParseExpressionCore(); - return _syntaxFactory.FromClause(@from, type, name, @in, expression); + + return _syntaxFactory.FromClause( + @from, + type, + name, + this.EatToken(SyntaxKind.InKeyword), + this.ParseExpressionCore()); } private JoinClauseSyntax ParseJoinClause() @@ -13831,19 +13661,19 @@ private JoinClauseSyntax ParseJoinClause() private LetClauseSyntax ParseLetClause() { Debug.Assert(this.CurrentToken.ContextualKind == SyntaxKind.LetKeyword); - var @let = this.EatContextualToken(SyntaxKind.LetKeyword); - var name = this.ParseIdentifierToken(); - var equal = this.EatToken(SyntaxKind.EqualsToken); - var expression = this.ParseExpressionCore(); - return _syntaxFactory.LetClause(@let, name, equal, expression); + return _syntaxFactory.LetClause( + this.EatContextualToken(SyntaxKind.LetKeyword), + this.ParseIdentifierToken(), + this.EatToken(SyntaxKind.EqualsToken), + this.ParseExpressionCore()); } private WhereClauseSyntax ParseWhereClause() { Debug.Assert(this.CurrentToken.ContextualKind == SyntaxKind.WhereKeyword); - var @where = this.EatContextualToken(SyntaxKind.WhereKeyword); - var condition = this.ParseExpressionCore(); - return _syntaxFactory.WhereClause(@where, condition); + return _syntaxFactory.WhereClause( + this.EatContextualToken(SyntaxKind.WhereKeyword), + this.ParseExpressionCore()); } private OrderByClauseSyntax ParseOrderByClause() @@ -13852,36 +13682,31 @@ private OrderByClauseSyntax ParseOrderByClause() var @orderby = this.EatContextualToken(SyntaxKind.OrderByKeyword); var list = _pool.AllocateSeparated(); - try - { - // first argument - list.Add(this.ParseOrdering()); + // first argument + list.Add(this.ParseOrdering()); - // additional arguments - while (this.CurrentToken.Kind == SyntaxKind.CommaToken) + // additional arguments + while (this.CurrentToken.Kind == SyntaxKind.CommaToken) + { + if (this.CurrentToken.Kind == SyntaxKind.CloseParenToken || this.CurrentToken.Kind == SyntaxKind.SemicolonToken) { - if (this.CurrentToken.Kind == SyntaxKind.CloseParenToken || this.CurrentToken.Kind == SyntaxKind.SemicolonToken) - { - break; - } - else if (this.CurrentToken.Kind == SyntaxKind.CommaToken) - { - list.AddSeparator(this.EatToken(SyntaxKind.CommaToken)); - list.Add(this.ParseOrdering()); - continue; - } - else if (this.SkipBadOrderingListTokens(list, SyntaxKind.CommaToken) == PostSkipAction.Abort) - { - break; - } + break; + } + else if (this.CurrentToken.Kind == SyntaxKind.CommaToken) + { + list.AddSeparator(this.EatToken(SyntaxKind.CommaToken)); + list.Add(this.ParseOrdering()); + continue; + } + else if (this.SkipBadOrderingListTokens(list, SyntaxKind.CommaToken) == PostSkipAction.Abort) + { + break; } - - return _syntaxFactory.OrderByClause(@orderby, list); - } - finally - { - _pool.Free(list); } + + return _syntaxFactory.OrderByClause( + @orderby, + _pool.ToListAndFree(list)); } private PostSkipAction SkipBadOrderingListTokens(SeparatedSyntaxListBuilder list, SyntaxKind expected) @@ -13919,28 +13744,28 @@ private OrderingSyntax ParseOrdering() private SelectClauseSyntax ParseSelectClause() { Debug.Assert(this.CurrentToken.ContextualKind == SyntaxKind.SelectKeyword); - var @select = this.EatContextualToken(SyntaxKind.SelectKeyword); - var expression = this.ParseExpressionCore(); - return _syntaxFactory.SelectClause(@select, expression); + return _syntaxFactory.SelectClause( + this.EatContextualToken(SyntaxKind.SelectKeyword), + this.ParseExpressionCore()); } private GroupClauseSyntax ParseGroupClause() { Debug.Assert(this.CurrentToken.ContextualKind == SyntaxKind.GroupKeyword); - var @group = this.EatContextualToken(SyntaxKind.GroupKeyword); - var groupExpression = this.ParseExpressionCore(); - var @by = this.EatContextualToken(SyntaxKind.ByKeyword, ErrorCode.ERR_ExpectedContextualKeywordBy); - var byExpression = this.ParseExpressionCore(); - return _syntaxFactory.GroupClause(@group, groupExpression, @by, byExpression); + return _syntaxFactory.GroupClause( + this.EatContextualToken(SyntaxKind.GroupKeyword), + this.ParseExpressionCore(), + this.EatContextualToken(SyntaxKind.ByKeyword, ErrorCode.ERR_ExpectedContextualKeywordBy), + this.ParseExpressionCore()); } private QueryContinuationSyntax ParseQueryContinuation() { Debug.Assert(this.CurrentToken.ContextualKind == SyntaxKind.IntoKeyword); - var @into = this.EatContextualToken(SyntaxKind.IntoKeyword); - var name = this.ParseIdentifierToken(); - var body = this.ParseQueryBody(); - return _syntaxFactory.QueryContinuation(@into, name, body); + return _syntaxFactory.QueryContinuation( + this.EatContextualToken(SyntaxKind.IntoKeyword), + this.ParseIdentifierToken(), + this.ParseQueryBody()); } [Obsolete("Use IsIncrementalAndFactoryContextMatches")] diff --git a/src/Compilers/CSharp/Portable/Parser/SyntaxParser.cs b/src/Compilers/CSharp/Portable/Parser/SyntaxParser.cs index aebaae650b02d..baa4e8f84410f 100644 --- a/src/Compilers/CSharp/Portable/Parser/SyntaxParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/SyntaxParser.cs @@ -1085,17 +1085,6 @@ internal DirectiveStack Directives protected TNode CheckFeatureAvailability(TNode node, MessageID feature, bool forceWarning = false) where TNode : GreenNode { - LanguageVersion availableVersion = this.Options.LanguageVersion; - - // There are special error codes for some features, so handle those separately. - switch (feature) - { - case MessageID.IDS_FeatureModuleAttrLoc: - return availableVersion >= LanguageVersion.CSharp2 - ? node - : this.AddError(node, ErrorCode.WRN_NonECMAFeature, feature.Localize()); - } - var info = feature.GetFeatureAvailabilityDiagnosticInfo(this.Options); if (info != null) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs index e0db1c8edda72..fd6c937a84fe9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs @@ -45,6 +45,7 @@ public static SourcePropertyAccessorSymbol CreateAccessorSymbol( bool hasExpressionBody = syntax.ExpressionBody is object; bool isNullableAnalysisEnabled = containingType.DeclaringCompilation.IsNullableAnalysisEnabledIn(syntax); CheckForBlockAndExpressionBody(syntax.Body, syntax.ExpressionBody, syntax, diagnostics); + return new SourcePropertyAccessorSymbol( containingType, property, @@ -101,7 +102,7 @@ public static SourcePropertyAccessorSymbol CreateAccessorSymbol( hasBody: false, hasExpressionBody: false, isIterator: false, - modifiers: new SyntaxTokenList(), + modifiers: default, methodKind, usesInit, isAutoPropertyAccessor: true, @@ -169,12 +170,6 @@ private SourcePropertyAccessorSymbol( ModifierUtils.CheckAccessibility(this.DeclarationModifiers, this, isExplicitInterfaceImplementation, diagnostics, location); this.CheckModifiers(location, hasBody: true, isAutoPropertyOrExpressionBodied: true, diagnostics: diagnostics); - - if (syntax != null) - { - var messageId = property.IsIndexer ? MessageID.IDS_FeatureExpressionBodiedIndexer : MessageID.IDS_FeatureExpressionBodiedProperty; - messageId.CheckFeatureAvailability(diagnostics, syntax, syntax.ArrowToken.GetLocation()); - } } #nullable enable @@ -238,6 +233,9 @@ protected SourcePropertyAccessorSymbol( { this.CheckModifiers(location, hasBody || hasExpressionBody, isAutoPropertyAccessor, diagnostics); } + + if (modifiers.Count > 0) + MessageID.IDS_FeaturePropertyAccessorMods.CheckFeatureAvailability(diagnostics, syntax, modifiers[0].GetLocation()); } #nullable disable diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs index 180e96b65f6f4..31b1b2c0f5463 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs @@ -85,6 +85,9 @@ private SourceUserDefinedConversionSymbol( { CheckFeatureAvailabilityAndRuntimeSupport(syntax, location, hasBody: syntax.Body != null || syntax.ExpressionBody != null, diagnostics: diagnostics); } + + if (syntax.ExplicitInterfaceSpecifier != null) + MessageID.IDS_FeatureStaticAbstractMembersInInterfaces.CheckFeatureAvailability(diagnostics, syntax.ExplicitInterfaceSpecifier); } internal ConversionOperatorDeclarationSyntax GetSyntax() diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs index 7d6ca1a5f1e99..67eac60862530 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs @@ -85,6 +85,9 @@ private SourceUserDefinedOperatorSymbol( { CheckFeatureAvailabilityAndRuntimeSupport(syntax, location, hasBody: syntax.Body != null || syntax.ExpressionBody != null, diagnostics: diagnostics); } + + if (syntax.ExplicitInterfaceSpecifier != null) + MessageID.IDS_FeatureStaticAbstractMembersInInterfaces.CheckFeatureAvailability(diagnostics, syntax.ExplicitInterfaceSpecifier); } internal OperatorDeclarationSyntax GetSyntax() diff --git a/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs b/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs index 3b0d70a8769df..2175a905208f1 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs @@ -667,6 +667,15 @@ private static bool MatchAttributeTarget(IAttributeTargetSymbol attributeTarget, return isOwner; } + // Special error code for this case. + if (isOwner && + targetOpt.Identifier.ToAttributeLocation() == AttributeLocation.Module) + { + var parseOptions = (CSharpParseOptions)targetOpt.SyntaxTree.Options; + if (parseOptions.LanguageVersion == LanguageVersion.CSharp1) + diagnostics.Add(ErrorCode.WRN_NonECMAFeature, targetOpt.GetLocation(), MessageID.IDS_FeatureModuleAttrLoc); + } + AttributeLocation allowedTargets = attributesOwner.AllowedAttributeLocations; AttributeLocation explicitTarget = targetOpt.GetAttributeLocation(); diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs index 92bebf9104c48..78eafcb6a973e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs @@ -142,7 +142,7 @@ internal GetAccessorSymbol( hasBody: true, hasExpressionBody: false, isIterator: false, - modifiers: new SyntaxTokenList(), + modifiers: default, MethodKind.PropertyGet, usesInit: false, isAutoPropertyAccessor: false, diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs index 7ff342e974a3d..3a7bd01ef9f5c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs @@ -178,6 +178,11 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r { AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeIsReadOnlyAttribute(this)); } + + if (this.IsParams && this.ContainingSymbol is SynthesizedDelegateInvokeMethod) + { + AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(WellKnownMember.System_ParamArrayAttribute__ctor)); + } } internal override ImmutableArray InterpolatedStringHandlerArgumentIndexes => ImmutableArray.Empty; diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index b7eeb99dc0942..fbcecfc50e902 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -11769,7 +11769,7 @@ public void LoadinganalyzerNetStandard13() $@"warning AD0001: Analyzer 'TestAnalyzer' threw an exception of type 'System.NotImplementedException' with message '28'. System.NotImplementedException: 28 at TestAnalyzer.get_SupportedDiagnostics() - at Microsoft.CodeAnalysis.Diagnostics.AnalyzerManager.AnalyzerExecutionContext.<>c__DisplayClass20_0.b__0(Object _) + at Microsoft.CodeAnalysis.Diagnostics.AnalyzerManager.AnalyzerExecutionContext.<>c__DisplayClass20_0.b__0(Object _) at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteAndCatchIfThrows_NoLock[TArg](DiagnosticAnalyzer analyzer, Action`1 analyze, TArg argument, Nullable`1 info) ----- Analyzer 'TestAnalyzer' threw the following exception: diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs index 9f15179a953bc..187bbae531f77 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs @@ -13180,7 +13180,7 @@ public static void Main() } [Fact] - public void LambdaDefaultDisardParameter_DelegateConversion_DefaultValueMismatch() + public void LambdaDefaultDiscardParameter_DelegateConversion_DefaultValueMismatch() { var source = """ using System; @@ -13229,6 +13229,18 @@ public static void Main() Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "d").WithArguments("y", "Program.D").WithLocation(12, 27)); } + [Fact] + public void Lambda_DiscardParameters() + { + var source = """ + var lam1 = (string _, int _ = 1) => { }; + lam1("s", 2); + var lam2 = (string _, params int[] _) => { }; + lam2("s", 3, 4, 5); + """; + CreateCompilation(source).VerifyDiagnostics(); + } + [Fact] public void MethodGroup_LambdaAssignment_DefaultParameterMismatch_01() { @@ -13337,6 +13349,11 @@ public static void Main() [InlineData("char", "'a'", "a")] [InlineData("string", @"""a string""", "a string")] [InlineData("C", "null", "")] + [InlineData("C", "default(C)", "")] + [InlineData("C", "default", "")] + [InlineData("S", "new S()", "Program+S")] + [InlineData("S", "default(S)", "Program+S")] + [InlineData("S", "default", "Program+S")] public void LambdaDefaultParameter_AllConstantValueTypes(string parameterType, string defaultValue = "0", string expectedOutput = "0") { var source = $$""" @@ -13350,6 +13367,8 @@ public enum E class C {} + struct S {} + public static void Main() { var lam = ({{parameterType}} p = {{defaultValue}}) => p; @@ -13381,7 +13400,7 @@ public void LambdaDefaultParameter_TargetTypeInvalidLiteralConversion() Diagnostic(ErrorCode.ERR_NoConversionForDefaultParam, "s").WithArguments("int", "short").WithLocation(1, 18)); } - [ConditionalFact(typeof(NoIOperationValidation))] + [Fact] public void LambdaDefaultParameter_TargetTypedValidNonLiteralConversion() { var source = """ @@ -13391,7 +13410,7 @@ public void LambdaDefaultParameter_TargetTypedValidNonLiteralConversion() CreateCompilation(source).VerifyDiagnostics(); } - [ConditionalFact(typeof(NoIOperationValidation))] + [Fact] public void LambdaDefaultParameter_InterpolatedStringHandler() { var source = """ @@ -13451,7 +13470,7 @@ public static void Main() Diagnostic(ErrorCode.ERR_DefaultValueMustBeConstant, "f(1000)").WithArguments("x").WithLocation(8, 28)); } - [ConditionalFact(typeof(NoIOperationValidation))] + [Fact] public void LambdaWithDefault_NonConstantLiteral_InterpolatedString() { var source = """ @@ -13483,6 +13502,52 @@ public void LambdaWithDefault_NonConstantLiteral_U8String() Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "ReadOnlySpan").WithArguments("ReadOnlySpan<>").WithLocation(1, 12)); } + [Fact] + public void LambdaWithDefault_EmbeddedType_Propagated() + { + var source1 = """ + using System.Runtime.InteropServices; + [assembly: PrimaryInteropAssembly(0, 0)] + [assembly: Guid("863D5BC0-46A1-49AC-97AA-A5F0D441A9DA")] + [ComImport, Guid("863D5BC0-46A1-49AD-97AA-A5F0D441A9DA")] + public interface MyEmbeddedType { } + """; + var comp1 = CreateCompilation(source1); + var ref1 = comp1.EmitToImageReference(embedInteropTypes: true); + + var source2 = """ + var l = (MyEmbeddedType t = null) => {}; + """; + var comp2 = CreateCompilation(source2, new[] { ref1 }); + CompileAndVerify(comp2, symbolValidator: static module => + { + Assert.Contains("MyEmbeddedType", module.TypeNames); + }); + } + + [Fact] + public void LambdaWithDefault_EmbeddedType_NotPropagated_Default() + { + var source1 = """ + using System.Runtime.InteropServices; + [assembly: PrimaryInteropAssembly(0, 0)] + [assembly: Guid("863D5BC0-46A1-49AC-97AA-A5F0D441A9DA")] + [ComImport, Guid("863D5BC0-46A1-49AD-97AA-A5F0D441A9DA")] + public interface MyEmbeddedType { } + """; + var comp1 = CreateCompilation(source1); + var ref1 = comp1.EmitToImageReference(embedInteropTypes: true); + + var source2 = """ + var l = (object o = default(MyEmbeddedType)) => {}; + """; + var comp2 = CreateCompilation(source2, new[] { ref1 }); + CompileAndVerify(comp2, symbolValidator: static module => + { + Assert.DoesNotContain("MyEmbeddedType", module.TypeNames); + }); + } + [Fact] public void LambdaWithParameterDefaultValueAttribute() { @@ -13927,6 +13992,108 @@ public abstract class MulticastDelegate : Delegate { } Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "2m").WithArguments("System.Decimal").WithLocation(2, 24)); } + [Fact] + public void ParamsArray_MissingParamArrayAttribute_Lambda() + { + var source = """ + var lam = (params int[] xs) => xs.Length; + """; + var comp = CreateCompilation(source); + comp.MakeTypeMissing(WellKnownType.System_ParamArrayAttribute); + comp.VerifyDiagnostics( + // (1,12): error CS0656: Missing compiler required member 'System.ParamArrayAttribute..ctor' + // var lam = (params int[] xs) => xs.Length; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "params").WithArguments("System.ParamArrayAttribute", ".ctor").WithLocation(1, 12)); + } + + [Fact] + public void ParamsArray_MissingParamArrayAttribute_Lambda_ExplicitDelegateType() + { + var source = """ + System.Func lam = (params int[] xs) => xs.Length; + """; + var comp = CreateCompilation(source); + comp.MakeTypeMissing(WellKnownType.System_ParamArrayAttribute); + comp.VerifyDiagnostics( + // (1,32): error CS0656: Missing compiler required member 'System.ParamArrayAttribute..ctor' + // System.Func lam = (params int[] xs) => xs.Length; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "params").WithArguments("System.ParamArrayAttribute", ".ctor").WithLocation(1, 32), + // (1,45): warning CS9100: Parameter 1 has params modifier in lambda but not in target delegate type. + // System.Func lam = (params int[] xs) => xs.Length; + Diagnostic(ErrorCode.WRN_ParamsArrayInLambdaOnly, "xs").WithArguments("1").WithLocation(1, 45)); + } + + [Fact] + public void ParamsArray_MissingParamArrayAttribute_LocalFunction() + { + var source = """ + int local(params int[] xs) => xs.Length; + local(); + """; + var comp = CreateCompilation(source); + comp.MakeTypeMissing(WellKnownType.System_ParamArrayAttribute); + comp.VerifyDiagnostics( + // (1,11): error CS0656: Missing compiler required member 'System.ParamArrayAttribute..ctor' + // int local(params int[] xs) => xs.Length; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "params int[] xs").WithArguments("System.ParamArrayAttribute", ".ctor").WithLocation(1, 11)); + } + + [Fact] + public void ParamsArray_MissingParamArrayAttribute_ExternalMethodGroup() + { + var source1 = """ + public class C + { + public static void M(params int[] xs) { } + } + """; + var comp1 = CreateCompilation(source1).VerifyDiagnostics(); + + var source2 = """ + var m = C.M; + """; + var comp2 = CreateCompilation(source2, new[] { comp1.ToMetadataReference() }); + comp2.MakeTypeMissing(WellKnownType.System_ParamArrayAttribute); + comp2.VerifyDiagnostics( + // (1,9): error CS0656: Missing compiler required member 'System.ParamArrayAttribute..ctor' + // var m = C.M; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "C.M").WithArguments("System.ParamArrayAttribute", ".ctor").WithLocation(1, 9)); + } + + [Fact] + public void ParamsArray_MissingParamArrayAttribute_Method() + { + var source = """ + class C + { + static void M(params int[] xs) { } + } + """; + var comp = CreateCompilation(source); + comp.MakeTypeMissing(WellKnownType.System_ParamArrayAttribute); + comp.VerifyDiagnostics( + // (3,19): error CS0656: Missing compiler required member 'System.ParamArrayAttribute..ctor' + // static void M(params int[] xs) { } + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "params int[] xs").WithArguments("System.ParamArrayAttribute", ".ctor").WithLocation(3, 19)); + } + + [Fact] + public void ParamsArray_MissingParamArrayAttribute_Property() + { + var source = """ + class C + { + int this[params int[] xs] { get => throw null; set => throw null; } + } + """; + var comp = CreateCompilation(source); + comp.MakeTypeMissing(WellKnownType.System_ParamArrayAttribute); + comp.VerifyDiagnostics( + // (3,14): error CS0656: Missing compiler required member 'System.ParamArrayAttribute..ctor' + // int this[params int[] xs] { get => throw null; set => throw null; } + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "params int[] xs").WithArguments("System.ParamArrayAttribute", ".ctor").WithLocation(3, 14)); + } + [Fact] public void ParamsArray_SynthesizedTypesMatch() { @@ -13981,13 +14148,22 @@ void Method7(int x, System.TypedReference y, params int[] ys) { } """).VerifyDiagnostics(); } - [Fact] - public void ParamsArray_SynthesizedDelegateIL() + [Theory] + [InlineData("(int x, params int[] ys) => { }", "")] + [InlineData("M", "static void M(int x, params int[] ys) { }")] + public void ParamsArray_SynthesizedDelegateIL(string variable, string method) { - var source = """ - static void Report(object obj) => System.Console.WriteLine(obj.GetType()); - var lam = (int x, params int[] ys) => { }; - Report(lam); + var source = $$""" + class C + { + static void Report(object obj) => System.Console.WriteLine(obj.GetType()); + static void Main() + { + var m = {{variable}}; + Report(m); + } + {{method}} + } """; var verifier = CompileAndVerify(source, expectedOutput: "<>f__AnonymousDelegate0").VerifyDiagnostics(); verifier.VerifyTypeIL("<>f__AnonymousDelegate0", $$""" @@ -14011,11 +14187,126 @@ instance void Invoke ( int32[] arg2 ) runtime managed { + .param [2] + .custom instance void [{{s_libPrefix}}]System.ParamArrayAttribute::.ctor() = ( + 01 00 00 00 + ) } // end of method '<>f__AnonymousDelegate0'::Invoke } // end of class <>f__AnonymousDelegate0 """); } + [Fact] + public void ParamsArray_SynthesizedMethodIL_Lambda() + { + var source = """ + var lam = (int x, params int[] ys) => { }; + System.Console.WriteLine(lam.Method.DeclaringType!.Name); + """; + var verifier = CompileAndVerify(source, expectedOutput: "<>c").VerifyDiagnostics(); + verifier.VerifyTypeIL("<>c", $$""" + .class nested private auto ansi sealed serializable beforefieldinit '<>c' + extends [{{s_libPrefix}}]System.Object + { + .custom instance void [{{s_libPrefix}}]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Fields + .field public static initonly class Program/'<>c' '<>9' + .field public static class '<>f__AnonymousDelegate0' '<>9__0_0' + // Methods + .method private hidebysig specialname rtspecialname static + void .cctor () cil managed + { + // Method begins at RVA 0x20a4 + // Code size 11 (0xb) + .maxstack 8 + IL_0000: newobj instance void Program/'<>c'::.ctor() + IL_0005: stsfld class Program/'<>c' Program/'<>c'::'<>9' + IL_000a: ret + } // end of method '<>c'::.cctor + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x209c + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [{{s_libPrefix}}]System.Object::.ctor() + IL_0006: ret + } // end of method '<>c'::.ctor + .method assembly hidebysig + instance void '<
$>b__0_0' ( + int32 x, + int32[] ys + ) cil managed + { + // Method begins at RVA 0x20b0 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>c'::'<
$>b__0_0' + } // end of class <>c + """); + } + + [Fact] + public void ParamsArray_SynthesizedMethodIL_LocalFunction() + { + var source = """ + class C + { + public static void M() + { + void local(int x, params int[] ys) { } + } + } + """; + var verifier = CompileAndVerify(source).VerifyDiagnostics( + // (5,14): warning CS8321: The local function 'local' is declared but never used + // void local(int x, params int[] ys) { } + Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "local").WithArguments("local").WithLocation(5, 14)); + verifier.VerifyTypeIL("C", $$""" + .class private auto ansi beforefieldinit C + extends [{{s_libPrefix}}]System.Object + { + // Methods + .method public hidebysig static + void M () cil managed + { + // Method begins at RVA 0x2067 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method C::M + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2069 + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [{{s_libPrefix}}]System.Object::.ctor() + IL_0006: ret + } // end of method C::.ctor + .method assembly hidebysig static + void 'g__local|0_0' ( + int32 x, + int32[] ys + ) cil managed + { + .custom instance void [{{s_libPrefix}}]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method C::'g__local|0_0' + } // end of class C + """); + } + [Fact] public void ParamsArray_DelegateConversions_Lambdas() { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/ExpressionBodiedMemberTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/ExpressionBodiedMemberTests.cs index 9c6a695db5e08..771fcb51af1bb 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/ExpressionBodiedMemberTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/ExpressionBodiedMemberTests.cs @@ -1044,14 +1044,13 @@ public class C CreateCompilation(source, parseOptions: TestOptions.Regular6).VerifyDiagnostics( // (5,9): error CS8059: Feature 'expression body constructor and destructor' is not available in C# 6. Please use language version 7.0 or greater. // C() => Console.WriteLine(1); - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "=> Console.WriteLine(1)").WithArguments("expression body constructor and destructor", "7.0").WithLocation(5, 9), + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "=>").WithArguments("expression body constructor and destructor", "7.0").WithLocation(5, 9), // (6,10): error CS8059: Feature 'expression body constructor and destructor' is not available in C# 6. Please use language version 7.0 or greater. // ~C() => Console.WriteLine(2); - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "=> Console.WriteLine(2)").WithArguments("expression body constructor and destructor", "7.0").WithLocation(6, 10), + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "=>").WithArguments("expression body constructor and destructor", "7.0").WithLocation(6, 10), // (7,17): error CS8059: Feature 'expression body property accessor' is not available in C# 6. Please use language version 7.0 or greater. // int P { set => Console.WriteLine(value); } - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "=> Console.WriteLine(value)").WithArguments("expression body property accessor", "7.0").WithLocation(7, 17) - ); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "=>").WithArguments("expression body property accessor", "7.0").WithLocation(7, 17)); } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index 25ecd6653a3e5..91c977a1eabf8 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -16,11 +16,12 @@ using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; +using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests { - public class LambdaTests : CSharpTestBase + public class LambdaTests : SemanticModelTestBase { [Fact, WorkItem(37456, "https://github.com/dotnet/roslyn/issues/37456")] public void Verify37456() @@ -6666,11 +6667,6 @@ void M() void M2(string parameter = nameof(parameter)) => throw null; } - -public class MyAttribute : System.Attribute -{ - public MyAttribute(string name1) { } -} "); comp.VerifyDiagnostics( // (6,49): error CS0103: The name 'parameter' does not exist in the current context @@ -7725,7 +7721,7 @@ void M() Diagnostic(ErrorCode.ERR_NameNotInContext, "s").WithArguments("s").WithLocation(8, 31)); } - [ConditionalFact(typeof(NoIOperationValidation))] + [Fact] public void LambdaDefaultLocalConstantSameScope_PreDefinition() { var source = """ @@ -7742,7 +7738,7 @@ void M() comp.VerifyDiagnostics(); } - [ConditionalFact(typeof(NoIOperationValidation))] + [Fact] public void LambdaDefaultLocalConstantSameScope_PostDefinition() { var source = """ @@ -7765,7 +7761,7 @@ void M() Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "s").WithArguments("s").WithLocation(5, 33)); } - [ConditionalFact(typeof(NoIOperationValidation))] + [Fact] public void LambdaDefaultSelfReference() { var source = """ @@ -7789,7 +7785,7 @@ public static void Main(string[] args) Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "lam").WithArguments("lam").WithLocation(7, 33)); } - [ConditionalFact(typeof(NoIOperationValidation))] + [Fact] public void LambdaDefaultSelfReference_ParameterBefore() { var source = """ @@ -7813,7 +7809,7 @@ public static void Main(string[] args) Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "lam").WithArguments("lam").WithLocation(7, 40)); } - [ConditionalFact(typeof(NoIOperationValidation))] + [Fact] public void LambdaDefaultSelfReference_ParameterAfter() { var source = """ @@ -7903,6 +7899,32 @@ public static void Main() Diagnostic(ErrorCode.ERR_NoConversionForDefaultParam, "s").WithArguments("int", "string").WithLocation(6, 27)); } + [Theory] + [InlineData("ref")] + [InlineData("out")] + public void LambdaDefault_RefOut(string modifier) + { + var source = $$""" + var lam = void ({{modifier}} int x = 1) => throw null; + """; + CreateCompilation(source).VerifyDiagnostics( + // (1,17): error CS1741: A ref or out parameter cannot have a default value + // var lam = void (ref int x = 1) => throw null; + Diagnostic(ErrorCode.ERR_RefOutDefaultValue, modifier).WithLocation(1, 17)); + } + + [Fact] + public void LambdaDefault_ThisModifier() + { + var source = """ + var lam = void (this int x = 1) => throw null; + """; + CreateCompilation(source).VerifyDiagnostics( + // (1,17): error CS1041: Identifier expected; 'this' is a keyword + // var lam = void (this int x = 1) => throw null; + Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "this").WithArguments("", "this").WithLocation(1, 17)); + } + [Fact] public void LambdaWithDefaultParameterAndParams() { @@ -8107,11 +8129,11 @@ public static void M() var methodSyntax = (MethodDeclarationSyntax)comp.GetMember("C.M").GetNonNullSyntaxNode(); var methodModel = model.GetMemberModel(methodSyntax.Body); Assert.NotNull(methodModel); - var methodBinder = GetBinder(methodModel.GetEnclosingBinder(methodSyntax.Body.SpanStart)); - var defaultValueBinder = GetBinder(defaultValueModel.GetEnclosingBinder(defaultValue.SpanStart)); + var methodBinder = getBinder(methodModel.GetEnclosingBinder(methodSyntax.Body.SpanStart)); + var defaultValueBinder = getBinder(defaultValueModel.GetEnclosingBinder(defaultValue.SpanStart)); Assert.Same(methodBinder, defaultValueBinder); - static T GetBinder(Binder binder) where T : Binder + static T getBinder(Binder binder) where T : Binder { while (true) { @@ -8124,6 +8146,69 @@ static T GetBinder(Binder binder) where T : Binder } } + [Fact] + public void LambdaWithDefaultParameter_BindingScope() + { + var source = """ + #nullable enable + class C { + public static void M() { + var lam = (int a, + string b = nameof(a), // 1 + string c = nameof(lam), // 2 + string d = nameof(M), + string e = nameof(C), + T? f = default(T), + C? g = default(C) + ) => { }; + lam(1); + } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (5,31): error CS0103: The name 'a' does not exist in the current context + // string b = nameof(a), // 1 + Diagnostic(ErrorCode.ERR_NameNotInContext, "a").WithArguments("a").WithLocation(5, 31), + // (6,31): error CS0841: Cannot use local variable 'lam' before it is declared + // string c = nameof(lam), // 2 + Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "lam").WithArguments("lam").WithLocation(6, 31)); + } + + [Fact] + public void LambdaWithDefaultParameter_LookupNames() + { + var source = """ + class C { + private const int N1 = 10; + private int x; + private void M1() { } + public static void M2() { + const int N2 = 20; + int y = 2; + var lam = (int a, int b = /*pos*/5, int c = 6) => { }; + lam(1); + } + } + """; + var tree = Parse(source); + var comp = CreateCompilation(tree); + var model = comp.GetSemanticModel(tree); + var names = model.LookupNames(GetPositionForBinding(source)); + Assert.Contains("C", names); + Assert.Contains("x", names); + Assert.Contains("y", names); + Assert.Contains("N1", names); + Assert.Contains("N2", names); + Assert.Contains("T1", names); + Assert.Contains("T2", names); + Assert.Contains("M1", names); + Assert.Contains("M2", names); + Assert.Contains("lam", names); + Assert.DoesNotContain("a", names); + Assert.DoesNotContain("b", names); + Assert.DoesNotContain("c", names); + } + [Fact] public void ParamsArray_Langversion() { @@ -8158,12 +8243,13 @@ public void ParamsArray_Langversion_DelegateSyntax(LanguageVersion languageVersi public void ParamsArray_Call() { var source = """ - var lam = (params int[] xs) => System.Console.WriteLine(xs?.Length.ToString() ?? "null"); + var lam = (params int?[] xs) => System.Console.WriteLine(xs?.Length.ToString() ?? "null"); lam(); lam(1); lam(1, 2, 3); - lam(new[] { 1, 2, 3 }); + lam(new int?[] { 1, 2, 3 }); lam(null); + lam((int?)null); """; CompileAndVerify(source, expectedOutput: """ 0 @@ -8171,6 +8257,7 @@ public void ParamsArray_Call() 3 3 null + 1 """).VerifyDiagnostics(); } @@ -8187,7 +8274,7 @@ public void ParamsArray_WithDefaultValue() } [Fact] - public void ParamsArray_CompilerAttribute() + public void ParamsArray_ParamArrayAttribute() { var source = """ var lam = ([System.ParamArray] int[] xs) => xs.Length; @@ -8252,6 +8339,35 @@ public void ParamsArray_Symbol_MultipleParamsArrays() Assert.True(((SourceParameterSymbol)lambdas[1].Parameters[2]).IsParams); } + [Fact] + public void ParamsArray_Symbol_ExternalReference() + { + var source = """ + static void Report(object obj) => System.Console.WriteLine(obj.GetType()); + var lam1 = (params int[] xs) => xs.Length; + Report(lam1); + var lam2 = (int[] xs) => xs.Length; + Report(lam2); + var lam3 = (int[] xs, params int[] ys) => xs.Length + ys.Length; + Report(lam3); + """; + CompileAndVerify(source, expectedOutput: """ + <>f__AnonymousDelegate0 + System.Func`2[System.Int32[],System.Int32] + <>f__AnonymousDelegate1 + """, symbolValidator: static module => + { + var lam1 = (NamedTypeSymbol)module.GlobalNamespace.GetMember("<>f__AnonymousDelegate0"); + Assert.True(lam1.DelegateParameters().Single().IsParams); + + var lam3 = (NamedTypeSymbol)module.GlobalNamespace.GetMember("<>f__AnonymousDelegate1"); + var lam3Parameters = lam3.DelegateParameters(); + Assert.Equal(2, lam3Parameters.Length); + Assert.False(lam3Parameters[0].IsParams); + Assert.True(lam3Parameters[1].IsParams); + }); + } + [Fact] public void ParamsArray_NotLast() { @@ -8295,15 +8411,63 @@ public void ParamsArray_NotArray() } [Fact] - public void ParamsArray_ParamArrayAttribute() + public void ParamsArray_Multidimensional() { var source = """ - var lam = ([System.ParamArrayAttribute] int[] xs) => xs; + var lam = (params int[,] xs) => xs.Length; """; CreateCompilation(source).VerifyDiagnostics( - // (1,13): error CS0674: Do not use 'System.ParamArrayAttribute'. Use the 'params' keyword instead. - // var lam = ([System.ParamArrayAttribute] int[] xs) => xs; - Diagnostic(ErrorCode.ERR_ExplicitParamArray, "System.ParamArrayAttribute").WithLocation(1, 13)); + // (1,12): error CS0225: The params parameter must be a single dimensional array + // var lam = (params int[,] xs) => xs.Length; + Diagnostic(ErrorCode.ERR_ParamsMustBeArray, "params").WithLocation(1, 12)); + } + + [Fact] + public void ParamsArray_Jagged() + { + var source = """ + var lam = (params int[][] xs) => xs.Length; + """; + CreateCompilation(source).VerifyDiagnostics(); + } + + [Theory] + [InlineData("ref")] + [InlineData("out")] + [InlineData("in")] + public void ParamsArray_OtherModifiers(string modifier) + { + var source = $$""" + var lam = void (params {{modifier}} int[] xs) => throw null; + """; + CreateCompilation(source).VerifyDiagnostics( + // (1,24): error CS1611: The params parameter cannot be declared as ref + // var lam = void (params ref int[] xs) => throw null; + Diagnostic(ErrorCode.ERR_ParamsCantBeWithModifier, modifier).WithArguments(modifier).WithLocation(1, 24)); + } + + [Fact] + public void ParamsArray_ThisModifier_01() + { + var source = """ + var lam = (this params int[] xs) => xs.Length; + """; + CreateCompilation(source).VerifyDiagnostics( + // (1,12): error CS1041: Identifier expected; 'this' is a keyword + // var lam = (this params int[] xs) => xs.Length; + Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "this").WithArguments("", "this").WithLocation(1, 12)); + } + + [Fact] + public void ParamsArray_ThisModifier_02() + { + var source = """ + var lam = (params this int[] xs) => xs.Length; + """; + CreateCompilation(source).VerifyDiagnostics( + // (1,19): error CS0027: Keyword 'this' is not available in the current context + // var lam = (params this int[] xs) => xs.Length; + Diagnostic(ErrorCode.ERR_ThisInBadContext, "this").WithLocation(1, 19)); } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/OutVarTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/OutVarTests.cs index 999c936cd9232..8c5979345985e 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/OutVarTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/OutVarTests.cs @@ -36436,6 +36436,79 @@ void M() Assert.Equal("System.String M2(out System.Int32 x, [System.String y = null])", symbolInfo.Symbol.ToTestDisplayString()); Assert.Same(symbolInfo.Symbol, speculativeModel.GetDeclaredSymbol(tree2.GetRoot().DescendantNodes().OfType().Where(l => l.Identifier.ValueText == "M2").Single())); } + + [Fact] + public void GetSymbolInfoOnSpeculativeMethodBodySemanticModelInDefaultParameterValue_03() + { + var source = """ + class C + { + void M() + { + var lam = (string parameter = M2(out var x)) => { }; + _ = lam; + } + + static string M2(out int x) => throw null; + } + """; + + var comp = CreateCompilation(source).VerifyDiagnostics( + // (5,39): error CS1736: Default parameter value for 'parameter' must be a compile-time constant + // var lam = (string parameter = M2(out var x)) => { }; + Diagnostic(ErrorCode.ERR_DefaultValueMustBeConstant, "M2(out var x)").WithArguments("parameter").WithLocation(5, 39)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var tree2 = CSharpSyntaxTree.ParseText(source); + var method = tree2.GetRoot().DescendantNodes().OfType().First(); + Assert.True(model.TryGetSpeculativeSemanticModelForMethodBody(method.Body.SpanStart, method, out var speculativeModel)); + + var invocation = tree2.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("M2(out var x)", invocation.ToString()); + var symbolInfo = speculativeModel.GetSymbolInfo(invocation); + Assert.Equal("System.String C.M2(out System.Int32 x)", symbolInfo.Symbol.ToTestDisplayString()); + + var equalsValue = method.DescendantNodes().OfType().Single() + .DescendantNodes().OfType().Single(); + Assert.True(model.TryGetSpeculativeSemanticModel(equalsValue.Value.SpanStart, equalsValue, out speculativeModel)); + symbolInfo = speculativeModel.GetSymbolInfo(invocation); + Assert.Equal("System.String C.M2(out System.Int32 x)", symbolInfo.Symbol.ToTestDisplayString()); + } + + [Fact] + public void GetSymbolInfoOnSpeculativeMethodBodySemanticModelInDefaultParameterValue_04() + { + var source = """ + class C + { + void M() + { + var lam = (string parameter = () => { static string M2(out int x, string y = M2(out var a, "b")) => throw null; }) => { }; + _ = lam; + } + } + """; + + var comp = CreateCompilation(source).VerifyDiagnostics( + // (5,39): error CS1736: Default parameter value for 'parameter' must be a compile-time constant + // var lam = (string parameter = () => { static string M2(out int x, string y = M2(out var a, "b")) => throw null; }) => { }; + Diagnostic(ErrorCode.ERR_DefaultValueMustBeConstant, @"() => { static string M2(out int x, string y = M2(out var a, ""b"")) => throw null; }").WithArguments("parameter").WithLocation(5, 39)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var tree2 = CSharpSyntaxTree.ParseText(source); + var method = tree2.GetRoot().DescendantNodes().OfType().First(); + Assert.True(model.TryGetSpeculativeSemanticModelForMethodBody(method.Body.SpanStart, method, out var speculativeModel)); + + var invocation = tree2.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal(@"M2(out var a, ""b"")", invocation.ToString()); + var symbolInfo = speculativeModel.GetSymbolInfo(invocation); + Assert.Equal("System.String M2(out System.Int32 x, [System.String y = null])", symbolInfo.Symbol.ToTestDisplayString()); + Assert.Same(symbolInfo.Symbol, speculativeModel.GetDeclaredSymbol(tree2.GetRoot().DescendantNodes().OfType().Where(l => l.Identifier.ValueText == "M2").Single())); + } } internal static class OutVarTestsExtensions diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs index 61b1787d54b17..612de6a91af98 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs @@ -283,15 +283,13 @@ record struct Point(int x, int y); comp.VerifyDiagnostics( // (2,8): error CS8773: Feature 'record structs' is not available in C# 9.0. Please use language version 10.0 or greater. // record struct Point { } - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "struct").WithArguments("record structs", "10.0").WithLocation(2, 8) - ); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "struct").WithArguments("record structs", "10.0").WithLocation(2, 8)); comp = CreateCompilation(new[] { src3, IsExternalInitTypeDefinition }, parseOptions: TestOptions.Regular9, options: TestOptions.ReleaseDll); comp.VerifyDiagnostics( // (2,8): error CS8773: Feature 'record structs' is not available in C# 9.0. Please use language version 10.0 or greater. - // record struct Point { } - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "struct").WithArguments("record structs", "10.0").WithLocation(2, 8) - ); + // record struct Point(int x, int y); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "struct").WithArguments("record structs", "10.0").WithLocation(2, 8)); comp = CreateCompilation(new[] { src1, IsExternalInitTypeDefinition }, parseOptions: TestOptions.Regular10, options: TestOptions.ReleaseDll); comp.VerifyDiagnostics( @@ -378,22 +376,19 @@ record struct Point { } comp.VerifyDiagnostics( // (4,12): error CS8773: Feature 'record structs' is not available in C# 9.0. Please use language version 10.0 or greater. // record struct Point { } - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "struct").WithArguments("record structs", "10.0").WithLocation(4, 12) - ); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "struct").WithArguments("record structs", "10.0").WithLocation(4, 12)); comp = CreateCompilation(new[] { src3, IsExternalInitTypeDefinition }, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics( // (4,12): error CS8773: Feature 'record structs' is not available in C# 9.0. Please use language version 10.0 or greater. - // record struct Point(int x, int y); - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "struct").WithArguments("record structs", "10.0").WithLocation(4, 12) - ); + // record struct Point { } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "struct").WithArguments("record structs", "10.0").WithLocation(4, 12)); comp = CreateCompilation(src4, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics( // (4,12): error CS8773: Feature 'record structs' is not available in C# 9.0. Please use language version 10.0 or greater. // record struct Point { } - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "struct").WithArguments("record structs", "10.0").WithLocation(4, 12) - ); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "struct").WithArguments("record structs", "10.0").WithLocation(4, 12)); comp = CreateCompilation(src1); comp.VerifyDiagnostics( @@ -1025,7 +1020,7 @@ internal S2() { } // (3,5): error CS8773: Feature 'parameterless struct constructors' is not available in C# 9.0. Please use language version 10.0 or greater. // S1() { } Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "S1").WithArguments("parameterless struct constructors", "10.0").WithLocation(3, 5), - // (3,5): error CS8938: The parameterless struct constructor must be 'public'. + // (3,5): error CS8958: The parameterless struct constructor must be 'public'. // S1() { } Diagnostic(ErrorCode.ERR_NonPublicParameterlessStructConstructor, "S1").WithLocation(3, 5), // (5,8): error CS8773: Feature 'record structs' is not available in C# 9.0. Please use language version 10.0 or greater. @@ -1034,7 +1029,7 @@ internal S2() { } // (7,14): error CS8773: Feature 'parameterless struct constructors' is not available in C# 9.0. Please use language version 10.0 or greater. // internal S2() { } Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "S2").WithArguments("parameterless struct constructors", "10.0").WithLocation(7, 14), - // (7,14): error CS8938: The parameterless struct constructor must be 'public'. + // (7,14): error CS8958: The parameterless struct constructor must be 'public'. // internal S2() { } Diagnostic(ErrorCode.ERR_NonPublicParameterlessStructConstructor, "S2").WithLocation(7, 14)); @@ -10589,8 +10584,7 @@ record struct A(int X) Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "struct").WithArguments("record structs", "10.0").WithLocation(8, 8), // (8,17): error CS8773: Feature 'positional fields in records' is not available in C# 9.0. Please use language version 10.0 or greater. // record struct A(int X) - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "int X").WithArguments("positional fields in records", "10.0").WithLocation(8, 17) - ); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "int X").WithArguments("positional fields in records", "10.0").WithLocation(8, 17)); comp = CreateCompilation(source, parseOptions: TestOptions.Regular10); comp.VerifyDiagnostics(); diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests.cs index d9e7c02a24392..8d5f6c310ad41 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests.cs @@ -8616,11 +8616,15 @@ class B : A x + y; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 12); + var text = "public int N.I.operator +(int x, int y) => x + y;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,22): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { public int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 22), + // (1,22): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { public int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 22), + // (1,22): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { public int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 22), + // (1,35): error CS0106: The modifier 'public' is not valid for this item + // class C { public int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "+").WithArguments("public").WithLocation(1, 35), + // (1,35): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int, int)' must be declared static + // class C { public int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "+").WithArguments("C.operator +(int, int)").WithLocation(1, 35)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,22): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { public int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 22), + // (1,22): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { public int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 22), + // (1,35): error CS0106: The modifier 'public' is not valid for this item + // class C { public int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "+").WithArguments("public").WithLocation(1, 35), + // (1,35): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int, int)' must be declared static + // class C { public int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "+").WithArguments("C.operator +(int, int)").WithLocation(1, 35)); foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingDeclaration("public int N.I.operator +(int x, int y) => x + y;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? new[] { error } : new DiagnosticDescription[] { }); + UsingDeclaration(text, options: options.WithLanguageVersion(version)); N(SyntaxKind.OperatorDeclaration); { @@ -2000,7 +2044,58 @@ public void OperatorDeclaration_ExplicitImplementation_01() [Fact] public void OperatorDeclaration_ExplicitImplementation_02() { - var errors = new[] { + var text = "public int N.I.implicit (int x) => x;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,18): error CS1553: Declaration is not valid; use '+ operator (...' instead + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(1, 18), + // (1,22): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 22), + // (1,22): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 22), + // (1,22): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 22), + // (1,26): error CS1003: Syntax error, 'operator' expected + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "implicit").WithArguments("operator").WithLocation(1, 26), + // (1,26): error CS1019: Overloadable unary operator expected + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "implicit").WithLocation(1, 26), + // (1,26): error CS0106: The modifier 'public' is not valid for this item + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "").WithArguments("public").WithLocation(1, 26), + // (1,26): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "").WithArguments("C.operator +(int)").WithLocation(1, 26)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,18): error CS1553: Declaration is not valid; use '+ operator (...' instead + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(1, 18), + // (1,22): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 22), + // (1,22): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 22), + // (1,26): error CS1003: Syntax error, 'operator' expected + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "implicit").WithArguments("operator").WithLocation(1, 26), + // (1,26): error CS1019: Overloadable unary operator expected + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "implicit").WithLocation(1, 26), + // (1,26): error CS0106: The modifier 'public' is not valid for this item + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "").WithArguments("public").WithLocation(1, 26), + // (1,26): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "").WithArguments("C.operator +(int)").WithLocation(1, 26)); + + var errors = new[] + { // (1,8): error CS1553: Declaration is not valid; use '+ operator (...' instead // public int N.I.implicit (int x) => x; Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(1, 8), @@ -2010,20 +2105,13 @@ public void OperatorDeclaration_ExplicitImplementation_02() // (1,16): error CS1019: Overloadable unary operator expected // public int N.I.implicit (int x) => x; Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "implicit").WithLocation(1, 16) - }; + }; foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingDeclaration("public int N.I.implicit (int x) => x;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? - errors.Append( - // (1,12): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // public int N.I.implicit (int x) => x; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 12) - ).ToArray() : - errors); + UsingDeclaration(text, options: options.WithLanguageVersion(version), errors); N(SyntaxKind.OperatorDeclaration); { @@ -2081,6 +2169,56 @@ public void OperatorDeclaration_ExplicitImplementation_02() [Fact] public void OperatorDeclaration_ExplicitImplementation_03() { + var text = "public int N.I.explicit (int x) => x;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,18): error CS1553: Declaration is not valid; use '+ operator (...' instead + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(1, 18), + // (1,22): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 22), + // (1,22): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 22), + // (1,22): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 22), + // (1,26): error CS1003: Syntax error, 'operator' expected + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "explicit").WithArguments("operator").WithLocation(1, 26), + // (1,26): error CS1019: Overloadable unary operator expected + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "explicit").WithLocation(1, 26), + // (1,26): error CS0106: The modifier 'public' is not valid for this item + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "").WithArguments("public").WithLocation(1, 26), + // (1,26): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "").WithArguments("C.operator +(int)").WithLocation(1, 26)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,18): error CS1553: Declaration is not valid; use '+ operator (...' instead + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(1, 18), + // (1,22): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 22), + // (1,22): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 22), + // (1,26): error CS1003: Syntax error, 'operator' expected + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "explicit").WithArguments("operator").WithLocation(1, 26), + // (1,26): error CS1019: Overloadable unary operator expected + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "explicit").WithLocation(1, 26), + // (1,26): error CS0106: The modifier 'public' is not valid for this item + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "").WithArguments("public").WithLocation(1, 26), + // (1,26): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "").WithArguments("C.operator +(int)").WithLocation(1, 26)); + var errors = new[] { // (1,8): error CS1553: Declaration is not valid; use '+ operator (...' instead // public int N.I.explicit (int x) => x; @@ -2097,14 +2235,7 @@ public void OperatorDeclaration_ExplicitImplementation_03() { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingDeclaration("public int N.I.explicit (int x) => x;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? - errors.Append( - // (1,12): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // public int N.I.explicit (int x) => x; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 12) - ).ToArray() : - errors); + UsingDeclaration(text, options: options.WithLanguageVersion(version), errors); N(SyntaxKind.OperatorDeclaration); { @@ -2162,6 +2293,44 @@ public void OperatorDeclaration_ExplicitImplementation_03() [Fact] public void OperatorDeclaration_ExplicitImplementation_04() { + var text = "public int N.I operator +(int x) => x;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,22): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { public int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 22), + // (1,22): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { public int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I ").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 22), + // (1,22): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { public int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 22), + // (1,26): error CS1003: Syntax error, '.' expected + // class C { public int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 26), + // (1,35): error CS0106: The modifier 'public' is not valid for this item + // class C { public int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "+").WithArguments("public").WithLocation(1, 35), + // (1,35): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { public int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "+").WithArguments("C.operator +(int)").WithLocation(1, 35)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,22): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { public int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 22), + // (1,22): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { public int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 22), + // (1,26): error CS1003: Syntax error, '.' expected + // class C { public int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 26), + // (1,35): error CS0106: The modifier 'public' is not valid for this item + // class C { public int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "+").WithArguments("public").WithLocation(1, 35), + // (1,35): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { public int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "+").WithArguments("C.operator +(int)").WithLocation(1, 35)); + var errors = new[] { // (1,16): error CS1003: Syntax error, '.' expected // public int N.I operator +(int x) => x; @@ -2172,14 +2341,7 @@ public void OperatorDeclaration_ExplicitImplementation_04() { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingDeclaration("public int N.I operator +(int x) => x;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? - errors.Append( - // (1,12): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // public int N.I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I ").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 12) - ).ToArray() : - errors); + UsingDeclaration(text, options: options.WithLanguageVersion(version), errors); N(SyntaxKind.OperatorDeclaration); { @@ -2237,6 +2399,44 @@ public void OperatorDeclaration_ExplicitImplementation_04() [Fact] public void OperatorDeclaration_ExplicitImplementation_05() { + var text = "public int I operator +(int x) => x;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,22): error CS0246: The type or namespace name 'I' could not be found (are you missing a using directive or an assembly reference?) + // class C { public int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "I").WithArguments("I").WithLocation(1, 22), + // (1,22): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { public int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "I ").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 22), + // (1,22): error CS0538: 'I' in explicit interface declaration is not an interface + // class C { public int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "I").WithArguments("I").WithLocation(1, 22), + // (1,24): error CS1003: Syntax error, '.' expected + // class C { public int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 24), + // (1,33): error CS0106: The modifier 'public' is not valid for this item + // class C { public int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "+").WithArguments("public").WithLocation(1, 33), + // (1,33): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { public int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "+").WithArguments("C.operator +(int)").WithLocation(1, 33)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,22): error CS0246: The type or namespace name 'I' could not be found (are you missing a using directive or an assembly reference?) + // class C { public int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "I").WithArguments("I").WithLocation(1, 22), + // (1,22): error CS0538: 'I' in explicit interface declaration is not an interface + // class C { public int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "I").WithArguments("I").WithLocation(1, 22), + // (1,24): error CS1003: Syntax error, '.' expected + // class C { public int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 24), + // (1,33): error CS0106: The modifier 'public' is not valid for this item + // class C { public int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "+").WithArguments("public").WithLocation(1, 33), + // (1,33): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { public int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "+").WithArguments("C.operator +(int)").WithLocation(1, 33)); + var errors = new[] { // (1,14): error CS1003: Syntax error, '.' expected // public int I operator +(int x) => x; @@ -2247,14 +2447,7 @@ public void OperatorDeclaration_ExplicitImplementation_05() { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingDeclaration("public int I operator +(int x) => x;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? - errors.Append( - // (1,12): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // public int I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "I ").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 12) - ).ToArray() : - errors); + UsingDeclaration(text, options: options.WithLanguageVersion(version), errors); N(SyntaxKind.OperatorDeclaration); { @@ -2621,17 +2814,43 @@ public void OperatorDeclaration_ExplicitImplementation_10() [Fact] public void OperatorDeclaration_ExplicitImplementation_11() { - var error = - // (1,12): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // public int N.I.operator +(int x, int y) => x + y; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 12); + var text = "public int N.I.operator +(int x, int y) => x + y;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,22): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { public int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 22), + // (1,22): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { public int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 22), + // (1,22): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { public int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 22), + // (1,35): error CS0106: The modifier 'public' is not valid for this item + // class C { public int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "+").WithArguments("public").WithLocation(1, 35), + // (1,35): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int, int)' must be declared static + // class C { public int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "+").WithArguments("C.operator +(int, int)").WithLocation(1, 35)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,22): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { public int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 22), + // (1,22): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { public int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 22), + // (1,35): error CS0106: The modifier 'public' is not valid for this item + // class C { public int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "+").WithArguments("public").WithLocation(1, 35), + // (1,35): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int, int)' must be declared static + // class C { public int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "+").WithArguments("C.operator +(int, int)").WithLocation(1, 35)); foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingTree("public int N.I.operator +(int x, int y) => x + y;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? new[] { error } : new DiagnosticDescription[] { }); + UsingTree(text, options: options.WithLanguageVersion(version)); N(SyntaxKind.CompilationUnit); { @@ -2710,6 +2929,56 @@ public void OperatorDeclaration_ExplicitImplementation_11() [Fact] public void OperatorDeclaration_ExplicitImplementation_12() { + var text = "public int N.I.implicit (int x) => x;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,18): error CS1553: Declaration is not valid; use '+ operator (...' instead + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(1, 18), + // (1,22): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 22), + // (1,22): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 22), + // (1,22): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 22), + // (1,26): error CS1003: Syntax error, 'operator' expected + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "implicit").WithArguments("operator").WithLocation(1, 26), + // (1,26): error CS1019: Overloadable unary operator expected + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "implicit").WithLocation(1, 26), + // (1,26): error CS0106: The modifier 'public' is not valid for this item + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "").WithArguments("public").WithLocation(1, 26), + // (1,26): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "").WithArguments("C.operator +(int)").WithLocation(1, 26)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,18): error CS1553: Declaration is not valid; use '+ operator (...' instead + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(1, 18), + // (1,22): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 22), + // (1,22): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 22), + // (1,26): error CS1003: Syntax error, 'operator' expected + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "implicit").WithArguments("operator").WithLocation(1, 26), + // (1,26): error CS1019: Overloadable unary operator expected + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "implicit").WithLocation(1, 26), + // (1,26): error CS0106: The modifier 'public' is not valid for this item + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "").WithArguments("public").WithLocation(1, 26), + // (1,26): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { public int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "").WithArguments("C.operator +(int)").WithLocation(1, 26)); + var errors = new[] { // (1,8): error CS1553: Declaration is not valid; use '+ operator (...' instead // public int N.I.implicit (int x) => x; @@ -2726,14 +2995,7 @@ public void OperatorDeclaration_ExplicitImplementation_12() { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingTree("public int N.I.implicit (int x) => x;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? - errors.Append( - // (1,12): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // public int N.I.implicit (int x) => x; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 12) - ).ToArray() : - errors); + UsingTree(text, options: options.WithLanguageVersion(version), errors); N(SyntaxKind.CompilationUnit); { @@ -2795,6 +3057,56 @@ public void OperatorDeclaration_ExplicitImplementation_12() [Fact] public void OperatorDeclaration_ExplicitImplementation_13() { + var text = "public int N.I.explicit (int x) => x;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,18): error CS1553: Declaration is not valid; use '+ operator (...' instead + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(1, 18), + // (1,22): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 22), + // (1,22): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 22), + // (1,22): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 22), + // (1,26): error CS1003: Syntax error, 'operator' expected + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "explicit").WithArguments("operator").WithLocation(1, 26), + // (1,26): error CS1019: Overloadable unary operator expected + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "explicit").WithLocation(1, 26), + // (1,26): error CS0106: The modifier 'public' is not valid for this item + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "").WithArguments("public").WithLocation(1, 26), + // (1,26): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "").WithArguments("C.operator +(int)").WithLocation(1, 26)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,18): error CS1553: Declaration is not valid; use '+ operator (...' instead + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(1, 18), + // (1,22): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 22), + // (1,22): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 22), + // (1,26): error CS1003: Syntax error, 'operator' expected + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "explicit").WithArguments("operator").WithLocation(1, 26), + // (1,26): error CS1019: Overloadable unary operator expected + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "explicit").WithLocation(1, 26), + // (1,26): error CS0106: The modifier 'public' is not valid for this item + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "").WithArguments("public").WithLocation(1, 26), + // (1,26): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { public int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "").WithArguments("C.operator +(int)").WithLocation(1, 26)); + var errors = new[] { // (1,8): error CS1553: Declaration is not valid; use '+ operator (...' instead // public int N.I.explicit (int x) => x; @@ -2811,14 +3123,7 @@ public void OperatorDeclaration_ExplicitImplementation_13() { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingTree("public int N.I.explicit (int x) => x;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? - errors.Append( - // (1,12): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // public int N.I.explicit (int x) => x; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 12) - ).ToArray() : - errors); + UsingTree(text, options: options.WithLanguageVersion(version), errors); N(SyntaxKind.CompilationUnit); { @@ -2880,6 +3185,44 @@ public void OperatorDeclaration_ExplicitImplementation_13() [Fact] public void OperatorDeclaration_ExplicitImplementation_14() { + var text = "public int N.I operator +(int x) => x;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,22): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { public int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 22), + // (1,22): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { public int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I ").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 22), + // (1,22): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { public int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 22), + // (1,26): error CS1003: Syntax error, '.' expected + // class C { public int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 26), + // (1,35): error CS0106: The modifier 'public' is not valid for this item + // class C { public int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "+").WithArguments("public").WithLocation(1, 35), + // (1,35): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { public int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "+").WithArguments("C.operator +(int)").WithLocation(1, 35)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,22): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { public int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 22), + // (1,22): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { public int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 22), + // (1,26): error CS1003: Syntax error, '.' expected + // class C { public int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 26), + // (1,35): error CS0106: The modifier 'public' is not valid for this item + // class C { public int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "+").WithArguments("public").WithLocation(1, 35), + // (1,35): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { public int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "+").WithArguments("C.operator +(int)").WithLocation(1, 35)); + var errors = new[] { // (1,16): error CS1003: Syntax error, '.' expected // public int N.I operator +(int x) => x; @@ -2890,14 +3233,7 @@ public void OperatorDeclaration_ExplicitImplementation_14() { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingTree("public int N.I operator +(int x) => x;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? - errors.Append( - // (1,12): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // public int N.I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I ").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 12) - ).ToArray() : - errors); + UsingTree(text, options: options.WithLanguageVersion(version), errors); N(SyntaxKind.CompilationUnit); { @@ -2959,6 +3295,44 @@ public void OperatorDeclaration_ExplicitImplementation_14() [Fact] public void OperatorDeclaration_ExplicitImplementation_15() { + var text = "public int I operator +(int x) => x;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,22): error CS0246: The type or namespace name 'I' could not be found (are you missing a using directive or an assembly reference?) + // class C { public int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "I").WithArguments("I").WithLocation(1, 22), + // (1,22): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { public int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "I ").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 22), + // (1,22): error CS0538: 'I' in explicit interface declaration is not an interface + // class C { public int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "I").WithArguments("I").WithLocation(1, 22), + // (1,24): error CS1003: Syntax error, '.' expected + // class C { public int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 24), + // (1,33): error CS0106: The modifier 'public' is not valid for this item + // class C { public int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "+").WithArguments("public").WithLocation(1, 33), + // (1,33): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { public int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "+").WithArguments("C.operator +(int)").WithLocation(1, 33)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,22): error CS0246: The type or namespace name 'I' could not be found (are you missing a using directive or an assembly reference?) + // class C { public int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "I").WithArguments("I").WithLocation(1, 22), + // (1,22): error CS0538: 'I' in explicit interface declaration is not an interface + // class C { public int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "I").WithArguments("I").WithLocation(1, 22), + // (1,24): error CS1003: Syntax error, '.' expected + // class C { public int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 24), + // (1,33): error CS0106: The modifier 'public' is not valid for this item + // class C { public int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "+").WithArguments("public").WithLocation(1, 33), + // (1,33): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { public int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "+").WithArguments("C.operator +(int)").WithLocation(1, 33)); + var errors = new[] { // (1,14): error CS1003: Syntax error, '.' expected // public int I operator +(int x) => x; @@ -2969,14 +3343,7 @@ public void OperatorDeclaration_ExplicitImplementation_15() { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingTree("public int I operator +(int x) => x;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? - errors.Append( - // (1,12): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // public int I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "I ").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 12) - ).ToArray() : - errors); + UsingTree(text, options: options.WithLanguageVersion(version), errors); N(SyntaxKind.CompilationUnit); { @@ -3493,17 +3860,37 @@ public void OperatorDeclaration_ExplicitImplementation_22() [Fact] public void OperatorDeclaration_ExplicitImplementation_23() { - var error = - // (1,5): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // int N.I.operator +(int x, int y) => x + y; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 5); + var text = "int N.I.operator +(int x, int y) => x + y;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,15): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 15), + // (1,15): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 15), + // (1,15): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 15), + // (1,28): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int, int)' must be declared static + // class C { int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "+").WithArguments("C.operator +(int, int)").WithLocation(1, 28)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,15): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 15), + // (1,15): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 15), + // (1,28): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int, int)' must be declared static + // class C { int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "+").WithArguments("C.operator +(int, int)").WithLocation(1, 28)); foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingDeclaration("int N.I.operator +(int x, int y) => x + y;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? new[] { error } : new DiagnosticDescription[] { }); + UsingDeclaration(text, options: options.WithLanguageVersion(version)); N(SyntaxKind.OperatorDeclaration); { @@ -3577,6 +3964,50 @@ public void OperatorDeclaration_ExplicitImplementation_23() [Fact] public void OperatorDeclaration_ExplicitImplementation_24() { + var text = "int N.I.implicit (int x) => x;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,11): error CS1553: Declaration is not valid; use '+ operator (...' instead + // class C { int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(1, 11), + // (1,15): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 15), + // (1,15): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 15), + // (1,15): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 15), + // (1,19): error CS1003: Syntax error, 'operator' expected + // class C { int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "implicit").WithArguments("operator").WithLocation(1, 19), + // (1,19): error CS1019: Overloadable unary operator expected + // class C { int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "implicit").WithLocation(1, 19), + // (1,19): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "").WithArguments("C.operator +(int)").WithLocation(1, 19)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,11): error CS1553: Declaration is not valid; use '+ operator (...' instead + // class C { int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(1, 11), + // (1,15): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 15), + // (1,15): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 15), + // (1,19): error CS1003: Syntax error, 'operator' expected + // class C { int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "implicit").WithArguments("operator").WithLocation(1, 19), + // (1,19): error CS1019: Overloadable unary operator expected + // class C { int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "implicit").WithLocation(1, 19), + // (1,19): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "").WithArguments("C.operator +(int)").WithLocation(1, 19)); + var errors = new[] { // (1,1): error CS1553: Declaration is not valid; use '+ operator (...' instead // int N.I.implicit (int x) => x; @@ -3593,14 +4024,7 @@ public void OperatorDeclaration_ExplicitImplementation_24() { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingDeclaration("int N.I.implicit (int x) => x;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? - errors.Append( - // (1,5): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // int N.I.implicit (int x) => x; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 5) - ).ToArray() : - errors); + UsingDeclaration(text, options: options.WithLanguageVersion(version), errors); N(SyntaxKind.OperatorDeclaration); { @@ -3657,6 +4081,50 @@ public void OperatorDeclaration_ExplicitImplementation_24() [Fact] public void OperatorDeclaration_ExplicitImplementation_25() { + var text = "int N.I.explicit (int x) => x;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,11): error CS1553: Declaration is not valid; use '+ operator (...' instead + // class C { int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(1, 11), + // (1,15): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 15), + // (1,15): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 15), + // (1,15): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 15), + // (1,19): error CS1003: Syntax error, 'operator' expected + // class C { int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "explicit").WithArguments("operator").WithLocation(1, 19), + // (1,19): error CS1019: Overloadable unary operator expected + // class C { int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "explicit").WithLocation(1, 19), + // (1,19): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "").WithArguments("C.operator +(int)").WithLocation(1, 19)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,11): error CS1553: Declaration is not valid; use '+ operator (...' instead + // class C { int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(1, 11), + // (1,15): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 15), + // (1,15): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 15), + // (1,19): error CS1003: Syntax error, 'operator' expected + // class C { int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "explicit").WithArguments("operator").WithLocation(1, 19), + // (1,19): error CS1019: Overloadable unary operator expected + // class C { int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "explicit").WithLocation(1, 19), + // (1,19): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "").WithArguments("C.operator +(int)").WithLocation(1, 19)); + var errors = new[] { // (1,1): error CS1553: Declaration is not valid; use '+ operator (...' instead // int N.I.explicit (int x) => x; @@ -3673,14 +4141,7 @@ public void OperatorDeclaration_ExplicitImplementation_25() { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingDeclaration("int N.I.explicit (int x) => x;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? - errors.Append( - // (1,5): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // int N.I.explicit (int x) => x; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 5) - ).ToArray() : - errors); + UsingDeclaration(text, options: options.WithLanguageVersion(version), errors); N(SyntaxKind.OperatorDeclaration); { @@ -3737,6 +4198,38 @@ public void OperatorDeclaration_ExplicitImplementation_25() [Fact] public void OperatorDeclaration_ExplicitImplementation_26() { + var text = "int N.I operator +(int x) => x;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,15): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 15), + // (1,15): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I ").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 15), + // (1,15): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 15), + // (1,19): error CS1003: Syntax error, '.' expected + // class C { int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 19), + // (1,28): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "+").WithArguments("C.operator +(int)").WithLocation(1, 28)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,15): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 15), + // (1,15): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 15), + // (1,19): error CS1003: Syntax error, '.' expected + // class C { int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 19), + // (1,28): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "+").WithArguments("C.operator +(int)").WithLocation(1, 28)); + var errors = new[] { // (1,9): error CS1003: Syntax error, '.' expected // int N.I operator +(int x) => x; @@ -3747,14 +4240,7 @@ public void OperatorDeclaration_ExplicitImplementation_26() { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingDeclaration("int N.I operator +(int x) => x;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? - errors.Append( - // (1,5): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // int N.I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I ").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 5) - ).ToArray() : - errors); + UsingDeclaration(text, options: options.WithLanguageVersion(version), errors); N(SyntaxKind.OperatorDeclaration); { @@ -3811,6 +4297,38 @@ public void OperatorDeclaration_ExplicitImplementation_26() [Fact] public void OperatorDeclaration_ExplicitImplementation_27() { + var text = "int I operator +(int x) => x;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,15): error CS0246: The type or namespace name 'I' could not be found (are you missing a using directive or an assembly reference?) + // class C { int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "I").WithArguments("I").WithLocation(1, 15), + // (1,15): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "I ").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 15), + // (1,15): error CS0538: 'I' in explicit interface declaration is not an interface + // class C { int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "I").WithArguments("I").WithLocation(1, 15), + // (1,17): error CS1003: Syntax error, '.' expected + // class C { int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 17), + // (1,26): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "+").WithArguments("C.operator +(int)").WithLocation(1, 26)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,15): error CS0246: The type or namespace name 'I' could not be found (are you missing a using directive or an assembly reference?) + // class C { int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "I").WithArguments("I").WithLocation(1, 15), + // (1,15): error CS0538: 'I' in explicit interface declaration is not an interface + // class C { int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "I").WithArguments("I").WithLocation(1, 15), + // (1,17): error CS1003: Syntax error, '.' expected + // class C { int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 17), + // (1,26): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "+").WithArguments("C.operator +(int)").WithLocation(1, 26)); + var errors = new[] { // (1,7): error CS1003: Syntax error, '.' expected // int I operator +(int x) => x; @@ -3821,14 +4339,7 @@ public void OperatorDeclaration_ExplicitImplementation_27() { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingDeclaration("int I operator +(int x) => x;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? - errors.Append( - // (1,5): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // int I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "I ").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 5) - ).ToArray() : - errors); + UsingDeclaration(text, options: options.WithLanguageVersion(version), errors); N(SyntaxKind.OperatorDeclaration); { @@ -4189,17 +4700,37 @@ public void OperatorDeclaration_ExplicitImplementation_32() [Fact] public void OperatorDeclaration_ExplicitImplementation_33() { - var error = - // (1,5): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // int N.I.operator +(int x, int y) => x + y; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 5); + var text = "int N.I.operator +(int x, int y) => x + y;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,15): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 15), + // (1,15): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 15), + // (1,15): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 15), + // (1,28): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int, int)' must be declared static + // class C { int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "+").WithArguments("C.operator +(int, int)").WithLocation(1, 28)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,15): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 15), + // (1,15): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 15), + // (1,28): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int, int)' must be declared static + // class C { int N.I.operator +(int x, int y) => x + y; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "+").WithArguments("C.operator +(int, int)").WithLocation(1, 28)); foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingTree("int N.I.operator +(int x, int y) => x + y;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? new[] { error } : new DiagnosticDescription[] { }); + UsingTree(text, options: options.WithLanguageVersion(version)); N(SyntaxKind.CompilationUnit); { @@ -4277,6 +4808,50 @@ public void OperatorDeclaration_ExplicitImplementation_33() [Fact] public void OperatorDeclaration_ExplicitImplementation_34() { + var text = "int N.I.implicit (int x) => x;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,11): error CS1553: Declaration is not valid; use '+ operator (...' instead + // class C { int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(1, 11), + // (1,15): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 15), + // (1,15): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 15), + // (1,15): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 15), + // (1,19): error CS1003: Syntax error, 'operator' expected + // class C { int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "implicit").WithArguments("operator").WithLocation(1, 19), + // (1,19): error CS1019: Overloadable unary operator expected + // class C { int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "implicit").WithLocation(1, 19), + // (1,19): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "").WithArguments("C.operator +(int)").WithLocation(1, 19)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,11): error CS1553: Declaration is not valid; use '+ operator (...' instead + // class C { int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(1, 11), + // (1,15): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 15), + // (1,15): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 15), + // (1,19): error CS1003: Syntax error, 'operator' expected + // class C { int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "implicit").WithArguments("operator").WithLocation(1, 19), + // (1,19): error CS1019: Overloadable unary operator expected + // class C { int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "implicit").WithLocation(1, 19), + // (1,19): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { int N.I.implicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "").WithArguments("C.operator +(int)").WithLocation(1, 19)); + var errors = new[] { // (1,1): error CS1553: Declaration is not valid; use '+ operator (...' instead // int N.I.implicit (int x) => x; @@ -4293,14 +4868,7 @@ public void OperatorDeclaration_ExplicitImplementation_34() { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingTree("int N.I.implicit (int x) => x;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? - errors.Append( - // (1,5): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // int N.I.implicit (int x) => x; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 5) - ).ToArray() : - errors); + UsingTree(text, options: options.WithLanguageVersion(version), errors); N(SyntaxKind.CompilationUnit); { @@ -4361,6 +4929,50 @@ public void OperatorDeclaration_ExplicitImplementation_34() [Fact] public void OperatorDeclaration_ExplicitImplementation_35() { + var text = "int N.I.explicit (int x) => x;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,11): error CS1553: Declaration is not valid; use '+ operator (...' instead + // class C { int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(1, 11), + // (1,15): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 15), + // (1,15): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 15), + // (1,15): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 15), + // (1,19): error CS1003: Syntax error, 'operator' expected + // class C { int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "explicit").WithArguments("operator").WithLocation(1, 19), + // (1,19): error CS1019: Overloadable unary operator expected + // class C { int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "explicit").WithLocation(1, 19), + // (1,19): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "").WithArguments("C.operator +(int)").WithLocation(1, 19)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,11): error CS1553: Declaration is not valid; use '+ operator (...' instead + // class C { int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_BadOperatorSyntax, "int").WithArguments("+").WithLocation(1, 11), + // (1,15): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 15), + // (1,15): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 15), + // (1,19): error CS1003: Syntax error, 'operator' expected + // class C { int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "explicit").WithArguments("operator").WithLocation(1, 19), + // (1,19): error CS1019: Overloadable unary operator expected + // class C { int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_OvlUnaryOperatorExpected, "explicit").WithLocation(1, 19), + // (1,19): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { int N.I.explicit (int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "").WithArguments("C.operator +(int)").WithLocation(1, 19)); + var errors = new[] { // (1,1): error CS1553: Declaration is not valid; use '+ operator (...' instead // int N.I.explicit (int x) => x; @@ -4377,14 +4989,7 @@ public void OperatorDeclaration_ExplicitImplementation_35() { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingTree("int N.I.explicit (int x) => x;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? - errors.Append( - // (1,5): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // int N.I.explicit (int x) => x; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 5) - ).ToArray() : - errors); + UsingTree(text, options: options.WithLanguageVersion(version), errors); N(SyntaxKind.CompilationUnit); { @@ -4445,6 +5050,38 @@ public void OperatorDeclaration_ExplicitImplementation_35() [Fact] public void OperatorDeclaration_ExplicitImplementation_36() { + var text = "int N.I operator +(int x) => x;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,15): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 15), + // (1,15): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I ").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 15), + // (1,15): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 15), + // (1,19): error CS1003: Syntax error, '.' expected + // class C { int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 19), + // (1,28): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "+").WithArguments("C.operator +(int)").WithLocation(1, 28)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,15): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 15), + // (1,15): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 15), + // (1,19): error CS1003: Syntax error, '.' expected + // class C { int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 19), + // (1,28): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { int N.I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "+").WithArguments("C.operator +(int)").WithLocation(1, 28)); + var errors = new[] { // (1,9): error CS1003: Syntax error, '.' expected // int N.I operator +(int x) => x; @@ -4455,14 +5092,7 @@ public void OperatorDeclaration_ExplicitImplementation_36() { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingTree("int N.I operator +(int x) => x;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? - errors.Append( - // (1,5): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // int N.I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I ").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 5) - ).ToArray() : - errors); + UsingTree(text, options: options.WithLanguageVersion(version), errors); N(SyntaxKind.CompilationUnit); { @@ -4523,6 +5153,38 @@ public void OperatorDeclaration_ExplicitImplementation_36() [Fact] public void OperatorDeclaration_ExplicitImplementation_37() { + var text = "int I operator +(int x) => x;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,15): error CS0246: The type or namespace name 'I' could not be found (are you missing a using directive or an assembly reference?) + // class C { int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "I").WithArguments("I").WithLocation(1, 15), + // (1,15): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "I ").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 15), + // (1,15): error CS0538: 'I' in explicit interface declaration is not an interface + // class C { int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "I").WithArguments("I").WithLocation(1, 15), + // (1,17): error CS1003: Syntax error, '.' expected + // class C { int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 17), + // (1,26): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "+").WithArguments("C.operator +(int)").WithLocation(1, 26)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,15): error CS0246: The type or namespace name 'I' could not be found (are you missing a using directive or an assembly reference?) + // class C { int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "I").WithArguments("I").WithLocation(1, 15), + // (1,15): error CS0538: 'I' in explicit interface declaration is not an interface + // class C { int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "I").WithArguments("I").WithLocation(1, 15), + // (1,17): error CS1003: Syntax error, '.' expected + // class C { int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 17), + // (1,26): error CS8930: Explicit implementation of a user-defined operator 'C.operator +(int)' must be declared static + // class C { int I operator +(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "+").WithArguments("C.operator +(int)").WithLocation(1, 26)); + var errors = new[] { // (1,7): error CS1003: Syntax error, '.' expected // int I operator +(int x) => x; @@ -4533,14 +5195,7 @@ public void OperatorDeclaration_ExplicitImplementation_37() { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingTree("int I operator +(int x) => x;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? - errors.Append( - // (1,5): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // int I operator +(int x) => x; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "I ").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 5) - ).ToArray() : - errors); + UsingTree(text, options: options.WithLanguageVersion(version), errors); N(SyntaxKind.CompilationUnit); { @@ -5232,17 +5887,37 @@ public void OperatorDeclaration_ExplicitImplementation_47() [Fact] public void ConversionDeclaration_ExplicitImplementation_01() { - var error = - // (1,10): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // implicit N.I.operator int(int x) => x; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 10); + var text = "implicit N.I.operator int(int x) => x;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,20): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { implicit N.I.operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 20), + // (1,20): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { implicit N.I.operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 20), + // (1,20): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { implicit N.I.operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 20), + // (1,33): error CS8930: Explicit implementation of a user-defined operator 'C.implicit operator int(int)' must be declared static + // class C { implicit N.I.operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "int").WithArguments("C.implicit operator int(int)").WithLocation(1, 33)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,20): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { implicit N.I.operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 20), + // (1,20): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { implicit N.I.operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 20), + // (1,33): error CS8930: Explicit implementation of a user-defined operator 'C.implicit operator int(int)' must be declared static + // class C { implicit N.I.operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "int").WithArguments("C.implicit operator int(int)").WithLocation(1, 33)); foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingDeclaration("implicit N.I.operator int(int x) => x;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? new[] { error } : new DiagnosticDescription[] { }); + UsingDeclaration(text, options: options.WithLanguageVersion(version)); N(SyntaxKind.ConversionOperatorDeclaration); { @@ -5299,24 +5974,50 @@ public void ConversionDeclaration_ExplicitImplementation_01() [Fact] public void ConversionDeclaration_ExplicitImplementation_02() { - var errors = new[] { + var text = "N.I.operator int(int x) => x;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,11): error CS1003: Syntax error, 'explicit' expected + // class C { N.I.operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "N").WithArguments("explicit").WithLocation(1, 11), + // (1,11): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { N.I.operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 11), + // (1,11): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { N.I.operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 11), + // (1,11): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { N.I.operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 11), + // (1,24): error CS8930: Explicit implementation of a user-defined operator 'C.explicit operator int(int)' must be declared static + // class C { N.I.operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "int").WithArguments("C.explicit operator int(int)").WithLocation(1, 24)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,11): error CS1003: Syntax error, 'explicit' expected + // class C { N.I.operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "N").WithArguments("explicit").WithLocation(1, 11), + // (1,11): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { N.I.operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 11), + // (1,11): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { N.I.operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 11), + // (1,24): error CS8930: Explicit implementation of a user-defined operator 'C.explicit operator int(int)' must be declared static + // class C { N.I.operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "int").WithArguments("C.explicit operator int(int)").WithLocation(1, 24)); + + var errors = new[] + { // (1,1): error CS1003: Syntax error, 'explicit' expected // N.I.operator int(int x) => x; Diagnostic(ErrorCode.ERR_SyntaxError, "N").WithArguments("explicit").WithLocation(1, 1) - }; + }; foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingDeclaration("N.I.operator int(int x) => x;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? - errors.Append( - // (1,1): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // N.I.operator int(int x) => x; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 1) - ).ToArray() : - errors); + UsingDeclaration(text, options: options.WithLanguageVersion(version), errors); N(SyntaxKind.ConversionOperatorDeclaration); { @@ -5424,24 +6125,50 @@ public void ConversionDeclaration_ExplicitImplementation_03() [Fact] public void ConversionDeclaration_ExplicitImplementation_04() { - var errors = new[] { + var text = "implicit N.I operator int(int x) => x;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,20): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { implicit N.I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 20), + // (1,20): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { implicit N.I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I ").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 20), + // (1,20): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { implicit N.I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 20), + // (1,24): error CS1003: Syntax error, '.' expected + // class C { implicit N.I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 24), + // (1,33): error CS8930: Explicit implementation of a user-defined operator 'C.implicit operator int(int)' must be declared static + // class C { implicit N.I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "int").WithArguments("C.implicit operator int(int)").WithLocation(1, 33)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,20): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { implicit N.I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 20), + // (1,20): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { implicit N.I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 20), + // (1,24): error CS1003: Syntax error, '.' expected + // class C { implicit N.I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 24), + // (1,33): error CS8930: Explicit implementation of a user-defined operator 'C.implicit operator int(int)' must be declared static + // class C { implicit N.I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "int").WithArguments("C.implicit operator int(int)").WithLocation(1, 33)); + + var errors = new[] + { // (1,14): error CS1003: Syntax error, '.' expected // implicit N.I operator int(int x) => x; Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 14) - }; + }; foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingDeclaration("implicit N.I operator int(int x) => x;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? - errors.Append( - // (1,10): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // implicit N.I operator int(int x) => x; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I ").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 10) - ).ToArray() : - errors); + UsingDeclaration(text, options: options.WithLanguageVersion(version), errors); N(SyntaxKind.ConversionOperatorDeclaration); { @@ -5498,24 +6225,50 @@ public void ConversionDeclaration_ExplicitImplementation_04() [Fact] public void ConversionDeclaration_ExplicitImplementation_05() { - var errors = new[] { + var text = "explicit I operator int(int x) => x;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,20): error CS0246: The type or namespace name 'I' could not be found (are you missing a using directive or an assembly reference?) + // class C { explicit I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "I").WithArguments("I").WithLocation(1, 20), + // (1,20): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { explicit I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "I ").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 20), + // (1,20): error CS0538: 'I' in explicit interface declaration is not an interface + // class C { explicit I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "I").WithArguments("I").WithLocation(1, 20), + // (1,22): error CS1003: Syntax error, '.' expected + // class C { explicit I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 22), + // (1,31): error CS8930: Explicit implementation of a user-defined operator 'C.explicit operator int(int)' must be declared static + // class C { explicit I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "int").WithArguments("C.explicit operator int(int)").WithLocation(1, 31)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,20): error CS0246: The type or namespace name 'I' could not be found (are you missing a using directive or an assembly reference?) + // class C { explicit I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "I").WithArguments("I").WithLocation(1, 20), + // (1,20): error CS0538: 'I' in explicit interface declaration is not an interface + // class C { explicit I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "I").WithArguments("I").WithLocation(1, 20), + // (1,22): error CS1003: Syntax error, '.' expected + // class C { explicit I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 22), + // (1,31): error CS8930: Explicit implementation of a user-defined operator 'C.explicit operator int(int)' must be declared static + // class C { explicit I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "int").WithArguments("C.explicit operator int(int)").WithLocation(1, 31)); + + var errors = new[] + { // (1,12): error CS1003: Syntax error, '.' expected // explicit I operator int(int x) => x; Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 12) - }; + }; foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingDeclaration("explicit I operator int(int x) => x;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? - errors.Append( - // (1,10): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // explicit I operator int(int x) => x; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "I ").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 10) - ).ToArray() : - errors); + UsingDeclaration(text, options: options.WithLanguageVersion(version), errors); N(SyntaxKind.ConversionOperatorDeclaration); { @@ -5859,17 +6612,37 @@ public void ConversionDeclaration_ExplicitImplementation_10() [Fact] public void ConversionDeclaration_ExplicitImplementation_11() { - var error = - // (1,10): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // explicit N.I.operator int(int x) => x; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 10); + var text = "explicit N.I.operator int(int x) => x;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,20): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { explicit N.I.operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 20), + // (1,20): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { explicit N.I.operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 20), + // (1,20): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { explicit N.I.operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 20), + // (1,33): error CS8930: Explicit implementation of a user-defined operator 'C.explicit operator int(int)' must be declared static + // class C { explicit N.I.operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "int").WithArguments("C.explicit operator int(int)").WithLocation(1, 33)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,20): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { explicit N.I.operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 20), + // (1,20): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { explicit N.I.operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 20), + // (1,33): error CS8930: Explicit implementation of a user-defined operator 'C.explicit operator int(int)' must be declared static + // class C { explicit N.I.operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "int").WithArguments("C.explicit operator int(int)").WithLocation(1, 33)); foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingTree("explicit N.I.operator int(int x) => x;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? new[] { error } : new DiagnosticDescription[] { }); + UsingTree(text, options: options.WithLanguageVersion(version)); N(SyntaxKind.CompilationUnit); { @@ -5930,27 +6703,59 @@ public void ConversionDeclaration_ExplicitImplementation_11() [Fact] public void ConversionDeclaration_ExplicitImplementation_12() { - var errors = new[] { + var text = "implicit N.I int(int x) => x;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,20): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { implicit N.I int(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 20), + // (1,20): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { implicit N.I int(int x) => x; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I ").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 20), + // (1,20): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { implicit N.I int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 20), + // (1,24): error CS1003: Syntax error, '.' expected + // class C { implicit N.I int(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments(".").WithLocation(1, 24), + // (1,24): error CS1003: Syntax error, 'operator' expected + // class C { implicit N.I int(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments("operator").WithLocation(1, 24), + // (1,24): error CS8930: Explicit implementation of a user-defined operator 'C.implicit operator int(int)' must be declared static + // class C { implicit N.I int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "int").WithArguments("C.implicit operator int(int)").WithLocation(1, 24)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,20): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { implicit N.I int(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 20), + // (1,20): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { implicit N.I int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 20), + // (1,24): error CS1003: Syntax error, '.' expected + // class C { implicit N.I int(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments(".").WithLocation(1, 24), + // (1,24): error CS1003: Syntax error, 'operator' expected + // class C { implicit N.I int(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments("operator").WithLocation(1, 24), + // (1,24): error CS8930: Explicit implementation of a user-defined operator 'C.implicit operator int(int)' must be declared static + // class C { implicit N.I int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "int").WithArguments("C.implicit operator int(int)").WithLocation(1, 24)); + + var errors = new[] + { // (1,14): error CS1003: Syntax error, '.' expected // implicit N.I int(int x) => x; Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments(".").WithLocation(1, 14), // (1,14): error CS1003: Syntax error, 'operator' expected // implicit N.I int(int x) => x; Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments("operator").WithLocation(1, 14) - }; + }; foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingTree("implicit N.I int(int x) => x;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? - errors.Append( - // (1,10): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // implicit N.I int(int x) => x; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I ").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 10) - ).ToArray() : - errors); + UsingTree(text, options: options.WithLanguageVersion(version), errors); N(SyntaxKind.CompilationUnit); { @@ -6011,24 +6816,50 @@ public void ConversionDeclaration_ExplicitImplementation_12() [Fact] public void ConversionDeclaration_ExplicitImplementation_13() { - var errors = new[] { + var text = "explicit N.I. int(int x) => x;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,20): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { explicit N.I. int(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 20), + // (1,20): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { explicit N.I. int(int x) => x; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 20), + // (1,20): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { explicit N.I. int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 20), + // (1,25): error CS1003: Syntax error, 'operator' expected + // class C { explicit N.I. int(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments("operator").WithLocation(1, 25), + // (1,25): error CS8930: Explicit implementation of a user-defined operator 'C.explicit operator int(int)' must be declared static + // class C { explicit N.I. int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "int").WithArguments("C.explicit operator int(int)").WithLocation(1, 25)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,20): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { explicit N.I. int(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 20), + // (1,20): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { explicit N.I. int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 20), + // (1,25): error CS1003: Syntax error, 'operator' expected + // class C { explicit N.I. int(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments("operator").WithLocation(1, 25), + // (1,25): error CS8930: Explicit implementation of a user-defined operator 'C.explicit operator int(int)' must be declared static + // class C { explicit N.I. int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "int").WithArguments("C.explicit operator int(int)").WithLocation(1, 25)); + + var errors = new[] + { // (1,15): error CS1003: Syntax error, 'operator' expected // explicit N.I. int(int x) => x; Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments("operator").WithLocation(1, 15) - }; + }; foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingTree("explicit N.I. int(int x) => x;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? - errors.Append( - // (1,10): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // explicit N.I. int(int x) => x; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I.").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 10) - ).ToArray() : - errors); + UsingTree(text, options: options.WithLanguageVersion(version), errors); N(SyntaxKind.CompilationUnit); { @@ -6089,24 +6920,50 @@ public void ConversionDeclaration_ExplicitImplementation_13() [Fact] public void ConversionDeclaration_ExplicitImplementation_14() { - var errors = new[] { + var text = "implicit N.I operator int(int x) => x;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,20): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { implicit N.I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 20), + // (1,20): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { implicit N.I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I ").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 20), + // (1,20): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { implicit N.I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 20), + // (1,24): error CS1003: Syntax error, '.' expected + // class C { implicit N.I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 24), + // (1,33): error CS8930: Explicit implementation of a user-defined operator 'C.implicit operator int(int)' must be declared static + // class C { implicit N.I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "int").WithArguments("C.implicit operator int(int)").WithLocation(1, 33)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,20): error CS0246: The type or namespace name 'N' could not be found (are you missing a using directive or an assembly reference?) + // class C { implicit N.I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "N").WithArguments("N").WithLocation(1, 20), + // (1,20): error CS0538: 'N.I' in explicit interface declaration is not an interface + // class C { implicit N.I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "N.I").WithArguments("N.I").WithLocation(1, 20), + // (1,24): error CS1003: Syntax error, '.' expected + // class C { implicit N.I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 24), + // (1,33): error CS8930: Explicit implementation of a user-defined operator 'C.implicit operator int(int)' must be declared static + // class C { implicit N.I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "int").WithArguments("C.implicit operator int(int)").WithLocation(1, 33)); + + var errors = new[] + { // (1,14): error CS1003: Syntax error, '.' expected // implicit N.I operator int(int x) => x; Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 14) - }; + }; foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingTree("implicit N.I operator int(int x) => x;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? - errors.Append( - // (1,10): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // implicit N.I operator int(int x) => x; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "N.I ").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 10) - ).ToArray() : - errors); + UsingTree(text, options: options.WithLanguageVersion(version), errors); N(SyntaxKind.CompilationUnit); { @@ -6167,24 +7024,50 @@ public void ConversionDeclaration_ExplicitImplementation_14() [Fact] public void ConversionDeclaration_ExplicitImplementation_15() { - var errors = new[] { + var text = "explicit I operator int(int x) => x;"; + var classWithText = $"class C {{ {text} }}"; + CreateCompilation(classWithText, parseOptions: TestOptions.Regular9).VerifyDiagnostics( + // (1,20): error CS0246: The type or namespace name 'I' could not be found (are you missing a using directive or an assembly reference?) + // class C { explicit I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "I").WithArguments("I").WithLocation(1, 20), + // (1,20): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. + // class C { explicit I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "I ").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 20), + // (1,20): error CS0538: 'I' in explicit interface declaration is not an interface + // class C { explicit I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "I").WithArguments("I").WithLocation(1, 20), + // (1,22): error CS1003: Syntax error, '.' expected + // class C { explicit I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 22), + // (1,31): error CS8930: Explicit implementation of a user-defined operator 'C.explicit operator int(int)' must be declared static + // class C { explicit I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "int").WithArguments("C.explicit operator int(int)").WithLocation(1, 31)); + CreateCompilation(classWithText, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,20): error CS0246: The type or namespace name 'I' could not be found (are you missing a using directive or an assembly reference?) + // class C { explicit I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "I").WithArguments("I").WithLocation(1, 20), + // (1,20): error CS0538: 'I' in explicit interface declaration is not an interface + // class C { explicit I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationNotInterface, "I").WithArguments("I").WithLocation(1, 20), + // (1,22): error CS1003: Syntax error, '.' expected + // class C { explicit I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 22), + // (1,31): error CS8930: Explicit implementation of a user-defined operator 'C.explicit operator int(int)' must be declared static + // class C { explicit I operator int(int x) => x; } + Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, "int").WithArguments("C.explicit operator int(int)").WithLocation(1, 31)); + + var errors = new[] + { // (1,12): error CS1003: Syntax error, '.' expected // explicit I operator int(int x) => x; Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".").WithLocation(1, 12) - }; + }; foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) { foreach (var version in new[] { LanguageVersion.CSharp9, LanguageVersion.Preview }) { - UsingTree("explicit I operator int(int x) => x;", options: options.WithLanguageVersion(version), - version == LanguageVersion.CSharp9 ? - errors.Append( - // (1,10): error CS8773: Feature 'static abstract members in interfaces' is not available in C# 9.0. Please use language version 11.0 or greater. - // explicit I operator int(int x) => x; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "I ").WithArguments("static abstract members in interfaces", "11.0").WithLocation(1, 10) - ).ToArray() : - errors); + UsingTree(text, options: options.WithLanguageVersion(version), errors); N(SyntaxKind.CompilationUnit); { diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs index fe272d5b2c608..d69b7af9dde3a 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs @@ -5665,14 +5665,27 @@ void Goo() } } "; + + CreateCompilation(text, parseOptions: TestOptions.Regular5).VerifyEmitDiagnostics( + // (6,21): error CS0246: The type or namespace name 'Goo' could not be found (are you missing a using directive or an assembly reference?) + // var q = new Goo { }; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Goo").WithArguments("Goo").WithLocation(6, 21)); + CreateCompilation(text, parseOptions: TestOptions.Regular2).VerifyEmitDiagnostics( + // (6,9): error CS8023: Feature 'implicitly typed local variable' is not available in C# 2. Please use language version 3 or greater. + // var q = new Goo { }; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion2, "var").WithArguments("implicitly typed local variable", "3").WithLocation(6, 9), + // (6,21): error CS0246: The type or namespace name 'Goo' could not be found (are you missing a using directive or an assembly reference?) + // var q = new Goo { }; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Goo").WithArguments("Goo").WithLocation(6, 21), + // (6,25): error CS8023: Feature 'object initializer' is not available in C# 2. Please use language version 3 or greater. + // var q = new Goo { }; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion2, "{").WithArguments("object initializer", "3").WithLocation(6, 25)); + var tree = SyntaxFactory.ParseSyntaxTree(text, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp5)); tree.GetCompilationUnitRoot().GetDiagnostics().Verify(); tree = SyntaxFactory.ParseSyntaxTree(text, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp2)); - tree.GetCompilationUnitRoot().GetDiagnostics().Verify( - // (6,25): error CS8023: Feature 'object initializer' is not available in C# 2. Please use language version 3 or greater. - // var q = new Goo { }; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion2, "{").WithArguments("object initializer", "3")); + tree.GetCompilationUnitRoot().GetDiagnostics().Verify(); } [Fact] @@ -6258,11 +6271,20 @@ void M() } } "; - SyntaxFactory.ParseSyntaxTree(text, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp3)).GetDiagnostics().Verify(); - SyntaxFactory.ParseSyntaxTree(text, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp2)).GetDiagnostics().Verify( + CreateCompilation(text, parseOptions: TestOptions.Regular3).VerifyEmitDiagnostics( + // (6,24): error CS0117: 'C' does not contain a definition for 'Goo' + // return new C { Goo = 1 }; + Diagnostic(ErrorCode.ERR_NoSuchMember, "Goo").WithArguments("C", "Goo").WithLocation(6, 24)); + CreateCompilation(text, parseOptions: TestOptions.Regular2).VerifyEmitDiagnostics( // (6,22): error CS8023: Feature 'object initializer' is not available in C# 2. Please use language version 3 or greater. // return new C { Goo = 1 }; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion2, "{").WithArguments("object initializer", "3")); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion2, "{").WithArguments("object initializer", "3").WithLocation(6, 22), + // (6,24): error CS0117: 'C' does not contain a definition for 'Goo' + // return new C { Goo = 1 }; + Diagnostic(ErrorCode.ERR_NoSuchMember, "Goo").WithArguments("C", "Goo").WithLocation(6, 24)); + + SyntaxFactory.ParseSyntaxTree(text, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp3)).GetDiagnostics().Verify(); + SyntaxFactory.ParseSyntaxTree(text, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp2)).GetDiagnostics().Verify(); } [Fact] @@ -6277,11 +6299,20 @@ void M() } } "; - SyntaxFactory.ParseSyntaxTree(text, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp3)).GetDiagnostics().Verify(); - SyntaxFactory.ParseSyntaxTree(text, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp2)).GetDiagnostics().Verify( + CreateCompilation(text, parseOptions: TestOptions.Regular3).VerifyEmitDiagnostics( + // (6,22): error CS1922: Cannot initialize type 'C' with a collection initializer because it does not implement 'System.Collections.IEnumerable' + // return new C { 1, 2, 3 }; + Diagnostic(ErrorCode.ERR_CollectionInitRequiresIEnumerable, "{ 1, 2, 3 }").WithArguments("C").WithLocation(6, 22)); + CreateCompilation(text, parseOptions: TestOptions.Regular2).VerifyEmitDiagnostics( // (6,22): error CS8023: Feature 'collection initializer' is not available in C# 2. Please use language version 3 or greater. // return new C { 1, 2, 3 }; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion2, "{").WithArguments("collection initializer", "3")); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion2, "{").WithArguments("collection initializer", "3").WithLocation(6, 22), + // (6,22): error CS1922: Cannot initialize type 'C' with a collection initializer because it does not implement 'System.Collections.IEnumerable' + // return new C { 1, 2, 3 }; + Diagnostic(ErrorCode.ERR_CollectionInitRequiresIEnumerable, "{ 1, 2, 3 }").WithArguments("C").WithLocation(6, 22)); + + SyntaxFactory.ParseSyntaxTree(text, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp3)).GetDiagnostics().Verify(); + SyntaxFactory.ParseSyntaxTree(text, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp2)).GetDiagnostics().Verify(); } [Fact] @@ -6390,10 +6421,25 @@ public void WRN_NonECMAFeature() { var source = @"[module:Obsolete()]"; SyntaxFactory.ParseSyntaxTree(source, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp2)).GetDiagnostics().Verify(); - SyntaxFactory.ParseSyntaxTree(source, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp1)).GetDiagnostics().Verify( - // (1,2): warning CS1645: Feature 'module as an attribute target specifier' is not part of the standardized ISO C# language specification, and may not be accepted by other compilers + SyntaxFactory.ParseSyntaxTree(source, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp1)).GetDiagnostics().Verify(); + + CreateCompilation(source, parseOptions: TestOptions.Regular2).VerifyDiagnostics( + // (1,9): error CS0246: The type or namespace name 'ObsoleteAttribute' could not be found (are you missing a using directive or an assembly reference?) + // [module:Obsolete()] + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Obsolete").WithArguments("ObsoleteAttribute").WithLocation(1, 9), + // (1,9): error CS0246: The type or namespace name 'Obsolete' could not be found (are you missing a using directive or an assembly reference?) // [module:Obsolete()] - Diagnostic(ErrorCode.WRN_NonECMAFeature, "module:").WithArguments("module as an attribute target specifier")); + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Obsolete").WithArguments("Obsolete").WithLocation(1, 9)); + CreateCompilation(source, parseOptions: TestOptions.Regular1).VerifyDiagnostics( + // (1,2): warning CS1645: Feature 'IDS_FeatureModuleAttrLoc' is not part of the standardized ISO C# language specification, and may not be accepted by other compilers + // [module:Obsolete()] + Diagnostic(ErrorCode.WRN_NonECMAFeature, "module:").WithArguments("IDS_FeatureModuleAttrLoc").WithLocation(1, 2), + // (1,9): error CS0246: The type or namespace name 'ObsoleteAttribute' could not be found (are you missing a using directive or an assembly reference?) + // [module:Obsolete()] + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Obsolete").WithArguments("ObsoleteAttribute").WithLocation(1, 9), + // (1,9): error CS0246: The type or namespace name 'Obsolete' could not be found (are you missing a using directive or an assembly reference?) + // [module:Obsolete()] + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Obsolete").WithArguments("Obsolete").WithLocation(1, 9)); } [Fact] @@ -6440,7 +6486,7 @@ void P(object o) Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion5, "=").WithArguments("auto property initializer", "6").WithLocation(3, 20), // (5,13): error CS8026: Feature 'expression-bodied method' is not available in C# 5. Please use language version 6 or greater. // int M() => 12; // expression-bodied method - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion5, "=> 12").WithArguments("expression-bodied method", "6").WithLocation(5, 13), + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion5, "=>").WithArguments("expression-bodied method", "6").WithLocation(5, 13), // (7,11): error CS8026: Feature 'expression-bodied property' is not available in C# 5. Please use language version 6 or greater. // int N => 12; // expression-bodied property Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion5, "=>").WithArguments("expression-bodied property", "6").WithLocation(7, 11), @@ -6449,13 +6495,13 @@ void P(object o) Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion5, "=>").WithArguments("expression-bodied indexer", "6").WithLocation(9, 21), // (11,48): error CS8026: Feature 'expression-bodied method' is not available in C# 5. Please use language version 6 or greater. // public static int operator +(Goo a, Goo b) => null; // expression-bodied operator - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion5, "=> null").WithArguments("expression-bodied method", "6").WithLocation(11, 48), + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion5, "=>").WithArguments("expression-bodied method", "6").WithLocation(11, 48), // (11,51): error CS0037: Cannot convert null to 'int' because it is a non-nullable value type // public static int operator +(Goo a, Goo b) => null; // expression-bodied operator Diagnostic(ErrorCode.ERR_ValueCantBeNull, "null").WithArguments("int").WithLocation(11, 51), // (13,49): error CS8026: Feature 'expression-bodied method' is not available in C# 5. Please use language version 6 or greater. // public static explicit operator bool(Goo a) => false; // expression-bodied conversion operator - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion5, "=> false").WithArguments("expression-bodied method", "6").WithLocation(13, 49), + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion5, "=>").WithArguments("expression-bodied method", "6").WithLocation(13, 49), // (18,18): error CS0246: The type or namespace name 'Exception' could not be found (are you missing a using directive or an assembly reference?) // } catch (Exception ex) when (ex.ToString() == null) { // exception filter Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Exception").WithArguments("Exception").WithLocation(18, 18), @@ -6471,16 +6517,7 @@ void P(object o) SyntaxFactory.ParseSyntaxTree(source, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp6)).GetDiagnostics().Verify(); - SyntaxFactory.ParseSyntaxTree(source, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp5)).GetDiagnostics().Verify( - // (5,13): error CS8026: Feature 'expression-bodied method' is not available in C# 5. Please use language version 6 or greater. - // int M() => 12; // expression-bodied method - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion5, "=> 12").WithArguments("expression-bodied method", "6").WithLocation(5, 13), - // (11,48): error CS8026: Feature 'expression-bodied method' is not available in C# 5. Please use language version 6 or greater. - // public static int operator +(Goo a, Goo b) => null; // expression-bodied operator - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion5, "=> null").WithArguments("expression-bodied method", "6").WithLocation(11, 48), - // (13,49): error CS8026: Feature 'expression-bodied method' is not available in C# 5. Please use language version 6 or greater. - // public static explicit operator bool(Goo a) => false; // expression-bodied conversion operator - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion5, "=> false").WithArguments("expression-bodied method", "6").WithLocation(13, 49)); + SyntaxFactory.ParseSyntaxTree(source, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp5)).GetDiagnostics().Verify(); } [ClrOnlyFact] diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/RecordParsing.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/RecordParsing.cs index 382ae1ea3ff09..5b0afcb3648a6 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/RecordParsing.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/RecordParsing.cs @@ -102,11 +102,21 @@ public void RecordParsing01() EOF(); // In langversion 8, this is a method - UsingTree(text, options: TestOptions.Regular8, + UsingTree(text, options: TestOptions.Regular8); + CreateCompilation(text, parseOptions: TestOptions.Regular8).VerifyDiagnostics( // (1,1): error CS8400: Feature 'top-level statements' is not available in C# 8.0. Please use language version 9.0 or greater. // record C(int X, int Y); - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "record C(int X, int Y);").WithArguments("top-level statements", "9.0").WithLocation(1, 1) - ); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "record C(int X, int Y);").WithArguments("top-level statements", "9.0").WithLocation(1, 1), + // (1,1): error CS0246: The type or namespace name 'record' could not be found (are you missing a using directive or an assembly reference?) + // record C(int X, int Y); + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "record").WithArguments("record").WithLocation(1, 1), + // (1,8): error CS8112: Local function 'C(int, int)' must declare a body because it is not marked 'static extern'. + // record C(int X, int Y); + Diagnostic(ErrorCode.ERR_LocalFunctionMissingBody, "C").WithArguments("C(int, int)").WithLocation(1, 8), + // (1,8): warning CS8321: The local function 'C' is declared but never used + // record C(int X, int Y); + Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "C").WithArguments("C").WithLocation(1, 8)); + N(SyntaxKind.CompilationUnit); { N(SyntaxKind.GlobalStatement); @@ -247,12 +257,20 @@ public void RecordParsing04() [Fact] public void RecordParsing05() { - var tree = ParseTree("record Point;", options: TestOptions.Regular8); - tree.GetDiagnostics().Verify( + var text = "record Point;"; + var tree = ParseTree(text, options: TestOptions.Regular8); + tree.GetDiagnostics().Verify(); + + CreateCompilation(text, parseOptions: TestOptions.Regular8).VerifyDiagnostics( // (1,1): error CS8400: Feature 'top-level statements' is not available in C# 8.0. Please use language version 9.0 or greater. // record Point; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "record Point;").WithArguments("top-level statements", "9.0").WithLocation(1, 1) - ); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "record Point;").WithArguments("top-level statements", "9.0").WithLocation(1, 1), + // (1,1): error CS0246: The type or namespace name 'record' could not be found (are you missing a using directive or an assembly reference?) + // record Point; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "record").WithArguments("record").WithLocation(1, 1), + // (1,8): warning CS0168: The variable 'Point' is declared but never used + // record Point; + Diagnostic(ErrorCode.WRN_UnreferencedVar, "Point").WithArguments("Point").WithLocation(1, 8)); UsingNode((CSharpSyntaxNode)tree.GetRoot()); @@ -314,7 +332,8 @@ public void RecordParsing06() [Fact] public void RecordParsing07() { - var tree = ParseTree("interface P(int x, int y);", options: TestOptions.Regular8); + var text = "interface P(int x, int y);"; + var tree = ParseTree(text, options: TestOptions.Regular8); tree.GetDiagnostics().Verify( // (1,12): error CS1514: { expected // interface P(int x, int y); @@ -322,13 +341,38 @@ public void RecordParsing07() // (1,12): error CS1513: } expected // interface P(int x, int y); Diagnostic(ErrorCode.ERR_RbraceExpected, "(").WithLocation(1, 12), + // (1,12): error CS8803: Top-level statements must precede namespace and type declarations. + // interface P(int x, int y); + Diagnostic(ErrorCode.ERR_TopLevelStatementAfterNamespaceOrType, "(int x, int y);").WithLocation(1, 12)); + + CreateCompilation(text, parseOptions: TestOptions.Regular8).VerifyDiagnostics( + // (1,12): error CS1514: { expected + // interface P(int x, int y); + Diagnostic(ErrorCode.ERR_LbraceExpected, "(").WithLocation(1, 12), + // (1,12): error CS1513: } expected + // interface P(int x, int y); + Diagnostic(ErrorCode.ERR_RbraceExpected, "(").WithLocation(1, 12), + // (1,12): error CS8803: Top-level statements must precede namespace and type declarations. + // interface P(int x, int y); + Diagnostic(ErrorCode.ERR_TopLevelStatementAfterNamespaceOrType, "(int x, int y);").WithLocation(1, 12), // (1,12): error CS8400: Feature 'top-level statements' is not available in C# 8.0. Please use language version 9.0 or greater. // interface P(int x, int y); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "(int x, int y);").WithArguments("top-level statements", "9.0").WithLocation(1, 12), - // (1,12): error CS8803: Top-level statements must precede namespace and type declarations. + // (1,12): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // interface P(int x, int y); - Diagnostic(ErrorCode.ERR_TopLevelStatementAfterNamespaceOrType, "(int x, int y);").WithLocation(1, 12) - ); + Diagnostic(ErrorCode.ERR_IllegalStatement, "(int x, int y)").WithLocation(1, 12), + // (1,13): error CS8185: A declaration is not allowed in this context. + // interface P(int x, int y); + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int x").WithLocation(1, 13), + // (1,13): error CS0165: Use of unassigned local variable 'x' + // interface P(int x, int y); + Diagnostic(ErrorCode.ERR_UseDefViolation, "int x").WithArguments("x").WithLocation(1, 13), + // (1,20): error CS8185: A declaration is not allowed in this context. + // interface P(int x, int y); + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int y").WithLocation(1, 20), + // (1,20): error CS0165: Use of unassigned local variable 'y' + // interface P(int x, int y); + Diagnostic(ErrorCode.ERR_UseDefViolation, "int y").WithArguments("y").WithLocation(1, 20)); } [Fact] @@ -2596,21 +2640,28 @@ public void Base_05() public void RecordStructParsing_RecordNamedStruct() { var text = "record struct(int X, int Y);"; - UsingTree(text, options: TestOptions.Regular9, + + CreateCompilation(text, parseOptions: TestOptions.Regular9).VerifyDiagnostics( // (1,8): error CS8773: Feature 'record structs' is not available in C# 9.0. Please use language version 10.0 or greater. // record struct(int X, int Y); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "struct").WithArguments("record structs", "10.0").WithLocation(1, 8), // (1,14): error CS1001: Identifier expected // record struct(int X, int Y); - Diagnostic(ErrorCode.ERR_IdentifierExpected, "(").WithLocation(1, 14) - ); + Diagnostic(ErrorCode.ERR_IdentifierExpected, "(").WithLocation(1, 14)); + UsingTree(text, options: TestOptions.Regular9, + // (1,14): error CS1001: Identifier expected + // record struct(int X, int Y); + Diagnostic(ErrorCode.ERR_IdentifierExpected, "(").WithLocation(1, 14)); verify(); + CreateCompilation(text, parseOptions: TestOptions.Regular10).VerifyDiagnostics( + // (1,14): error CS1001: Identifier expected + // record struct(int X, int Y); + Diagnostic(ErrorCode.ERR_IdentifierExpected, "(").WithLocation(1, 14)); UsingTree(text, options: TestOptions.Regular10, // (1,14): error CS1001: Identifier expected // record struct(int X, int Y); - Diagnostic(ErrorCode.ERR_IdentifierExpected, "(").WithLocation(1, 14) - ); + Diagnostic(ErrorCode.ERR_IdentifierExpected, "(").WithLocation(1, 14)); verify(); void verify() @@ -2656,22 +2707,39 @@ void verify() public void RecordStructParsing() { var text = "record struct C(int X, int Y);"; - UsingTree(text, options: TestOptions.Regular10); + CreateCompilation(text, parseOptions: TestOptions.Regular10).VerifyDiagnostics(); + UsingTree(text, options: TestOptions.Regular10); verifyParsedAsRecord(); - UsingTree(text, options: TestOptions.Regular9, - // (1,8): error CS8773: Feature 'record structs' is not available in C# 9.0. Please use language version 10.0 or greater. - // record struct C(int X, int Y); - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "struct").WithArguments("record structs", "10.0").WithLocation(1, 8) - ); - + CreateCompilation(text, parseOptions: TestOptions.Regular10).VerifyDiagnostics(); + UsingTree(text, options: TestOptions.Regular9); verifyParsedAsRecord(); UsingTree(text, options: TestOptions.Regular8, + // (1,8): error CS1001: Identifier expected + // record struct C(int X, int Y); + Diagnostic(ErrorCode.ERR_IdentifierExpected, "struct").WithLocation(1, 8), + // (1,8): error CS1002: ; expected + // record struct C(int X, int Y); + Diagnostic(ErrorCode.ERR_SemicolonExpected, "struct").WithLocation(1, 8), + // (1,16): error CS1514: { expected + // record struct C(int X, int Y); + Diagnostic(ErrorCode.ERR_LbraceExpected, "(").WithLocation(1, 16), + // (1,16): error CS1513: } expected + // record struct C(int X, int Y); + Diagnostic(ErrorCode.ERR_RbraceExpected, "(").WithLocation(1, 16), + // (1,16): error CS8803: Top-level statements must precede namespace and type declarations. + // record struct C(int X, int Y); + Diagnostic(ErrorCode.ERR_TopLevelStatementAfterNamespaceOrType, "(int X, int Y);").WithLocation(1, 16)); + + CreateCompilation(text, parseOptions: TestOptions.Regular8).VerifyDiagnostics( // (1,1): error CS8400: Feature 'top-level statements' is not available in C# 8.0. Please use language version 9.0 or greater. // record struct C(int X, int Y); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "record ").WithArguments("top-level statements", "9.0").WithLocation(1, 1), + // (1,1): error CS0246: The type or namespace name 'record' could not be found (are you missing a using directive or an assembly reference?) + // record struct C(int X, int Y); + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "record").WithArguments("record").WithLocation(1, 1), // (1,8): error CS1001: Identifier expected // record struct C(int X, int Y); Diagnostic(ErrorCode.ERR_IdentifierExpected, "struct").WithLocation(1, 8), @@ -2686,8 +2754,22 @@ public void RecordStructParsing() Diagnostic(ErrorCode.ERR_RbraceExpected, "(").WithLocation(1, 16), // (1,16): error CS8803: Top-level statements must precede namespace and type declarations. // record struct C(int X, int Y); - Diagnostic(ErrorCode.ERR_TopLevelStatementAfterNamespaceOrType, "(int X, int Y);").WithLocation(1, 16) - ); + Diagnostic(ErrorCode.ERR_TopLevelStatementAfterNamespaceOrType, "(int X, int Y);").WithLocation(1, 16), + // (1,16): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // record struct C(int X, int Y); + Diagnostic(ErrorCode.ERR_IllegalStatement, "(int X, int Y)").WithLocation(1, 16), + // (1,17): error CS8185: A declaration is not allowed in this context. + // record struct C(int X, int Y); + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int X").WithLocation(1, 17), + // (1,17): error CS0165: Use of unassigned local variable 'X' + // record struct C(int X, int Y); + Diagnostic(ErrorCode.ERR_UseDefViolation, "int X").WithArguments("X").WithLocation(1, 17), + // (1,24): error CS8185: A declaration is not allowed in this context. + // record struct C(int X, int Y); + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int Y").WithLocation(1, 24), + // (1,24): error CS0165: Use of unassigned local variable 'Y' + // record struct C(int X, int Y); + Diagnostic(ErrorCode.ERR_UseDefViolation, "int Y").WithArguments("Y").WithLocation(1, 24)); N(SyntaxKind.CompilationUnit); { @@ -2847,22 +2929,37 @@ public void RecordStructParsing_WithBody() public void RecordClassParsing() { var text = "record class C(int X, int Y);"; - UsingTree(text, options: TestOptions.Regular10); + CreateCompilation(text, parseOptions: TestOptions.Regular10).VerifyDiagnostics( + // (1,20): error CS0518: Predefined type 'System.Runtime.CompilerServices.IsExternalInit' is not defined or imported + // record class C(int X, int Y); + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "X").WithArguments("System.Runtime.CompilerServices.IsExternalInit").WithLocation(1, 20), + // (1,27): error CS0518: Predefined type 'System.Runtime.CompilerServices.IsExternalInit' is not defined or imported + // record class C(int X, int Y); + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "Y").WithArguments("System.Runtime.CompilerServices.IsExternalInit").WithLocation(1, 27)); + UsingTree(text, options: TestOptions.Regular10); verifyParsedAsRecord(); - UsingTree(text, options: TestOptions.Regular9, + CreateCompilation(text, parseOptions: TestOptions.Regular9).VerifyDiagnostics( // (1,8): error CS8773: Feature 'record structs' is not available in C# 9.0. Please use language version 10.0 or greater. // record class C(int X, int Y); - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "class").WithArguments("record structs", "10.0").WithLocation(1, 8) - ); - + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "class").WithArguments("record structs", "10.0").WithLocation(1, 8), + // (1,20): error CS0518: Predefined type 'System.Runtime.CompilerServices.IsExternalInit' is not defined or imported + // record class C(int X, int Y); + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "X").WithArguments("System.Runtime.CompilerServices.IsExternalInit").WithLocation(1, 20), + // (1,27): error CS0518: Predefined type 'System.Runtime.CompilerServices.IsExternalInit' is not defined or imported + // record class C(int X, int Y); + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "Y").WithArguments("System.Runtime.CompilerServices.IsExternalInit").WithLocation(1, 27)); + UsingTree(text, options: TestOptions.Regular9); verifyParsedAsRecord(); - UsingTree(text, options: TestOptions.Regular8, + CreateCompilation(text, parseOptions: TestOptions.Regular8).VerifyDiagnostics( // (1,1): error CS8400: Feature 'top-level statements' is not available in C# 8.0. Please use language version 9.0 or greater. // record class C(int X, int Y); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "record ").WithArguments("top-level statements", "9.0").WithLocation(1, 1), + // (1,1): error CS0246: The type or namespace name 'record' could not be found (are you missing a using directive or an assembly reference?) + // record class C(int X, int Y); + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "record").WithArguments("record").WithLocation(1, 1), // (1,8): error CS1001: Identifier expected // record class C(int X, int Y); Diagnostic(ErrorCode.ERR_IdentifierExpected, "class").WithLocation(1, 8), @@ -2877,8 +2974,39 @@ public void RecordClassParsing() Diagnostic(ErrorCode.ERR_RbraceExpected, "(").WithLocation(1, 15), // (1,15): error CS8803: Top-level statements must precede namespace and type declarations. // record class C(int X, int Y); - Diagnostic(ErrorCode.ERR_TopLevelStatementAfterNamespaceOrType, "(int X, int Y);").WithLocation(1, 15) - ); + Diagnostic(ErrorCode.ERR_TopLevelStatementAfterNamespaceOrType, "(int X, int Y);").WithLocation(1, 15), + // (1,15): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // record class C(int X, int Y); + Diagnostic(ErrorCode.ERR_IllegalStatement, "(int X, int Y)").WithLocation(1, 15), + // (1,16): error CS8185: A declaration is not allowed in this context. + // record class C(int X, int Y); + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int X").WithLocation(1, 16), + // (1,16): error CS0165: Use of unassigned local variable 'X' + // record class C(int X, int Y); + Diagnostic(ErrorCode.ERR_UseDefViolation, "int X").WithArguments("X").WithLocation(1, 16), + // (1,23): error CS8185: A declaration is not allowed in this context. + // record class C(int X, int Y); + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int Y").WithLocation(1, 23), + // (1,23): error CS0165: Use of unassigned local variable 'Y' + // record class C(int X, int Y); + Diagnostic(ErrorCode.ERR_UseDefViolation, "int Y").WithArguments("Y").WithLocation(1, 23)); + + UsingTree(text, options: TestOptions.Regular8, + // (1,8): error CS1001: Identifier expected + // record class C(int X, int Y); + Diagnostic(ErrorCode.ERR_IdentifierExpected, "class").WithLocation(1, 8), + // (1,8): error CS1002: ; expected + // record class C(int X, int Y); + Diagnostic(ErrorCode.ERR_SemicolonExpected, "class").WithLocation(1, 8), + // (1,15): error CS1514: { expected + // record class C(int X, int Y); + Diagnostic(ErrorCode.ERR_LbraceExpected, "(").WithLocation(1, 15), + // (1,15): error CS1513: } expected + // record class C(int X, int Y); + Diagnostic(ErrorCode.ERR_RbraceExpected, "(").WithLocation(1, 15), + // (1,15): error CS8803: Top-level statements must precede namespace and type declarations. + // record class C(int X, int Y); + Diagnostic(ErrorCode.ERR_TopLevelStatementAfterNamespaceOrType, "(int X, int Y);").WithLocation(1, 15)); N(SyntaxKind.CompilationUnit); { @@ -3220,87 +3348,40 @@ public void RecordStructParsing_WrongOrder_CSharp9() { var text = "struct record C(int X, int Y);"; UsingTree(text, options: TestOptions.Regular9, - // (1,15): error CS1514: { expected - // struct record C(int X, int Y); - Diagnostic(ErrorCode.ERR_LbraceExpected, "C").WithLocation(1, 15), - // (1,15): error CS1513: } expected - // struct record C(int X, int Y); - Diagnostic(ErrorCode.ERR_RbraceExpected, "C").WithLocation(1, 15), - // (1,15): error CS8803: Top-level statements must precede namespace and type declarations. - // struct record C(int X, int Y); - Diagnostic(ErrorCode.ERR_TopLevelStatementAfterNamespaceOrType, "C(int X, int Y);").WithLocation(1, 15), - // (1,17): error CS1525: Invalid expression term 'int' - // struct record C(int X, int Y); - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(1, 17), - // (1,21): error CS1003: Syntax error, ',' expected - // struct record C(int X, int Y); - Diagnostic(ErrorCode.ERR_SyntaxError, "X").WithArguments(",").WithLocation(1, 21), - // (1,24): error CS1525: Invalid expression term 'int' - // struct record C(int X, int Y); - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(1, 24), - // (1,28): error CS1003: Syntax error, ',' expected + // (1,8): error CS9012: Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? // struct record C(int X, int Y); - Diagnostic(ErrorCode.ERR_SyntaxError, "Y").WithArguments(",").WithLocation(1, 28) - ); + Diagnostic(ErrorCode.ERR_MisplacedRecord, "record").WithLocation(1, 8)); N(SyntaxKind.CompilationUnit); { - N(SyntaxKind.StructDeclaration); - { - N(SyntaxKind.StructKeyword); - N(SyntaxKind.IdentifierToken, "record"); - M(SyntaxKind.OpenBraceToken); - M(SyntaxKind.CloseBraceToken); - } - N(SyntaxKind.GlobalStatement); + N(SyntaxKind.RecordStructDeclaration); { - N(SyntaxKind.ExpressionStatement); + N(SyntaxKind.RecordKeyword); + M(SyntaxKind.StructKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.ParameterList); { - N(SyntaxKind.InvocationExpression); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); { - N(SyntaxKind.IdentifierName); + N(SyntaxKind.PredefinedType); { - N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.IntKeyword); } - N(SyntaxKind.ArgumentList); + N(SyntaxKind.IdentifierToken, "X"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); { - N(SyntaxKind.OpenParenToken); - N(SyntaxKind.Argument); - { - N(SyntaxKind.PredefinedType); - { - N(SyntaxKind.IntKeyword); - } - } - M(SyntaxKind.CommaToken); - N(SyntaxKind.Argument); - { - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "X"); - } - } - N(SyntaxKind.CommaToken); - N(SyntaxKind.Argument); - { - N(SyntaxKind.PredefinedType); - { - N(SyntaxKind.IntKeyword); - } - } - M(SyntaxKind.CommaToken); - N(SyntaxKind.Argument); - { - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "Y"); - } - } - N(SyntaxKind.CloseParenToken); + N(SyntaxKind.IntKeyword); } + N(SyntaxKind.IdentifierToken, "Y"); } - N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); } + N(SyntaxKind.SemicolonToken); } N(SyntaxKind.EndOfFileToken); } @@ -3417,87 +3498,40 @@ public void RecordClassParsing_WrongOrder_CSharp9() { var text = "class record C(int X, int Y);"; UsingTree(text, options: TestOptions.Regular9, - // (1,14): error CS1514: { expected - // class record C(int X, int Y); - Diagnostic(ErrorCode.ERR_LbraceExpected, "C").WithLocation(1, 14), - // (1,14): error CS1513: } expected - // class record C(int X, int Y); - Diagnostic(ErrorCode.ERR_RbraceExpected, "C").WithLocation(1, 14), - // (1,14): error CS8803: Top-level statements must precede namespace and type declarations. - // class record C(int X, int Y); - Diagnostic(ErrorCode.ERR_TopLevelStatementAfterNamespaceOrType, "C(int X, int Y);").WithLocation(1, 14), - // (1,16): error CS1525: Invalid expression term 'int' - // class record C(int X, int Y); - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(1, 16), - // (1,20): error CS1003: Syntax error, ',' expected - // class record C(int X, int Y); - Diagnostic(ErrorCode.ERR_SyntaxError, "X").WithArguments(",").WithLocation(1, 20), - // (1,23): error CS1525: Invalid expression term 'int' - // class record C(int X, int Y); - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(1, 23), - // (1,27): error CS1003: Syntax error, ',' expected + // (1,7): error CS9012: Unexpected keyword 'record'. Did you mean 'record struct' or 'record class'? // class record C(int X, int Y); - Diagnostic(ErrorCode.ERR_SyntaxError, "Y").WithArguments(",").WithLocation(1, 27) - ); + Diagnostic(ErrorCode.ERR_MisplacedRecord, "record").WithLocation(1, 7)); N(SyntaxKind.CompilationUnit); { - N(SyntaxKind.ClassDeclaration); - { - N(SyntaxKind.ClassKeyword); - N(SyntaxKind.IdentifierToken, "record"); - M(SyntaxKind.OpenBraceToken); - M(SyntaxKind.CloseBraceToken); - } - N(SyntaxKind.GlobalStatement); + N(SyntaxKind.RecordDeclaration); { - N(SyntaxKind.ExpressionStatement); + N(SyntaxKind.RecordKeyword); + M(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.ParameterList); { - N(SyntaxKind.InvocationExpression); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); { - N(SyntaxKind.IdentifierName); + N(SyntaxKind.PredefinedType); { - N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.IntKeyword); } - N(SyntaxKind.ArgumentList); + N(SyntaxKind.IdentifierToken, "X"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); { - N(SyntaxKind.OpenParenToken); - N(SyntaxKind.Argument); - { - N(SyntaxKind.PredefinedType); - { - N(SyntaxKind.IntKeyword); - } - } - M(SyntaxKind.CommaToken); - N(SyntaxKind.Argument); - { - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "X"); - } - } - N(SyntaxKind.CommaToken); - N(SyntaxKind.Argument); - { - N(SyntaxKind.PredefinedType); - { - N(SyntaxKind.IntKeyword); - } - } - M(SyntaxKind.CommaToken); - N(SyntaxKind.Argument); - { - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "Y"); - } - } - N(SyntaxKind.CloseParenToken); + N(SyntaxKind.IntKeyword); } + N(SyntaxKind.IdentifierToken, "Y"); } - N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); } + N(SyntaxKind.SemicolonToken); } N(SyntaxKind.EndOfFileToken); } @@ -3860,10 +3894,24 @@ public void RecordStructParsing_New() public void RecordStructParsing_Ref() { var text = "ref record struct S;"; - UsingTree(text, options: TestOptions.RegularPreview); + CreateCompilation(text, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( + // (1,19): error CS0106: The modifier 'ref' is not valid for this item + // ref record struct S; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "S").WithArguments("ref").WithLocation(1, 19)); + UsingTree(text, options: TestOptions.RegularPreview); verifyParsedAsRecord(); + CreateCompilation(text, parseOptions: TestOptions.Regular8).VerifyDiagnostics( + // (1,5): error CS0116: A namespace cannot directly contain members such as fields, methods or statements + // ref record struct S; + Diagnostic(ErrorCode.ERR_NamespaceUnexpected, "record").WithLocation(1, 5), + // (1,20): error CS1514: { expected + // ref record struct S; + Diagnostic(ErrorCode.ERR_LbraceExpected, ";").WithLocation(1, 20), + // (1,20): error CS1513: } expected + // ref record struct S; + Diagnostic(ErrorCode.ERR_RbraceExpected, ";").WithLocation(1, 20)); UsingTree(text, options: TestOptions.Regular8, // (1,5): error CS0116: A namespace cannot directly contain members such as fields or methods // ref record struct S; @@ -3873,8 +3921,7 @@ public void RecordStructParsing_Ref() Diagnostic(ErrorCode.ERR_LbraceExpected, ";").WithLocation(1, 20), // (1,20): error CS1513: } expected // ref record struct S; - Diagnostic(ErrorCode.ERR_RbraceExpected, ";").WithLocation(1, 20) - ); + Diagnostic(ErrorCode.ERR_RbraceExpected, ";").WithLocation(1, 20)); N(SyntaxKind.CompilationUnit); { @@ -3901,12 +3948,14 @@ public void RecordStructParsing_Ref() } EOF(); - UsingTree(text, options: TestOptions.Regular9, + CreateCompilation(text, parseOptions: TestOptions.Regular9).VerifyDiagnostics( // (1,12): error CS8773: Feature 'record structs' is not available in C# 9.0. Please use language version 10.0 or greater. // ref record struct S; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "struct").WithArguments("record structs", "10.0").WithLocation(1, 12) - ); - + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "struct").WithArguments("record structs", "10.0").WithLocation(1, 12), + // (1,19): error CS0106: The modifier 'ref' is not valid for this item + // ref record struct S; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "S").WithArguments("ref").WithLocation(1, 19)); + UsingTree(text, options: TestOptions.Regular9); verifyParsedAsRecord(); void verifyParsedAsRecord() @@ -3935,10 +3984,21 @@ public void RecordParsing_Ref() verifyParsedAsRecord(); - UsingTree(text, options: TestOptions.Regular8, + UsingTree(text, options: TestOptions.Regular8); + + CreateCompilation(text, parseOptions: TestOptions.Regular8).VerifyDiagnostics( // (1,1): error CS8400: Feature 'top-level statements' is not available in C# 8.0. Please use language version 9.0 or greater. // ref record R; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "ref record R;").WithArguments("top-level statements", "9.0").WithLocation(1, 1)); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "ref record R;").WithArguments("top-level statements", "9.0").WithLocation(1, 1), + // (1,5): error CS0246: The type or namespace name 'record' could not be found (are you missing a using directive or an assembly reference?) + // ref record R; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "record").WithArguments("record").WithLocation(1, 5), + // (1,12): error CS8174: A declaration of a by-reference variable must have an initializer + // ref record R; + Diagnostic(ErrorCode.ERR_ByReferenceVariableMustBeInitialized, "R").WithLocation(1, 12), + // (1,12): warning CS0168: The variable 'R' is declared but never used + // ref record R; + Diagnostic(ErrorCode.WRN_UnreferencedVar, "R").WithArguments("R").WithLocation(1, 12)); N(SyntaxKind.CompilationUnit); { diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/TopLevelStatementsParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/TopLevelStatementsParsingTests.cs index 69588be306bfc..e7f4ad2b133ae 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/TopLevelStatementsParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/TopLevelStatementsParsingTests.cs @@ -3231,10 +3231,13 @@ public void ErrorRecovery_10() record class Point(int x, int y); "; - UsingTree(test, TestOptions.Regular8, + CreateCompilation(test, parseOptions: TestOptions.Regular8).VerifyDiagnostics( // (2,1): error CS8400: Feature 'top-level statements' is not available in C# 8.0. Please use language version 9.0 or greater. // record class Point(int x, int y); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "record ").WithArguments("top-level statements", "9.0").WithLocation(2, 1), + // (2,1): error CS0246: The type or namespace name 'record' could not be found (are you missing a using directive or an assembly reference?) + // record class Point(int x, int y); + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "record").WithArguments("record").WithLocation(2, 1), // (2,8): error CS1001: Identifier expected // record class Point(int x, int y); Diagnostic(ErrorCode.ERR_IdentifierExpected, "class").WithLocation(2, 8), @@ -3249,8 +3252,39 @@ record class Point(int x, int y); Diagnostic(ErrorCode.ERR_RbraceExpected, "(").WithLocation(2, 19), // (2,19): error CS8803: Top-level statements must precede namespace and type declarations. // record class Point(int x, int y); - Diagnostic(ErrorCode.ERR_TopLevelStatementAfterNamespaceOrType, "(int x, int y);").WithLocation(2, 19) - ); + Diagnostic(ErrorCode.ERR_TopLevelStatementAfterNamespaceOrType, "(int x, int y);").WithLocation(2, 19), + // (2,19): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // record class Point(int x, int y); + Diagnostic(ErrorCode.ERR_IllegalStatement, "(int x, int y)").WithLocation(2, 19), + // (2,20): error CS8185: A declaration is not allowed in this context. + // record class Point(int x, int y); + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int x").WithLocation(2, 20), + // (2,20): error CS0165: Use of unassigned local variable 'x' + // record class Point(int x, int y); + Diagnostic(ErrorCode.ERR_UseDefViolation, "int x").WithArguments("x").WithLocation(2, 20), + // (2,27): error CS8185: A declaration is not allowed in this context. + // record class Point(int x, int y); + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int y").WithLocation(2, 27), + // (2,27): error CS0165: Use of unassigned local variable 'y' + // record class Point(int x, int y); + Diagnostic(ErrorCode.ERR_UseDefViolation, "int y").WithArguments("y").WithLocation(2, 27)); + + UsingTree(test, TestOptions.Regular8, + // (2,8): error CS1001: Identifier expected + // record class Point(int x, int y); + Diagnostic(ErrorCode.ERR_IdentifierExpected, "class").WithLocation(2, 8), + // (2,8): error CS1002: ; expected + // record class Point(int x, int y); + Diagnostic(ErrorCode.ERR_SemicolonExpected, "class").WithLocation(2, 8), + // (2,19): error CS1514: { expected + // record class Point(int x, int y); + Diagnostic(ErrorCode.ERR_LbraceExpected, "(").WithLocation(2, 19), + // (2,19): error CS1513: } expected + // record class Point(int x, int y); + Diagnostic(ErrorCode.ERR_RbraceExpected, "(").WithLocation(2, 19), + // (2,19): error CS8803: Top-level statements must precede namespace and type declarations. + // record class Point(int x, int y); + Diagnostic(ErrorCode.ERR_TopLevelStatementAfterNamespaceOrType, "(int x, int y);").WithLocation(2, 19)); N(SyntaxKind.CompilationUnit); { diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerActionCounts.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerActionCounts.cs index 699c170642977..bcb30914f0c95 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerActionCounts.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerActionCounts.cs @@ -82,6 +82,13 @@ internal AnalyzerActionCounts( OperationBlockActionsCount > 0 || OperationBlockStartActionsCount > 0 || SymbolStartActionsCount > 0; + + // All executable code actions, symbol actions, semantic model actions and compilation end actions + // are driven by compilation event queue in the AnalyzerDriver. + HasAnyActionsRequiringCompilationEvents = HasAnyExecutableCodeActions || + SymbolActionsCount > 0 || + SemanticModelActionsCount > 0 || + CompilationEndActionsCount > 0; } /// @@ -174,6 +181,22 @@ internal AnalyzerActionCounts( /// public bool HasAnyExecutableCodeActions { get; } + /// + /// Returns true if there are any analyzer action callbacks that are driven by compilation events, + /// such as , , etc. + /// Many callbacks into the diagnostics analyzers are driven in the + /// by compilation events added to the . For these callbacks to be executed, + /// the analyzer driver host needs to force complete the events in the relevant part of the compilation, + /// i.e. relevant tree(s) or entire compilation. This force complete operation incurs a performance cost, + /// which can be avoided if the analyzer(s) to be executed, such as syntax-only analyzers, do not register any + /// actions which are driven by compilation events. + /// Note that is an exception as it is *always* generated as soon as the + /// is created. Any action callbacks driven off + /// do not need any force completion and hence do not need to be accounted by this boolean flag. + /// + /// This flag is primarily intended for performance improvements in certain analyzer execution code paths. + public bool HasAnyActionsRequiringCompilationEvents { get; } + /// /// Gets a value indicating whether the analyzer supports concurrent execution. /// diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs index b66adfe4d7122..1168a7f9dc9ae 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs @@ -632,7 +632,7 @@ internal async Task AttachQueueAndProcessAllEventsAsync(AsyncQueue /// Compilation events to analyze. /// Scope of analysis. + /// Boolean flag indicating whether we should only process the already populated events or wait for . /// Cancellation token to abort analysis. /// Driver must be initialized before invoking this method, i.e. method must have been invoked and must be non-null. - internal void AttachQueueAndStartProcessingEvents(AsyncQueue eventQueue, AnalysisScope analysisScope, CancellationToken cancellationToken) + internal void AttachQueueAndStartProcessingEvents(AsyncQueue eventQueue, AnalysisScope analysisScope, bool usingPrePopulatedEventQueue, CancellationToken cancellationToken) { try { @@ -667,7 +668,7 @@ internal void AttachQueueAndStartProcessingEvents(AsyncQueue e this.DiagnosticQueue.TryComplete(); }); - _lazyPrimaryTask = ExecutePrimaryAnalysisTaskAsync(analysisScope, analysisState: null, usingPrePopulatedEventQueue: false, cancellationToken: cancellationToken) + _lazyPrimaryTask = ExecutePrimaryAnalysisTaskAsync(analysisScope, analysisState: null, usingPrePopulatedEventQueue, cancellationToken) .ContinueWith(c => DiagnosticQueue.TryComplete(), cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); } } @@ -872,7 +873,7 @@ internal static AnalyzerDriver CreateAndAttachToCompilation( analyzerDriver.Initialize(newCompilation, analysisOptions, new CompilationData(newCompilation), categorizeDiagnostics, trackSuppressedDiagnosticIds, cancellationToken); var analysisScope = new AnalysisScope(newCompilation, options, analyzers, hasAllAnalyzers: true, concurrentAnalysis: newCompilation.Options.ConcurrentBuild, categorizeDiagnostics: categorizeDiagnostics); - analyzerDriver.AttachQueueAndStartProcessingEvents(newCompilation.EventQueue!, analysisScope, cancellationToken: cancellationToken); + analyzerDriver.AttachQueueAndStartProcessingEvents(newCompilation.EventQueue!, analysisScope, usingPrePopulatedEventQueue: false, cancellationToken); return analyzerDriver; } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.AnalyzerExecutionContext.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.AnalyzerExecutionContext.cs index 4c3def0582586..3ee605dd2acc4 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.AnalyzerExecutionContext.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.AnalyzerExecutionContext.cs @@ -237,34 +237,43 @@ public void ClearSymbolScopeTask(ISymbol symbol) } public ImmutableArray GetOrComputeDiagnosticDescriptors(DiagnosticAnalyzer analyzer, AnalyzerExecutor analyzerExecutor) - => GetOrComputeDescriptors(ref _lazyDiagnosticDescriptors, ComputeDiagnosticDescriptors, analyzer, analyzerExecutor); + => GetOrComputeDescriptors(ref _lazyDiagnosticDescriptors, ComputeDiagnosticDescriptors_NoLock, analyzer, analyzerExecutor, _gate); public ImmutableArray GetOrComputeSuppressionDescriptors(DiagnosticSuppressor suppressor, AnalyzerExecutor analyzerExecutor) - => GetOrComputeDescriptors(ref _lazySuppressionDescriptors, ComputeSuppressionDescriptors, suppressor, analyzerExecutor); + => GetOrComputeDescriptors(ref _lazySuppressionDescriptors, ComputeSuppressionDescriptors_NoLock, suppressor, analyzerExecutor, _gate); private static ImmutableArray GetOrComputeDescriptors( ref ImmutableArray lazyDescriptors, - Func> computeDescriptors, + Func> computeDescriptorsNoLock, DiagnosticAnalyzer analyzer, - AnalyzerExecutor analyzerExecutor) + AnalyzerExecutor analyzerExecutor, + object gate) { if (!lazyDescriptors.IsDefault) { return lazyDescriptors; } - // Otherwise, compute the value. - // We do so outside the lock statement as we are calling into user code, which may be a long running operation. - var descriptors = computeDescriptors(analyzer, analyzerExecutor); + lock (gate) + { + // We re-check if lazyDescriptors is default inside the lock statement + // to ensure that we don't invoke 'computeDescriptorsNoLock' multiple times. + // 'computeDescriptorsNoLock' makes analyzer callbacks and these can throw + // exceptions, leading to AD0001 diagnostics and duplicate callbacks can + // lead to duplicate AD0001 diagnostics. + if (lazyDescriptors.IsDefault) + { + lazyDescriptors = computeDescriptorsNoLock(analyzer, analyzerExecutor); + } - ImmutableInterlocked.InterlockedInitialize(ref lazyDescriptors, descriptors); - return lazyDescriptors; + return lazyDescriptors; + } } /// /// Compute and exception handler for the given . /// - private static ImmutableArray ComputeDiagnosticDescriptors( + private static ImmutableArray ComputeDiagnosticDescriptors_NoLock( DiagnosticAnalyzer analyzer, AnalyzerExecutor analyzerExecutor) { @@ -313,7 +322,7 @@ private static ImmutableArray ComputeDiagnosticDescriptors return supportedDiagnostics; } - private static ImmutableArray ComputeSuppressionDescriptors( + private static ImmutableArray ComputeSuppressionDescriptors_NoLock( DiagnosticAnalyzer analyzer, AnalyzerExecutor analyzerExecutor) { diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs index ba6b58dd25134..14bd1fd3a6af5 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs @@ -415,27 +415,40 @@ private async Task ComputeAnalyzerDiagnosticsWithoutStateTrackingAsync(Cancellat var compilation = _compilation.WithEventQueue(eventQueue); var compilationData = new CompilationData(compilation); - // Create and attach the driver to compilation. + // Create and initialize driver. var categorizeDiagnostics = true; driver = CreateDriverForComputingDiagnosticsWithoutStateTracking(compilation, analyzers); driver.Initialize(compilation, _analysisOptions, compilationData, categorizeDiagnostics, trackSuppressedDiagnosticIds: false, cancellationToken); - var hasAllAnalyzers = analyzers.Length == Analyzers.Length; - var analysisScope = new AnalysisScope(compilation, _analysisOptions.Options, analyzers, hasAllAnalyzers, concurrentAnalysis: _analysisOptions.ConcurrentAnalysis, categorizeDiagnostics: categorizeDiagnostics); - driver.AttachQueueAndStartProcessingEvents(compilation.EventQueue!, analysisScope, cancellationToken); - - // Force compilation diagnostics and wait for analyzer execution to complete. - var compDiags = compilation.GetDiagnostics(cancellationToken); - await driver.WhenCompletedTask.ConfigureAwait(false); // Get analyzer action counts. var analyzerActionCounts = new Dictionary(analyzers.Length); + var hasAnyActionsRequiringCompilationEvents = false; foreach (var analyzer in analyzers) { var actionCounts = await driver.GetAnalyzerActionCountsAsync(analyzer, compilation.Options, cancellationToken).ConfigureAwait(false); analyzerActionCounts.Add(analyzer, actionCounts); + + if (actionCounts.HasAnyActionsRequiringCompilationEvents) + hasAnyActionsRequiringCompilationEvents = true; } Func getAnalyzerActionCounts = analyzer => analyzerActionCounts[analyzer]; + // Attach the driver to compilation and start processing events. + // If we do not have any analyzer actions that are driven by compilation events, + // we are only going to enqueue a CompilationStartedEvent, so ensure we + // pass 'usingPrePopulatedEventQueue = true' for that case. + var hasAllAnalyzers = analyzers.Length == Analyzers.Length; + var analysisScope = new AnalysisScope(compilation, _analysisOptions.Options, analyzers, hasAllAnalyzers, concurrentAnalysis: _analysisOptions.ConcurrentAnalysis, categorizeDiagnostics: categorizeDiagnostics); + driver.AttachQueueAndStartProcessingEvents(compilation.EventQueue!, analysisScope, usingPrePopulatedEventQueue: !hasAnyActionsRequiringCompilationEvents, cancellationToken); + + // If we have any analyzer actions that are driven by compilation events, we need to + // force compilation diagnostics to populate the compilation event queue and force it to be completed. + if (hasAnyActionsRequiringCompilationEvents) + _ = compilation.GetDiagnostics(cancellationToken); + + // Wait for analyzer execution to complete. + await driver.WhenCompletedTask.ConfigureAwait(false); + _analysisResultBuilder.ApplySuppressionsAndStoreAnalysisResult(analysisScope, driver, compilation, getAnalyzerActionCounts, fullAnalysisResultForAnalyzersInScope: true); } finally @@ -462,7 +475,7 @@ private async Task> GetAllDiagnosticsWithoutStateTrac driver.Initialize(compilation, _analysisOptions, compilationData, categorizeDiagnostics, trackSuppressedDiagnosticIds: false, cancellationToken); var hasAllAnalyzers = analyzers.Length == Analyzers.Length; var analysisScope = new AnalysisScope(compilation, _analysisOptions.Options, analyzers, hasAllAnalyzers, concurrentAnalysis: _analysisOptions.ConcurrentAnalysis, categorizeDiagnostics: categorizeDiagnostics); - driver.AttachQueueAndStartProcessingEvents(compilation.EventQueue!, analysisScope, cancellationToken); + driver.AttachQueueAndStartProcessingEvents(compilation.EventQueue!, analysisScope, usingPrePopulatedEventQueue: false, cancellationToken); // Force compilation diagnostics and wait for analyzer execution to complete. var compDiags = compilation.GetDiagnostics(cancellationToken); diff --git a/src/Compilers/Test/Core/Traits/Traits.cs b/src/Compilers/Test/Core/Traits/Traits.cs index 05c1055c98995..cb0b43ce1b5d8 100644 --- a/src/Compilers/Test/Core/Traits/Traits.cs +++ b/src/Compilers/Test/Core/Traits/Traits.cs @@ -307,6 +307,7 @@ public static class Features public const string StringIndentation = nameof(StringIndentation); public const string SuggestionTags = nameof(SuggestionTags); public const string SyncNamespaces = nameof(SyncNamespaces); + public const string Tagging = nameof(Tagging); public const string TargetTypedCompletion = nameof(TargetTypedCompletion); public const string TextStructureNavigator = nameof(TextStructureNavigator); public const string TaskList = nameof(TaskList); diff --git a/src/Compilers/Test/Utilities/CSharp/TestOptions.cs b/src/Compilers/Test/Utilities/CSharp/TestOptions.cs index 73474e0457c32..e0d230df5c8ce 100644 --- a/src/Compilers/Test/Utilities/CSharp/TestOptions.cs +++ b/src/Compilers/Test/Utilities/CSharp/TestOptions.cs @@ -17,6 +17,7 @@ public static class TestOptions { public static readonly CSharpParseOptions Regular = new CSharpParseOptions(kind: SourceCodeKind.Regular, documentationMode: DocumentationMode.Parse); public static readonly CSharpParseOptions Script = Regular.WithKind(SourceCodeKind.Script); + public static readonly CSharpParseOptions Regular1 = Regular.WithLanguageVersion(LanguageVersion.CSharp1); public static readonly CSharpParseOptions Regular2 = Regular.WithLanguageVersion(LanguageVersion.CSharp2); public static readonly CSharpParseOptions Regular3 = Regular.WithLanguageVersion(LanguageVersion.CSharp3); public static readonly CSharpParseOptions Regular4 = Regular.WithLanguageVersion(LanguageVersion.CSharp4); diff --git a/src/EditorFeatures/CSharpTest/Classification/TotalClassifierTests.cs b/src/EditorFeatures/CSharpTest/Classification/TotalClassifierTests.cs index 62ecd91e8d45f..b31bfc10b4df4 100644 --- a/src/EditorFeatures/CSharpTest/Classification/TotalClassifierTests.cs +++ b/src/EditorFeatures/CSharpTest/Classification/TotalClassifierTests.cs @@ -2617,45 +2617,6 @@ static void staticLocalFunction() { }|] Punctuation.CloseCurly); } - [Theory] - [CombinatorialData] - public async Task TestScopedVar(TestHost testHost) - { - await TestAsync(""" - static void method(scoped in S s) - { - scoped var rs1 = s; - } - - file readonly ref struct S { } - """, testHost, - Keyword("static"), - Keyword("void"), - Method("method"), - Static("method"), - Punctuation.OpenParen, - Keyword("scoped"), - Keyword("in"), - Struct("S"), - Parameter("s"), - Punctuation.CloseParen, - Punctuation.OpenCurly, - Keyword("scoped"), - Keyword("var"), - Local("rs1"), - Operators.Equals, - Parameter("s"), - Punctuation.Semicolon, - Punctuation.CloseCurly, - Keyword("file"), - Keyword("readonly"), - Keyword("ref"), - Keyword("struct"), - Struct("S"), - Punctuation.OpenCurly, - Punctuation.CloseCurly); - } - [Theory] [CombinatorialData] public async Task Lambda_DefaultParameterValue(TestHost testHost) diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs index 236eac88b045a..11660b2e61cef 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion.Providers; using Microsoft.CodeAnalysis.CSharp; @@ -11965,67 +11964,11 @@ class C await VerifyAnyItemExistsAsync(source); } - [Fact] - public async Task AfterScopedInsideMethod() - { - var source = @" -class C -{ - void M() - { - scoped $$ - } -} - -ref struct MyRefStruct { } -"; - await VerifyItemExistsAsync(MakeMarkup(source), "MyRefStruct"); - } - - [Fact] - public async Task AfterScopedGlobalStatement_FollowedByType() - { - var source = @" -scoped $$ - -ref struct MyRefStruct { } -"; - await VerifyItemExistsAsync(MakeMarkup(source), "MyRefStruct"); - } - - [Fact] - public async Task AfterScopedGlobalStatement_NotFollowedByType() - { - var source = """ - using System; - - scoped $$ - """; - - await VerifyItemExistsAsync(MakeMarkup(source), "ReadOnlySpan", displayTextSuffix: "<>"); - } - - [Fact] - public async Task AfterScopedInParameter() - { - var source = @" -class C -{ - void M(scoped $$) - { - } -} - -ref struct MyRefStruct { } -"; - await VerifyItemExistsAsync(MakeMarkup(source), "MyRefStruct"); - } - private static string MakeMarkup(string source, string languageVersion = "Preview") { return $$""" - + {{source}} diff --git a/src/EditorFeatures/CSharpTest/InlineDiagnostics/InlineDiagnosticsTaggerProviderTests.cs b/src/EditorFeatures/CSharpTest/InlineDiagnostics/InlineDiagnosticsTaggerProviderTests.cs index 8f98c97ec343f..0b7921bae4d60 100644 --- a/src/EditorFeatures/CSharpTest/InlineDiagnostics/InlineDiagnosticsTaggerProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/InlineDiagnostics/InlineDiagnosticsTaggerProviderTests.cs @@ -21,7 +21,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.InlineDiagnostics { [UseExportProvider] - [Trait(Traits.Feature, Traits.Features.ErrorSquiggles)] + [Trait(Traits.Feature, Traits.Features.ErrorSquiggles), Trait(Traits.Feature, Traits.Features.Tagging)] public class InlineDiagnosticsTaggerProviderTests { [WpfFact] diff --git a/src/EditorFeatures/CSharpTest/ReassignedVariable/CSharpReassignedVariableTests.cs b/src/EditorFeatures/CSharpTest/ReassignedVariable/CSharpReassignedVariableTests.cs index 5bac067b28fb6..f7fe02f04df99 100644 --- a/src/EditorFeatures/CSharpTest/ReassignedVariable/CSharpReassignedVariableTests.cs +++ b/src/EditorFeatures/CSharpTest/ReassignedVariable/CSharpReassignedVariableTests.cs @@ -510,24 +510,6 @@ void M(ref int [|p|]) }"); } - [Fact] - public async Task AssignmentThroughScopedRefLocal() - { - await TestAsync( -@" -using System; -class C -{ - void M(ref int [|p|]) - { - scoped ref var [|local|] = ref [|p|]; - [|local|] = 0; - [|local|] = 1; - Console.WriteLine([|local|]); - } -}"); - } - [Fact] public async Task TestRefLocalReassignment() { @@ -671,23 +653,6 @@ void M() }"); } - [Fact] - public async Task TestScopedReadonlyRefLocalWithNoReassignment() - { - await TestAsync( -@" -using System; -class C -{ - void M() - { - int p = 0; - scoped ref readonly int refP = ref p; - Console.WriteLine(p); - } -}"); - } - [Fact] public async Task TestReadonlyRefLocalWithNoReassignment1() { @@ -705,23 +670,6 @@ void M() }"); } - [Fact] - public async Task TestScopedReadonlyRefLocalWithNoReassignment1() - { - await TestAsync( -@" -using System; -class C -{ - void M1() - { - int p = 0; - scoped ref readonly int refP = ref p!; - Console.WriteLine(p); - } -}"); - } - [Fact] public async Task TestPointerCausingPossibleReassignment() { diff --git a/src/EditorFeatures/CSharpTest/Squiggles/ErrorSquiggleProducerTests.cs b/src/EditorFeatures/CSharpTest/Squiggles/ErrorSquiggleProducerTests.cs index 4b1cad93c3b5b..39c5476bff5ff 100644 --- a/src/EditorFeatures/CSharpTest/Squiggles/ErrorSquiggleProducerTests.cs +++ b/src/EditorFeatures/CSharpTest/Squiggles/ErrorSquiggleProducerTests.cs @@ -33,7 +33,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Squiggles { [UseExportProvider] - [Trait(Traits.Feature, Traits.Features.ErrorSquiggles)] + [Trait(Traits.Feature, Traits.Features.ErrorSquiggles), Trait(Traits.Feature, Traits.Features.Tagging)] public class ErrorSquiggleProducerTests { [WpfFact] @@ -231,11 +231,13 @@ public async Task TestNoErrorsAfterDocumentRemoved() { using var workspace = TestWorkspace.CreateCSharp("class"); using var wrapper = new DiagnosticTaggerWrapper(workspace); - var tagger = wrapper.TaggerProvider.CreateTagger(workspace.Documents.First().GetTextBuffer()); + + var firstDocument = workspace.Documents.First(); + var tagger = wrapper.TaggerProvider.CreateTagger(firstDocument.GetTextBuffer()); using var disposable = tagger as IDisposable; await wrapper.WaitForTags(); - var snapshot = workspace.Documents.First().GetTextBuffer().CurrentSnapshot; + var snapshot = firstDocument.GetTextBuffer().CurrentSnapshot; var spans = tagger.GetTags(snapshot.GetSnapshotSpanCollection()).ToList(); // Initially, while the buffer is associated with a Document, we should get @@ -243,8 +245,8 @@ public async Task TestNoErrorsAfterDocumentRemoved() Assert.True(spans.Count > 0); // Now remove the document. - workspace.CloseDocument(workspace.Documents.First().Id); - workspace.OnDocumentRemoved(workspace.Documents.First().Id); + workspace.CloseDocument(firstDocument.Id); + workspace.OnDocumentRemoved(firstDocument.Id); await wrapper.WaitForTags(); spans = tagger.GetTags(snapshot.GetSnapshotSpanCollection()).ToList(); @@ -257,11 +259,13 @@ public async Task TestNoErrorsAfterProjectRemoved() { using var workspace = TestWorkspace.CreateCSharp("class"); using var wrapper = new DiagnosticTaggerWrapper(workspace); - var tagger = wrapper.TaggerProvider.CreateTagger(workspace.Documents.First().GetTextBuffer()); + + var firstDocument = workspace.Documents.First(); + var tagger = wrapper.TaggerProvider.CreateTagger(firstDocument.GetTextBuffer()); using var disposable = tagger as IDisposable; await wrapper.WaitForTags(); - var snapshot = workspace.Documents.First().GetTextBuffer().CurrentSnapshot; + var snapshot = firstDocument.GetTextBuffer().CurrentSnapshot; var spans = tagger.GetTags(snapshot.GetSnapshotSpanCollection()).ToList(); // Initially, while the buffer is associated with a Document, we should get @@ -269,8 +273,8 @@ public async Task TestNoErrorsAfterProjectRemoved() Assert.True(spans.Count > 0); // Now remove the project. - workspace.CloseDocument(workspace.Documents.First().Id); - workspace.OnDocumentRemoved(workspace.Documents.First().Id); + workspace.CloseDocument(firstDocument.Id); + workspace.OnDocumentRemoved(firstDocument.Id); workspace.OnProjectRemoved(workspace.Projects.First().Id); await wrapper.WaitForTags(); spans = tagger.GetTags(snapshot.GetSnapshotSpanCollection()).ToList(); @@ -306,7 +310,7 @@ class Test TestDiagnosticTagProducer.CreateDiagnosticData(document, new TextSpan(0, 0)), TestDiagnosticTagProducer.CreateDiagnosticData(document, new TextSpan(0, 1)))); - var spans = await TestDiagnosticTagProducer.GetErrorsFromUpdateSource(workspace, updateArgs); + var spans = await TestDiagnosticTagProducer.GetErrorsFromUpdateSource(workspace, updateArgs, DiagnosticKind.CompilerSyntax); Assert.Equal(2, spans.Count()); var first = spans.First(); @@ -339,7 +343,7 @@ class Test TestDiagnosticTagProducer.CreateDiagnosticData(document, new TextSpan(0, 0)), TestDiagnosticTagProducer.CreateDiagnosticData(document, new TextSpan(0, 1)))); - var spans = await TestDiagnosticTagProducer.GetErrorsFromUpdateSource(workspace, updateArgs); + var spans = await TestDiagnosticTagProducer.GetErrorsFromUpdateSource(workspace, updateArgs, DiagnosticKind.CompilerSyntax); Assert.Equal(2, spans.Count()); var first = spans.First(); diff --git a/src/EditorFeatures/CSharpTest/SuggestionTags/SuggestionTagProducerTests.cs b/src/EditorFeatures/CSharpTest/SuggestionTags/SuggestionTagProducerTests.cs index eca4d752ccc66..09398040f7b44 100644 --- a/src/EditorFeatures/CSharpTest/SuggestionTags/SuggestionTagProducerTests.cs +++ b/src/EditorFeatures/CSharpTest/SuggestionTags/SuggestionTagProducerTests.cs @@ -22,9 +22,10 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.SuggestionTags { [UseExportProvider] + [Trait(Traits.Feature, Traits.Features.SuggestionTags), Trait(Traits.Feature, Traits.Features.Tagging)] public class SuggestionTagProducerTests { - [WpfFact, Trait(Traits.Feature, Traits.Features.SuggestionTags)] + [WpfFact] public async Task SuggestionTagTest1() { var (spans, selection) = await GetTagSpansAndSelectionAsync( diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/GlobalKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/GlobalKeywordRecommenderTests.cs index f3c046bc6a1e8..e7f2b6d073a5e 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/GlobalKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/GlobalKeywordRecommenderTests.cs @@ -349,12 +349,5 @@ await VerifyKeywordAsync( @"$$ [assembly: Call()]"); } - - [Fact] - public async Task TestAfterScoped() - { - await VerifyKeywordAsync("scoped $$"); - await VerifyKeywordAsync(AddInsideMethod("scoped $$")); - } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/InKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/InKeywordRecommenderTests.cs index f0337f6b80b3b..0115856792b42 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/InKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/InKeywordRecommenderTests.cs @@ -9,17 +9,16 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { - [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public class InKeywordRecommenderTests : KeywordRecommenderTests { - [Fact] + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestNotAtRoot_Interactive() { await VerifyAbsenceAsync(SourceCodeKind.Script, @"$$"); } - [Fact] + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestNotAfterClass_Interactive() { await VerifyAbsenceAsync(SourceCodeKind.Script, @@ -27,7 +26,7 @@ await VerifyAbsenceAsync(SourceCodeKind.Script, $$"); } - [Fact] + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestNotAfterGlobalStatement_Interactive() { await VerifyAbsenceAsync(SourceCodeKind.Script, @@ -35,7 +34,7 @@ await VerifyAbsenceAsync(SourceCodeKind.Script, $$"); } - [Fact] + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestNotAfterGlobalVariableDeclaration_Interactive() { await VerifyAbsenceAsync(SourceCodeKind.Script, @@ -43,49 +42,49 @@ await VerifyAbsenceAsync(SourceCodeKind.Script, $$"); } - [Fact] + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestNotInUsingAlias() { await VerifyAbsenceAsync( @"using Goo = $$"); } - [Fact] + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestNotInGlobalUsingAlias() { await VerifyAbsenceAsync( @"global using Goo = $$"); } - [Fact] + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestNotInEmptyStatement() { await VerifyAbsenceAsync(AddInsideMethod( @"$$")); } - [Fact] + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestNotAfterFrom() { await VerifyAbsenceAsync(AddInsideMethod( @"var q = from $$")); } - [Fact] + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestAfterFromIdentifier() { await VerifyKeywordAsync(AddInsideMethod( @"var q = from x $$")); } - [Fact] + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestAfterFromAndTypeAndIdentifier() { await VerifyKeywordAsync(AddInsideMethod( @"var q = from int x $$")); } - [Fact] + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestNotAfterJoin() { await VerifyAbsenceAsync(AddInsideMethod( @@ -93,7 +92,7 @@ await VerifyAbsenceAsync(AddInsideMethod( join $$")); } - [Fact] + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestAfterJoinIdentifier() { await VerifyKeywordAsync(AddInsideMethod( @@ -101,7 +100,7 @@ await VerifyKeywordAsync(AddInsideMethod( join z $$")); } - [Fact] + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestAfterJoinAndTypeAndIdentifier() { await VerifyKeywordAsync(AddInsideMethod( @@ -109,7 +108,7 @@ await VerifyKeywordAsync(AddInsideMethod( join int z $$")); } - [Fact] + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestAfterJoinNotAfterIn() { await VerifyAbsenceAsync(AddInsideMethod( @@ -117,7 +116,7 @@ await VerifyAbsenceAsync(AddInsideMethod( join z in $$")); } - [Fact] + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] [WorkItem(544158, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544158")] public async Task TestNotAfterJoinPredefinedType() { @@ -131,7 +130,7 @@ void M() join int $$"); } - [Fact] + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] [WorkItem(544158, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544158")] public async Task TestNotAfterJoinType() { @@ -145,126 +144,126 @@ void M() join Int32 $$"); } - [Fact] + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestInForEach() { await VerifyKeywordAsync(AddInsideMethod( @"foreach (var v $$")); } - [Fact] + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestInForEach1() { await VerifyKeywordAsync(AddInsideMethod( @"foreach (var v $$ c")); } - [Fact] + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestInForEach2() { await VerifyKeywordAsync(AddInsideMethod( @"foreach (var v $$ c")); } - [Fact] + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestNotInForEach() { await VerifyAbsenceAsync(AddInsideMethod( @"foreach ($$")); } - [Fact] + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestNotInForEach1() { await VerifyAbsenceAsync(AddInsideMethod( @"foreach (var $$")); } - [Fact] + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestNotInForEach2() { await VerifyAbsenceAsync(AddInsideMethod( @"foreach (var v in $$")); } - [Fact] + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestNotInForEach3() { await VerifyAbsenceAsync(AddInsideMethod( @"foreach (var v in c $$")); } - [Fact] + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestInterfaceTypeVarianceAfterAngle() { await VerifyKeywordAsync( @"interface IGoo<$$"); } - [Fact] + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestInterfaceTypeVarianceNotAfterIn() { await VerifyAbsenceAsync( @"interface IGoo "scoped"; - - private readonly ScopedKeywordRecommender _recommender = new(); - - public ScopedKeywordRecommenderTests() - { - this.RecommendKeywordsAsync = (position, context) => Task.FromResult(_recommender.RecommendKeywords(position, context, CancellationToken.None)); - } - - [Fact] - public async Task TestAtRoot_Interactive() - { - await VerifyKeywordAsync(SourceCodeKind.Script, -@"$$"); - } - - [Fact] - public async Task TestAfterClass_Interactive() - { - await VerifyKeywordAsync(SourceCodeKind.Script, -@"class C { } -$$"); - } - - [Fact] - public async Task TestAfterGlobalStatement_Interactive() - { - await VerifyKeywordAsync(SourceCodeKind.Script, -@"System.Console.WriteLine(); -$$"); - } - - [Fact] - public async Task TestAfterGlobalVariableDeclaration_Interactive() - { - await VerifyKeywordAsync(SourceCodeKind.Script, -@"int i = 0; -$$"); - } - - [Fact] - public async Task TestNotInUsingAlias() - { - await VerifyAbsenceAsync( -@"using Goo = $$"); - } - - [Fact] - public async Task TestNotInGlobalUsingAlias() - { - await VerifyAbsenceAsync( -@"global using Goo = $$"); - } - - [Fact] - public async Task TestNotAfterStackAlloc() - { - await VerifyAbsenceAsync( -@"class C { - int* goo = stackalloc $$"); - } - - [Fact] - public async Task TestNotInFixedStatement() - { - await VerifyAbsenceAsync(AddInsideMethod( -@"fixed ($$")); - } - - [Fact] - public async Task TestNotInDelegateReturnType() - { - await VerifyAbsenceAsync( -@"public delegate $$"); - } - - [Fact] - public async Task TestPossibleLambda() - { - // Could be `var x = ((scoped ref int x) => x);` - await VerifyKeywordAsync(AddInsideMethod( -@"var x = (($$")); - } - - [Fact] - public async Task TestEmptyStatement() - { - await VerifyKeywordAsync(AddInsideMethod( -@"$$")); - } - - [Fact] - public async Task TestBeforeStatement() - { - await VerifyKeywordAsync(AddInsideMethod( -@"$$ -return true;")); - } - - [Fact] - public async Task TestAfterStatement() - { - await VerifyKeywordAsync(AddInsideMethod( -@"return true; -$$")); - } - - [Fact] - public async Task TestNotInClass() - { - await VerifyAbsenceAsync(@"class C -{ - $$ -}"); - } - - [Fact] - public async Task TestInFor() - { - await VerifyKeywordAsync(AddInsideMethod( -@"for ($$")); - } - - [Fact] - public async Task TestInFor2() - { - await VerifyKeywordAsync(AddInsideMethod( -@"for ($$;")); - } - - [Fact] - public async Task TestInFor3() - { - await VerifyKeywordAsync(AddInsideMethod( -@"for ($$;;")); - } - - [Fact] - public async Task TestNotInFor() - { - await VerifyAbsenceAsync(AddInsideMethod( -@"for (var $$")); - } - - [Fact] - public async Task TestNotAfterVar() - { - await VerifyAbsenceAsync(AddInsideMethod( -@"var $$")); - } - - [Fact] - public async Task TestInForEach() - { - await VerifyKeywordAsync(AddInsideMethod( -@"foreach ($$")); - } - - [Fact] - public async Task TestNotInForEach() - { - await VerifyAbsenceAsync(AddInsideMethod( -@"foreach (var $$")); - } - - [Fact] - public async Task TestInAwaitForEach() - { - await VerifyKeywordAsync(AddInsideMethod( -@"await foreach ($$")); - } - - [Fact] - public async Task TestNotInAwaitForEach() - { - await VerifyAbsenceAsync(AddInsideMethod( -@"await foreach (var $$")); - } - - [Fact] - public async Task TestNotInForEachRefLoop0() - { - await VerifyAbsenceAsync(AddInsideMethod( -@"foreach (ref $$")); - } - - [Fact] - public async Task TestNotInForEachRefLoop1() - { - await VerifyAbsenceAsync(AddInsideMethod( -@"foreach (ref $$ x")); - } - - [Fact] - public async Task TestNotInForEachRefLoop2() - { - await VerifyAbsenceAsync(AddInsideMethod( -@"foreach (ref s$$ x")); - } - - [Fact] - public async Task TestNotInForEachRefReadonlyLoop0() - { - await VerifyAbsenceAsync(AddInsideMethod( -@"foreach (ref readonly $$ x")); - } - - [Fact] - public async Task TestNotInForRefLoop0() - { - await VerifyAbsenceAsync(AddInsideMethod( -@"for (ref $$")); - } - - [Fact] - public async Task TestNotInForRefLoop1() - { - await VerifyAbsenceAsync(AddInsideMethod( -@"for (ref s$$")); - } - - [Fact] - public async Task TestNotInForRefReadonlyLoop0() - { - await VerifyAbsenceAsync(AddInsideMethod( -@"for (ref readonly $$")); - } - - [Fact] - public async Task TestNotInForRefReadonlyLoop1() - { - await VerifyAbsenceAsync(AddInsideMethod( -@"for (ref readonly s$$")); - } - - [Fact] - public async Task TestNotInUsing() - { - await VerifyAbsenceAsync(AddInsideMethod( -@"using ($$")); - } - - [Fact] - public async Task TestNotInUsing2() - { - await VerifyAbsenceAsync(AddInsideMethod( -@"using (var $$")); - } - - [Fact] - public async Task TestNotInAwaitUsing() - { - await VerifyAbsenceAsync(AddInsideMethod( -@"await using ($$")); - } - - [Fact] - public async Task TestNotInAwaitUsing2() - { - await VerifyAbsenceAsync(AddInsideMethod( -@"await using (var $$")); - } - - [Fact] - public async Task TestNotAfterConstLocal() - { - await VerifyAbsenceAsync(AddInsideMethod( -@"const $$")); - } - - [Fact] - public async Task TestNotAfterConstField() - { - await VerifyAbsenceAsync( -@"class C { - const $$"); - } - - [Fact] - public async Task TestAfterOutKeywordInArgument() - { - await VerifyKeywordAsync(AddInsideMethod( -@"M(out $$")); - } - - [Fact] - public async Task TestNotAfterRefInMemberContext() - { - await VerifyAbsenceAsync( -@"class C { - ref $$"); - } - - [Fact] - public async Task TestNotAfterRefReadonlyInMemberContext() - { - await VerifyAbsenceAsync( -@"class C { - ref readonly $$"); - } - - [Fact] - public async Task TestNotAfterRefInStatementContext() - { - await VerifyAbsenceAsync(AddInsideMethod( -@"ref $$")); - } - - [Fact] - public async Task TestNotAfterRefReadonlyInStatementContext() - { - await VerifyAbsenceAsync(AddInsideMethod( -@"ref readonly $$")); - } - - [Fact] - public async Task TestNotAfterRefLocalDeclaration() - { - await VerifyAbsenceAsync(AddInsideMethod( -@"ref $$ int local;")); - } - - [Fact] - public async Task TestNotAfterRefReadonlyLocalDeclaration() - { - await VerifyAbsenceAsync(AddInsideMethod( -@"ref readonly $$ int local;")); - } - - [Fact] - public async Task TestNotAfterRefExpression() - { - await VerifyAbsenceAsync(AddInsideMethod( -@"ref int x = ref $$")); - } - - [Fact] - public async Task TestInParameter1() - { - await VerifyKeywordAsync(""" - class C - { - public void M($$) - } - """); - } - - [Fact] - public async Task TestInParameter2() - { - await VerifyKeywordAsync(""" - class C - { - public void M($$ ref) - } - """); - } - - [Fact] - public async Task TestInParameter3() - { - await VerifyKeywordAsync(""" - class C - { - public void M($$ ref int i) - } - """); - } - - [Fact] - public async Task TestInParameter4() - { - await VerifyKeywordAsync(""" - class C - { - public void M($$ ref int i) - } - """); - } - - [Fact] - public async Task TestInOperatorParameter() - { - await VerifyKeywordAsync(""" - class C - { - public static C operator +($$ in C c) - } - """); - } - - [Fact] - public async Task TestInAnonymousMethodParameter() - { - await VerifyKeywordAsync(""" - class C - { - void M() - { - var x = delegate ($$) { }; - } - } - """); - } - - [Fact] - public async Task TestInParameterAfterThisScoped() - { - await VerifyKeywordAsync(""" - static class C - { - static void M(this $$) - } - """); - } - } -} diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/VarKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/VarKeywordRecommenderTests.cs index cbb86f9066ffb..9db736fd230b7 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/VarKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/VarKeywordRecommenderTests.cs @@ -450,12 +450,5 @@ public async Task TestInMixedDeclarationAndAssignmentInDeconstruction() await VerifyKeywordAsync(AddInsideMethod( @"(x, $$) = (0, 0);")); } - - [Fact] - public async Task TestAfterScoped() - { - await VerifyKeywordAsync(AddInsideMethod("scoped $$")); - await VerifyKeywordAsync("scoped $$"); - } } } diff --git a/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTaggerProvider.cs b/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTaggerProvider.cs index 2a7d25f548086..9b8d2722e749d 100644 --- a/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTaggerProvider.cs +++ b/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTaggerProvider.cs @@ -29,7 +29,7 @@ namespace Microsoft.CodeAnalysis.Editor.InlineDiagnostics [Export(typeof(ITaggerProvider))] [ContentType(ContentTypeNames.RoslynContentType)] [TagType(typeof(InlineDiagnosticsTag))] - internal class InlineDiagnosticsTaggerProvider : AbstractDiagnosticsAdornmentTaggerProvider + internal sealed class InlineDiagnosticsTaggerProvider : AbstractDiagnosticsAdornmentTaggerProvider { private readonly IEditorFormatMap _editorFormatMap; private readonly IClassificationFormatMapService _classificationFormatMapService; @@ -57,13 +57,13 @@ public InlineDiagnosticsTaggerProvider( _classificationTypeRegistryService = classificationTypeRegistryService; } - protected internal override bool SupportsDiagnosticMode(DiagnosticMode mode) + protected sealed override bool SupportsDiagnosticMode(DiagnosticMode mode) { // We support inline diagnostics in both push and pull (since lsp doesn't support inline diagnostics yet). return true; } - protected internal override bool IncludeDiagnostic(DiagnosticData diagnostic) + protected sealed override bool IncludeDiagnostic(DiagnosticData diagnostic) { return diagnostic.Severity is DiagnosticSeverity.Warning or DiagnosticSeverity.Error && @@ -117,16 +117,16 @@ diagnostic.Severity is DiagnosticSeverity.Warning or DiagnosticSeverity.Error && /// /// TODO: is there anything we can do better here? Inline diagnostic tags are not really data, but more UI - /// elements with specific constrols, positions and events attached to them. There doesn't seem to be a safe - /// way to reuse any of these currently. Ideally we could do something similar to inline-hints where there's a - /// data tagger portion (which is async and has clean equality semantics), and then the UI portion which just + /// elements with specific controls, positions and events attached to them. There doesn't seem to be a safe way + /// to reuse any of these currently. Ideally we could do something similar to inline-hints where there's a data + /// tagger portion (which is async and has clean equality semantics), and then the UI portion which just /// translates those data-tags to the UI tags. /// /// Doing direct equality means we'll always end up regenerating all tags. But hopefully there won't be that /// many in a document to matter. /// /// - protected override bool TagEquals(InlineDiagnosticsTag tag1, InlineDiagnosticsTag tag2) + protected sealed override bool TagEquals(InlineDiagnosticsTag tag1, InlineDiagnosticsTag tag2) => tag1 == tag2; } } diff --git a/src/EditorFeatures/Core/Diagnostics/AbstractAggregateDiagnosticsTaggerProvider.AggregateTagger.cs b/src/EditorFeatures/Core/Diagnostics/AbstractAggregateDiagnosticsTaggerProvider.AggregateTagger.cs new file mode 100644 index 0000000000000..610eae4e7ac49 --- /dev/null +++ b/src/EditorFeatures/Core/Diagnostics/AbstractAggregateDiagnosticsTaggerProvider.AggregateTagger.cs @@ -0,0 +1,58 @@ +// 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 Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Tagging; + +namespace Microsoft.CodeAnalysis.Diagnostics; + +internal abstract partial class AbstractAggregateDiagnosticsTaggerProvider where TTag : ITag +{ + /// + /// Simple tagger that aggregates the underlying syntax/semantic compiler/analyzer taggers and presents them as + /// a single event source and source of tags. + /// + private sealed class AggregateTagger : ITagger, IDisposable + { + private readonly ImmutableArray> _taggers; + + public AggregateTagger(ImmutableArray> taggers) + => _taggers = taggers; + + public void Dispose() + { + foreach (var tagger in _taggers) + (tagger as IDisposable)?.Dispose(); + } + + public event EventHandler TagsChanged + { + add + { + foreach (var tagger in _taggers) + tagger.TagsChanged += value; + } + + remove + { + foreach (var tagger in _taggers) + tagger.TagsChanged -= value; + } + } + + public IEnumerable> GetTags(NormalizedSnapshotSpanCollection spans) + { + using var _ = ArrayBuilder>.GetInstance(out var result); + + foreach (var tagger in _taggers) + result.AddRange(tagger.GetTags(spans)); + + return result.ToImmutable(); + } + } +} diff --git a/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsTaggerProvider.cs b/src/EditorFeatures/Core/Diagnostics/AbstractAggregateDiagnosticsTaggerProvider.SingleDiagnosticKindTaggerProvider.cs similarity index 69% rename from src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsTaggerProvider.cs rename to src/EditorFeatures/Core/Diagnostics/AbstractAggregateDiagnosticsTaggerProvider.SingleDiagnosticKindTaggerProvider.cs index fa17c1c8de37f..a212af18d152f 100644 --- a/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsTaggerProvider.cs +++ b/src/EditorFeatures/Core/Diagnostics/AbstractAggregateDiagnosticsTaggerProvider.SingleDiagnosticKindTaggerProvider.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor; +using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Preview; using Microsoft.CodeAnalysis.Editor.Shared.Tagging; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; @@ -22,21 +23,31 @@ using Microsoft.CodeAnalysis.Workspaces; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; -using Microsoft.VisualStudio.Text.Tagging; +using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Diagnostics +namespace Microsoft.CodeAnalysis.Diagnostics; + +internal partial class AbstractAggregateDiagnosticsTaggerProvider { /// - /// Base type for all taggers that interact with the and produce tags for - /// the diagnostics with different UI presentations. + /// Low level tagger responsible for producing specific diagnostics tags for some feature for some particular . It is itself never exported directly, but it it is used by the which aggregates its results and the results for all the + /// other to produce all the diagnostics for that feature. /// - internal abstract partial class AbstractDiagnosticsTaggerProvider : AsynchronousTaggerProvider - where TTag : ITag + private sealed class SingleDiagnosticKindTaggerProvider : AsynchronousTaggerProvider { + private readonly DiagnosticKind _diagnosticKind; private readonly IDiagnosticService _diagnosticService; private readonly IDiagnosticAnalyzerService _analyzerService; - protected AbstractDiagnosticsTaggerProvider( + private readonly AbstractAggregateDiagnosticsTaggerProvider _callback; + + protected override ImmutableArray Options => _callback.Options; + + public SingleDiagnosticKindTaggerProvider( + AbstractAggregateDiagnosticsTaggerProvider callback, + DiagnosticKind diagnosticKind, IThreadingContext threadingContext, IDiagnosticService diagnosticService, IDiagnosticAnalyzerService analyzerService, @@ -45,17 +56,22 @@ protected AbstractDiagnosticsTaggerProvider( IAsynchronousOperationListener listener) : base(threadingContext, globalOptions, visibilityTracker, listener) { + _callback = callback; + _diagnosticKind = diagnosticKind; _diagnosticService = diagnosticService; _analyzerService = analyzerService; } - protected internal abstract bool IsEnabled { get; } - protected internal abstract bool SupportsDiagnosticMode(DiagnosticMode mode); - protected internal abstract bool IncludeDiagnostic(DiagnosticData data); - protected internal abstract ITagSpan? CreateTagSpan(Workspace workspace, SnapshotSpan span, DiagnosticData data); + protected sealed override TaggerDelay EventChangeDelay => TaggerDelay.Short; + protected sealed override TaggerDelay AddedTagNotificationDelay => TaggerDelay.OnIdle; - protected override TaggerDelay EventChangeDelay => TaggerDelay.Short; - protected override TaggerDelay AddedTagNotificationDelay => TaggerDelay.OnIdle; + /// + /// When we hear about a new event cancel the costly work we're doing and compute against the latest snapshot. + /// + protected sealed override bool CancelOnNewWork => true; + + protected sealed override bool TagEquals(TTag tag1, TTag tag2) + => _callback.TagEquals(tag1, tag2); protected sealed override ITaggerEventSource CreateEventSource(ITextView? textView, ITextBuffer subjectBuffer) { @@ -70,36 +86,27 @@ protected sealed override ITaggerEventSource CreateEventSource(ITextView? textVi TaggerEventSources.OnTextChanged(subjectBuffer)); } - /// - /// Get the that should have the tag applied to it. - /// In most cases, this is the but overrides can change it (e.g. unnecessary classifications). - /// - /// the diagnostic containing the location(s). - /// an array of locations that should have the tag applied. - protected internal virtual ImmutableArray GetLocationsToTag(DiagnosticData diagnosticData) - => diagnosticData.DataLocation is not null ? ImmutableArray.Create(diagnosticData.DataLocation) : ImmutableArray.Empty; - - protected override Task ProduceTagsAsync( + protected sealed override Task ProduceTagsAsync( TaggerContext context, DocumentSnapshotSpan spanToTag, int? caretPosition, CancellationToken cancellationToken) { return ProduceTagsAsync(context, spanToTag, cancellationToken); } private async Task ProduceTagsAsync( - TaggerContext context, DocumentSnapshotSpan spanToTag, CancellationToken cancellationToken) + TaggerContext context, DocumentSnapshotSpan documentSpanToTag, CancellationToken cancellationToken) { - if (!this.IsEnabled) + if (!_callback.IsEnabled) return; var diagnosticMode = GlobalOptions.GetDiagnosticMode(InternalDiagnosticsOptions.NormalDiagnosticMode); - if (!SupportsDiagnosticMode(diagnosticMode)) + if (!_callback.SupportsDiagnosticMode(diagnosticMode)) return; - var document = spanToTag.Document; + var document = documentSpanToTag.Document; if (document == null) return; - var snapshot = spanToTag.SnapshotSpan.Snapshot; + var snapshot = documentSpanToTag.SnapshotSpan.Snapshot; var workspace = document.Project.Solution.Workspace; @@ -115,13 +122,16 @@ private async Task ProduceTagsAsync( try { var diagnostics = await _analyzerService.GetDiagnosticsForSpanAsync( - document, range: null, cancellationToken: cancellationToken).ConfigureAwait(false); + document, + documentSpanToTag.SnapshotSpan.Span.ToTextSpan(), + diagnosticKind: _diagnosticKind, + cancellationToken: cancellationToken).ConfigureAwait(false); - var requestedSpan = spanToTag.SnapshotSpan; + var requestedSpan = documentSpanToTag.SnapshotSpan; foreach (var diagnosticData in diagnostics) { - if (this.IncludeDiagnostic(diagnosticData)) + if (_callback.IncludeDiagnostic(diagnosticData)) { // We're going to be retrieving the diagnostics against the last time the engine // computed them against this document *id*. That might have been a different @@ -134,13 +144,13 @@ private async Task ProduceTagsAsync( // So we'll eventually reach a point where the diagnostics exactly match the // editorSnapshot. - var diagnosticSpans = this.GetLocationsToTag(diagnosticData) + var diagnosticSpans = _callback.GetLocationsToTag(diagnosticData) .Select(loc => loc.UnmappedFileSpan.GetClampedTextSpan(sourceText).ToSnapshotSpan(snapshot)); foreach (var diagnosticSpan in diagnosticSpans) { if (diagnosticSpan.IntersectsWith(requestedSpan) && !IsSuppressed(suppressedDiagnosticsSpans, diagnosticSpan)) { - var tagSpan = this.CreateTagSpan(workspace, diagnosticSpan, diagnosticData); + var tagSpan = _callback.CreateTagSpan(workspace, diagnosticSpan, diagnosticData); if (tagSpan != null) context.AddTag(tagSpan); } diff --git a/src/EditorFeatures/Core/Diagnostics/AbstractAggregateDiagnosticsTaggerProvider.cs b/src/EditorFeatures/Core/Diagnostics/AbstractAggregateDiagnosticsTaggerProvider.cs new file mode 100644 index 0000000000000..a372771dcc79e --- /dev/null +++ b/src/EditorFeatures/Core/Diagnostics/AbstractAggregateDiagnosticsTaggerProvider.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 System.Collections.Immutable; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Workspaces; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Tagging; + +namespace Microsoft.CodeAnalysis.Diagnostics; + +/// +/// Base type for all taggers that interact with the and produce tags for the +/// diagnostics with different UI presentations. It does no computation work itself, but instead defers that to it's +/// underlying s. +/// +internal abstract partial class AbstractAggregateDiagnosticsTaggerProvider : ITaggerProvider + where TTag : ITag +{ + /// + /// Underlying diagnostic tagger responsible for the syntax/semantic and compiler/analyzer split. The ordering of + /// these taggers is not relevant. They are not executed serially. Rather, they all run concurrently, notifying us + /// (potentially concurrently as well) when change occur. + /// + private readonly ImmutableArray _diagnosticsTaggerProviders; + + protected readonly IGlobalOptionService GlobalOptions; + + protected AbstractAggregateDiagnosticsTaggerProvider( + IThreadingContext threadingContext, + IDiagnosticService diagnosticService, + IDiagnosticAnalyzerService analyzerService, + IGlobalOptionService globalOptions, + ITextBufferVisibilityTracker? visibilityTracker, + IAsynchronousOperationListener listener) + { + GlobalOptions = globalOptions; + + _diagnosticsTaggerProviders = ImmutableArray.Create( + CreateDiagnosticsTaggerProvider(DiagnosticKind.CompilerSyntax), + CreateDiagnosticsTaggerProvider(DiagnosticKind.CompilerSemantic), + CreateDiagnosticsTaggerProvider(DiagnosticKind.AnalyzerSyntax), + CreateDiagnosticsTaggerProvider(DiagnosticKind.AnalyzerSemantic)); + + return; + + SingleDiagnosticKindTaggerProvider CreateDiagnosticsTaggerProvider(DiagnosticKind diagnosticKind) + => new(this, diagnosticKind, threadingContext, diagnosticService, analyzerService, globalOptions, visibilityTracker, listener); + } + + public ITagger? CreateTagger(ITextBuffer buffer) where T : ITag + { + using var _ = ArrayBuilder>.GetInstance(out var taggers); + foreach (var taggerProvider in _diagnosticsTaggerProviders) + taggers.AddIfNotNull(taggerProvider.CreateTagger(buffer)); + + var tagger = new AggregateTagger(taggers.ToImmutable()); + if (tagger is not ITagger genericTagger) + { + tagger.Dispose(); + return null; + } + + return genericTagger; + } + + // Functionality for subclasses to control how this diagnostic tagging operates. All the individual + // SingleDiagnosticKindTaggerProvider will defer to these to do the work so that they otherwise operate + // identically. + + protected abstract ImmutableArray Options { get; } + protected virtual ImmutableArray FeatureOptions { get; } = ImmutableArray.Empty; + + protected abstract bool IsEnabled { get; } + + protected abstract bool SupportsDiagnosticMode(DiagnosticMode mode); + protected abstract bool IncludeDiagnostic(DiagnosticData data); + + protected abstract bool TagEquals(TTag tag1, TTag tag2); + protected abstract ITagSpan? CreateTagSpan(Workspace workspace, SnapshotSpan span, DiagnosticData data); + + /// + /// Get the that should have the tag applied to it. + /// In most cases, this is the but overrides can change it (e.g. unnecessary classifications). + /// + /// the diagnostic containing the location(s). + /// an array of locations that should have the tag applied. + protected virtual ImmutableArray GetLocationsToTag(DiagnosticData diagnosticData) + => diagnosticData.DataLocation is not null ? ImmutableArray.Create(diagnosticData.DataLocation) : ImmutableArray.Empty; +} diff --git a/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs b/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs index 3fdb0de8bc686..b0b248dd3e03b 100644 --- a/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs +++ b/src/EditorFeatures/Core/Diagnostics/AbstractDiagnosticsAdornmentTaggerProvider.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.Diagnostics { internal abstract partial class AbstractDiagnosticsAdornmentTaggerProvider : - AbstractDiagnosticsTaggerProvider + AbstractAggregateDiagnosticsTaggerProvider where TTag : class, ITag { protected AbstractDiagnosticsAdornmentTaggerProvider( @@ -27,9 +27,11 @@ protected AbstractDiagnosticsAdornmentTaggerProvider( { } - protected internal sealed override bool IsEnabled => true; + protected abstract TTag? CreateTag(Workspace workspace, DiagnosticData diagnostic); + + protected sealed override bool IsEnabled => true; - protected internal sealed override ITagSpan? CreateTagSpan( + protected sealed override ITagSpan? CreateTagSpan( Workspace workspace, SnapshotSpan span, DiagnosticData data) { var errorTag = CreateTag(workspace, data); @@ -62,7 +64,5 @@ protected static SnapshotSpan AdjustSnapshotSpan(SnapshotSpan span, int minimumL // make sure length is smaller than snapshot.Length which can happen if start == 0 return new SnapshotSpan(snapshot, start, Math.Min(start + length, snapshot.Length) - start); } - - protected abstract TTag? CreateTag(Workspace workspace, DiagnosticData diagnostic); } } diff --git a/src/EditorFeatures/Core/Diagnostics/DiagnosticsClassificationTaggerProvider.cs b/src/EditorFeatures/Core/Diagnostics/DiagnosticsClassificationTaggerProvider.cs index 69835fd11ac86..1c8fbb4847ff6 100644 --- a/src/EditorFeatures/Core/Diagnostics/DiagnosticsClassificationTaggerProvider.cs +++ b/src/EditorFeatures/Core/Diagnostics/DiagnosticsClassificationTaggerProvider.cs @@ -31,13 +31,13 @@ namespace Microsoft.CodeAnalysis.Diagnostics [ContentType(ContentTypeNames.RoslynContentType)] [ContentType(ContentTypeNames.XamlContentType)] [TagType(typeof(ClassificationTag))] - internal sealed partial class DiagnosticsClassificationTaggerProvider : AbstractDiagnosticsTaggerProvider + internal sealed partial class DiagnosticsClassificationTaggerProvider : AbstractAggregateDiagnosticsTaggerProvider { private readonly ClassificationTypeMap _typeMap; private readonly ClassificationTag _classificationTag; private readonly EditorOptionsService _editorOptionsService; - protected override ImmutableArray Options { get; } = ImmutableArray.Create(InternalFeatureOnOffOptions.Classification); + protected sealed override ImmutableArray Options { get; } = ImmutableArray.Create(InternalFeatureOnOffOptions.Classification); [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -58,16 +58,16 @@ public DiagnosticsClassificationTaggerProvider( // If we are under high contrast mode, the editor ignores classification tags that fade things out, // because that reduces contrast. Since the editor will ignore them, there's no reason to produce them. - protected internal override bool IsEnabled + protected sealed override bool IsEnabled => !_editorOptionsService.Factory.GlobalOptions.GetOptionValue(DefaultTextViewHostOptions.IsInContrastModeId); - protected internal override bool SupportsDiagnosticMode(DiagnosticMode mode) + protected sealed override bool SupportsDiagnosticMode(DiagnosticMode mode) { // We only support push diagnostics. When pull diagnostics are on, diagnostic fading is handled by the lsp client. return mode == DiagnosticMode.Push; } - protected internal override bool IncludeDiagnostic(DiagnosticData data) + protected sealed override bool IncludeDiagnostic(DiagnosticData data) { if (!data.CustomTags.Contains(WellKnownDiagnosticTags.Unnecessary)) { @@ -84,10 +84,10 @@ protected internal override bool IncludeDiagnostic(DiagnosticData data) return true; } - protected internal override ITagSpan CreateTagSpan(Workspace workspace, SnapshotSpan span, DiagnosticData data) + protected sealed override ITagSpan CreateTagSpan(Workspace workspace, SnapshotSpan span, DiagnosticData data) => new TagSpan(span, _classificationTag); - protected internal override ImmutableArray GetLocationsToTag(DiagnosticData diagnosticData) + protected sealed override ImmutableArray GetLocationsToTag(DiagnosticData diagnosticData) { if (diagnosticData.TryGetUnnecessaryDataLocations(out var locationsToTag)) { @@ -98,7 +98,7 @@ protected internal override ImmutableArray GetLocationsT return base.GetLocationsToTag(diagnosticData); } - protected override bool TagEquals(ClassificationTag tag1, ClassificationTag tag2) + protected sealed override bool TagEquals(ClassificationTag tag1, ClassificationTag tag2) => tag1.ClassificationType.Classification == tag2.ClassificationType.Classification; } } diff --git a/src/EditorFeatures/Core/Diagnostics/DiagnosticsSquiggleTaggerProvider.cs b/src/EditorFeatures/Core/Diagnostics/DiagnosticsSquiggleTaggerProvider.cs index 4479a74b1013a..4fc247bfc41ed 100644 --- a/src/EditorFeatures/Core/Diagnostics/DiagnosticsSquiggleTaggerProvider.cs +++ b/src/EditorFeatures/Core/Diagnostics/DiagnosticsSquiggleTaggerProvider.cs @@ -43,13 +43,13 @@ public DiagnosticsSquiggleTaggerProvider( { } - protected internal override bool SupportsDiagnosticMode(DiagnosticMode mode) + protected sealed override bool SupportsDiagnosticMode(DiagnosticMode mode) { // We only support push diagnostics. When pull diagnostics are on, squiggles are handled by the lsp client. return mode == DiagnosticMode.Push; } - protected internal override bool IncludeDiagnostic(DiagnosticData diagnostic) + protected sealed override bool IncludeDiagnostic(DiagnosticData diagnostic) { var isUnnecessary = diagnostic.Severity == DiagnosticSeverity.Hidden && diagnostic.CustomTags.Contains(WellKnownDiagnosticTags.Unnecessary); @@ -58,7 +58,7 @@ protected internal override bool IncludeDiagnostic(DiagnosticData diagnostic) !string.IsNullOrWhiteSpace(diagnostic.Message); } - protected override IErrorTag? CreateTag(Workspace workspace, DiagnosticData diagnostic) + protected sealed override IErrorTag? CreateTag(Workspace workspace, DiagnosticData diagnostic) { Debug.Assert(!string.IsNullOrWhiteSpace(diagnostic.Message)); var errorType = GetErrorTypeFromDiagnostic(diagnostic); @@ -125,7 +125,7 @@ protected internal override bool IncludeDiagnostic(DiagnosticData diagnostic) } } - protected override bool TagEquals(IErrorTag tag1, IErrorTag tag2) + protected sealed override bool TagEquals(IErrorTag tag1, IErrorTag tag2) { Contract.ThrowIfFalse(tag1 is RoslynErrorTag); Contract.ThrowIfFalse(tag2 is RoslynErrorTag); diff --git a/src/EditorFeatures/Core/Diagnostics/DiagnosticsSuggestionTaggerProvider.cs b/src/EditorFeatures/Core/Diagnostics/DiagnosticsSuggestionTaggerProvider.cs index 4b45ebbd0c7d7..1485fb08f1c93 100644 --- a/src/EditorFeatures/Core/Diagnostics/DiagnosticsSuggestionTaggerProvider.cs +++ b/src/EditorFeatures/Core/Diagnostics/DiagnosticsSuggestionTaggerProvider.cs @@ -28,7 +28,7 @@ namespace Microsoft.CodeAnalysis.Diagnostics internal sealed partial class DiagnosticsSuggestionTaggerProvider : AbstractDiagnosticsAdornmentTaggerProvider { - protected override ImmutableArray Options { get; } = ImmutableArray.Create(InternalFeatureOnOffOptions.Squiggles); + protected sealed override ImmutableArray Options { get; } = ImmutableArray.Create(InternalFeatureOnOffOptions.Squiggles); [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -43,26 +43,26 @@ public DiagnosticsSuggestionTaggerProvider( { } - protected internal override bool IncludeDiagnostic(DiagnosticData diagnostic) + protected sealed override bool IncludeDiagnostic(DiagnosticData diagnostic) => diagnostic.Severity == DiagnosticSeverity.Info; - protected internal override bool SupportsDiagnosticMode(DiagnosticMode mode) + protected sealed override bool SupportsDiagnosticMode(DiagnosticMode mode) { // We only support push diagnostics. When pull diagnostics are on, ellipses suggestions are handled by the // lsp client. return mode == DiagnosticMode.Push; } - protected override IErrorTag CreateTag(Workspace workspace, DiagnosticData diagnostic) + protected sealed override IErrorTag CreateTag(Workspace workspace, DiagnosticData diagnostic) => new RoslynErrorTag(PredefinedErrorTypeNames.HintedSuggestion, workspace, diagnostic); - protected override SnapshotSpan AdjustSnapshotSpan(SnapshotSpan snapshotSpan) + protected sealed override SnapshotSpan AdjustSnapshotSpan(SnapshotSpan snapshotSpan) { // We always want suggestion tags to be two characters long. return AdjustSnapshotSpan(snapshotSpan, minimumLength: 2, maximumLength: 2); } - protected override bool TagEquals(IErrorTag tag1, IErrorTag tag2) + protected sealed override bool TagEquals(IErrorTag tag1, IErrorTag tag2) { Contract.ThrowIfFalse(tag1 is RoslynErrorTag); Contract.ThrowIfFalse(tag2 is RoslynErrorTag); diff --git a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource.cs b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource.cs index c39552b2526a7..d8d080ff5701b 100644 --- a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource.cs +++ b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource.cs @@ -310,6 +310,8 @@ private void ResumeIfVisible() private ITaggerEventSource CreateEventSource() { + Contract.ThrowIfTrue(_dataSource.Options.Any(o => o is not Option2 and not PerLanguageOption2), "All options must be Option2 or PerLanguageOption2"); + var eventSource = _dataSource.CreateEventSource(_textView, _subjectBuffer); // If there are any options specified for this tagger, then also hook up event diff --git a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs index ff101af8346d4..0d15ec86f7ea0 100644 --- a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs +++ b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs @@ -168,7 +168,7 @@ private void OnEventSourceChanged(object? _1, TaggerEventArgs _2) => EnqueueWork(highPriority: false); private void EnqueueWork(bool highPriority) - => _eventChangeQueue.AddWork(highPriority); + => _eventChangeQueue.AddWork(highPriority, _dataSource.CancelOnNewWork); private async ValueTask ProcessEventChangeAsync(ImmutableSegmentedList changes, CancellationToken cancellationToken) { diff --git a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.cs b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.cs index e9f569cd8e5bf..602d0c4837394 100644 --- a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.cs +++ b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.cs @@ -91,6 +91,11 @@ internal abstract partial class AbstractAsynchronousTaggerProvider where T /// protected virtual TaggerDelay AddedTagNotificationDelay => TaggerDelay.NearImmediate; + /// + /// Whether or not events from the should cancel in-flight tag-computation. + /// + protected virtual bool CancelOnNewWork { get; } + /// /// Comparer used to check if two tags are the same. Used so that when new tags are produced, they can be /// appropriately 'diffed' to determine what changes to actually report in . @@ -121,8 +126,6 @@ protected AbstractAsynchronousTaggerProvider( AsyncListener = asyncListener; _visibilityTracker = visibilityTracker; - Contract.ThrowIfTrue(this.Options.Any(o => o is not Option2 and not PerLanguageOption2), "All options must be Option2 or PerLanguageOption2"); - #if DEBUG StackTrace = new StackTrace().ToString(); #endif diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticsClassificationTaggerProviderTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticsClassificationTaggerProviderTests.cs index 3bbea50339f72..fada59b76e859 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticsClassificationTaggerProviderTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticsClassificationTaggerProviderTests.cs @@ -26,11 +26,10 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics { [UseExportProvider] - [Trait(Traits.Feature, Traits.Features.Diagnostics)] + [Trait(Traits.Feature, Traits.Features.Diagnostics), Trait(Traits.Feature, Traits.Features.Tagging)] public class DiagnosticsClassificationTaggerProviderTests { - [WpfTheory] - [CombinatorialData] + [WpfTheory, CombinatorialData] public async Task Test_FadingSpans(bool throughAdditionalLocations) { var analyzer = new Analyzer(diagnosticId: "test", throughAdditionalLocations); @@ -41,12 +40,14 @@ public async Task Test_FadingSpans(bool throughAdditionalLocations) using var workspace = TestWorkspace.CreateCSharp(new string[] { "class A { }", "class E { }" }, parseOptions: CSharpParseOptions.Default, composition: SquiggleUtilities.CompositionWithSolutionCrawler); using var wrapper = new DiagnosticTaggerWrapper(workspace, analyzerMap); - var tagger = wrapper.TaggerProvider.CreateTagger(workspace.Documents.First().GetTextBuffer()); + + var firstDocument = workspace.Documents.First(); + var tagger = wrapper.TaggerProvider.CreateTagger(firstDocument.GetTextBuffer()); using var disposable = tagger as IDisposable; // test first update await wrapper.WaitForTags(); - var snapshot = workspace.Documents.First().GetTextBuffer().CurrentSnapshot; + var snapshot = firstDocument.GetTextBuffer().CurrentSnapshot; var spans = tagger.GetTags(snapshot.GetSnapshotSpanCollection()).ToList(); if (!throughAdditionalLocations) { @@ -140,12 +141,14 @@ public async Task Test_FadingOptions(string diagnosticId, bool fadingOptionValue // Set up the tagger using var wrapper = new DiagnosticTaggerWrapper(workspace, analyzerMap); - var tagger = wrapper.TaggerProvider.CreateTagger(workspace.Documents.First().GetTextBuffer()); + + var firstDocument = workspace.Documents.First(); + var tagger = wrapper.TaggerProvider.CreateTagger(firstDocument.GetTextBuffer()); using var disposable = tagger as IDisposable; // test first update await wrapper.WaitForTags(); - var snapshot = workspace.Documents.First().GetTextBuffer().CurrentSnapshot; + var snapshot = firstDocument.GetTextBuffer().CurrentSnapshot; var spans = tagger.GetTags(snapshot.GetSnapshotSpanCollection()).ToList(); if (!fadingOptionValue) { diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticsSquiggleTaggerProviderTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticsSquiggleTaggerProviderTests.cs index 99f8cbf6f86f4..b00fa8d6243ac 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticsSquiggleTaggerProviderTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticsSquiggleTaggerProviderTests.cs @@ -27,7 +27,7 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics { [UseExportProvider] - [Trait(Traits.Feature, Traits.Features.Diagnostics)] + [Trait(Traits.Feature, Traits.Features.Diagnostics), Trait(Traits.Feature, Traits.Features.Tagging)] public class DiagnosticsSquiggleTaggerProviderTests { private static readonly TestComposition s_compositionWithMockDiagnosticService = @@ -46,25 +46,27 @@ public async Task Test_TagSourceDiffer() using var workspace = TestWorkspace.CreateCSharp(new string[] { "class A { }", "class E { }" }, parseOptions: CSharpParseOptions.Default); using var wrapper = new DiagnosticTaggerWrapper(workspace, analyzerMap); - var tagger = wrapper.TaggerProvider.CreateTagger(workspace.Documents.First().GetTextBuffer()); + + var firstDocument = workspace.Documents.First(); + var tagger = wrapper.TaggerProvider.CreateTagger(firstDocument.GetTextBuffer()); using var disposable = tagger as IDisposable; // test first update await wrapper.WaitForTags(); - var snapshot = workspace.Documents.First().GetTextBuffer().CurrentSnapshot; + var snapshot = firstDocument.GetTextBuffer().CurrentSnapshot; var spans = tagger.GetTags(snapshot.GetSnapshotSpanCollection()).ToList(); Assert.True(spans.First().Span.Contains(new Span(0, 1))); // test second update analyzer.ChangeSeverity(); - var document = workspace.CurrentSolution.GetRequiredDocument(workspace.Documents.First().Id); + var document = workspace.CurrentSolution.GetRequiredDocument(firstDocument.Id); var text = await document.GetTextAsync(); workspace.TryApplyChanges(document.WithText(text.WithChanges(new TextChange(new TextSpan(text.Length - 1, 1), string.Empty))).Project.Solution); await wrapper.WaitForTags(); - snapshot = workspace.Documents.First().GetTextBuffer().CurrentSnapshot; + snapshot = firstDocument.GetTextBuffer().CurrentSnapshot; spans = tagger.GetTags(snapshot.GetSnapshotSpanCollection()).ToList(); Assert.True(spans.First().Span.Contains(new Span(0, 1))); } @@ -74,9 +76,11 @@ public async Task MultipleTaggersAndDispose() { using var workspace = TestWorkspace.CreateCSharp(new string[] { "class A {" }, parseOptions: CSharpParseOptions.Default); using var wrapper = new DiagnosticTaggerWrapper(workspace); + // Make two taggers. - var tagger1 = wrapper.TaggerProvider.CreateTagger(workspace.Documents.First().GetTextBuffer()); - var tagger2 = wrapper.TaggerProvider.CreateTagger(workspace.Documents.First().GetTextBuffer()); + var firstDocument = workspace.Documents.First(); + var tagger1 = wrapper.TaggerProvider.CreateTagger(firstDocument.GetTextBuffer()); + var tagger2 = wrapper.TaggerProvider.CreateTagger(firstDocument.GetTextBuffer()); // But dispose the first one. We still want the second one to work. ((IDisposable)tagger1).Dispose(); @@ -84,7 +88,7 @@ public async Task MultipleTaggersAndDispose() using var disposable = tagger2 as IDisposable; await wrapper.WaitForTags(); - var snapshot = workspace.Documents.First().GetTextBuffer().CurrentSnapshot; + var snapshot = firstDocument.GetTextBuffer().CurrentSnapshot; var spans = tagger2.GetTags(snapshot.GetSnapshotSpanCollection()).ToList(); Assert.False(spans.IsEmpty()); } @@ -101,18 +105,23 @@ public async Task TaggerProviderCreatedAfterInitialDiagnosticsReported() var taggerProvider = wrapper.TaggerProvider; // Make a taggers. - var tagger1 = wrapper.TaggerProvider.CreateTagger(workspace.Documents.First().GetTextBuffer()); + var firstDocument = workspace.Documents.First(); + var tagger1 = wrapper.TaggerProvider.CreateTagger(firstDocument.GetTextBuffer()); using var disposable = tagger1 as IDisposable; await wrapper.WaitForTags(); // We should have tags at this point. - var snapshot = workspace.Documents.First().GetTextBuffer().CurrentSnapshot; + var snapshot = firstDocument.GetTextBuffer().CurrentSnapshot; var spans = tagger1.GetTags(snapshot.GetSnapshotSpanCollection()).ToList(); Assert.False(spans.IsEmpty()); } - [WpfFact] - public async Task TestWithMockDiagnosticService_TaggerProviderCreatedBeforeInitialDiagnosticsReported() + [WpfTheory] + [InlineData(DiagnosticKind.CompilerSyntax)] + [InlineData(DiagnosticKind.CompilerSemantic)] + [InlineData(DiagnosticKind.AnalyzerSyntax)] + [InlineData(DiagnosticKind.AnalyzerSemantic)] + internal async Task TestWithMockDiagnosticService_TaggerProviderCreatedBeforeInitialDiagnosticsReported(DiagnosticKind diagnosticKind) { // This test produces diagnostics from a mock service so that we are disconnected from // all the asynchrony of the actual async analyzer engine. If this fails, then the @@ -132,26 +141,31 @@ public async Task TestWithMockDiagnosticService_TaggerProviderCreatedBeforeIniti var provider = workspace.ExportProvider.GetExportedValues().OfType().Single(); // Create the tagger before the first diagnostic event has been fired. - var tagger = provider.CreateTagger(workspace.Documents.First().GetTextBuffer()); + var firstDocument = workspace.Documents.First(); + var tagger = provider.CreateTagger(firstDocument.GetTextBuffer()); Contract.ThrowIfNull(tagger); // Now product the first diagnostic and fire the events. var tree = await workspace.CurrentSolution.Projects.Single().Documents.Single().GetRequiredSyntaxTreeAsync(CancellationToken.None); var span = TextSpan.FromBounds(0, 5); - diagnosticService.CreateDiagnosticAndFireEvents(workspace, analyzerService, Location.Create(tree, span)); + diagnosticService.CreateDiagnosticAndFireEvents(workspace, analyzerService, Location.Create(tree, span), diagnosticKind); using var disposable = tagger as IDisposable; await listenerProvider.GetWaiter(FeatureAttribute.DiagnosticService).ExpeditedWaitAsync(); await listenerProvider.GetWaiter(FeatureAttribute.ErrorSquiggles).ExpeditedWaitAsync(); - var snapshot = workspace.Documents.First().GetTextBuffer().CurrentSnapshot; + var snapshot = firstDocument.GetTextBuffer().CurrentSnapshot; var spans = tagger.GetTags(snapshot.GetSnapshotSpanCollection()).ToList(); Assert.Equal(1, spans.Count); Assert.Equal(span.ToSpan(), spans[0].Span.Span); } - [WpfFact] - public async Task TestWithMockDiagnosticService_TaggerProviderCreatedAfterInitialDiagnosticsReported() + [WpfTheory] + [InlineData(DiagnosticKind.CompilerSyntax)] + [InlineData(DiagnosticKind.CompilerSemantic)] + [InlineData(DiagnosticKind.AnalyzerSyntax)] + [InlineData(DiagnosticKind.AnalyzerSemantic)] + internal async Task TestWithMockDiagnosticService_TaggerProviderCreatedAfterInitialDiagnosticsReported(DiagnosticKind diagnosticKind) { // This test produces diagnostics from a mock service so that we are disconnected from // all the asynchrony of the actual async analyzer engine. If this fails, then the @@ -173,16 +187,17 @@ public async Task TestWithMockDiagnosticService_TaggerProviderCreatedAfterInitia // Create and fire the diagnostic events before the tagger is even made. var tree = await workspace.CurrentSolution.Projects.Single().Documents.Single().GetRequiredSyntaxTreeAsync(CancellationToken.None); var span = TextSpan.FromBounds(0, 5); - diagnosticService.CreateDiagnosticAndFireEvents(workspace, analyzerService, Location.Create(tree, span)); + diagnosticService.CreateDiagnosticAndFireEvents(workspace, analyzerService, Location.Create(tree, span), diagnosticKind); - var tagger = provider.CreateTagger(workspace.Documents.First().GetTextBuffer()); + var firstDocument = workspace.Documents.First(); + var tagger = provider.CreateTagger(firstDocument.GetTextBuffer()); Contract.ThrowIfNull(tagger); using var disposable = tagger as IDisposable; await listenerProvider.GetWaiter(FeatureAttribute.DiagnosticService).ExpeditedWaitAsync(); await listenerProvider.GetWaiter(FeatureAttribute.ErrorSquiggles).ExpeditedWaitAsync(); - var snapshot = workspace.Documents.First().GetTextBuffer().CurrentSnapshot; + var snapshot = firstDocument.GetTextBuffer().CurrentSnapshot; var spans = tagger.GetTags(snapshot.GetSnapshotSpanCollection()).ToList(); Assert.Equal(1, spans.Count); Assert.Equal(span.ToSpan(), spans[0].Span.Span); diff --git a/src/EditorFeatures/Test/Diagnostics/MockDiagnosticService.cs b/src/EditorFeatures/Test/Diagnostics/MockDiagnosticService.cs index 29572be8f02a0..978fac6f966d4 100644 --- a/src/EditorFeatures/Test/Diagnostics/MockDiagnosticService.cs +++ b/src/EditorFeatures/Test/Diagnostics/MockDiagnosticService.cs @@ -60,14 +60,14 @@ private ImmutableArray GetDiagnosticBuckets(Workspace workspac : ImmutableArray.Create(new DiagnosticBucket(this, workspace, GetProjectId(workspace), GetDocumentId(workspace))); } - internal void CreateDiagnosticAndFireEvents(Workspace workspace, MockDiagnosticAnalyzerService analyzerService, Location location) + internal void CreateDiagnosticAndFireEvents(Workspace workspace, MockDiagnosticAnalyzerService analyzerService, Location location, DiagnosticKind diagnosticKind) { var document = workspace.CurrentSolution.Projects.Single().Documents.Single(); _diagnosticData = DiagnosticData.Create(Diagnostic.Create(DiagnosticId, "MockCategory", "MockMessage", DiagnosticSeverity.Error, DiagnosticSeverity.Error, isEnabledByDefault: true, warningLevel: 0, location: location), document); - analyzerService.Diagnostics = ImmutableArray.Create(_diagnosticData); + analyzerService.AddDiagnostic(_diagnosticData, diagnosticKind); DiagnosticsUpdated?.Invoke(this, DiagnosticsUpdatedArgs.DiagnosticsCreated( this, workspace, workspace.CurrentSolution, GetProjectId(workspace), GetDocumentId(workspace), diff --git a/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs b/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs index 17a3ff16012cf..78f8103929763 100644 --- a/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs +++ b/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs @@ -30,7 +30,7 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Preview { [UseExportProvider] - [Trait(Traits.Editor, Traits.Editors.Preview)] + [Trait(Traits.Editor, Traits.Editors.Preview), Trait(Traits.Feature, Traits.Features.Tagging)] public class PreviewWorkspaceTests { [Fact] diff --git a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb index fbeb9c5e98be8..9e2c799397a16 100644 --- a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb @@ -2331,16 +2331,8 @@ class MyClass End Function - - - - - - - - - - Friend Async Function TestGetDiagnosticsForDiagnosticKindsAsync(diagnosticKinds As DiagnosticKinds) As Task + + Friend Async Function TestGetDiagnosticsForDiagnosticKindAsync(diagnosticKind As DiagnosticKind) As Task Dim test = 0 Then + Dim all = diagnosticKind = DiagnosticKind.All + + If all OrElse diagnosticKind = DiagnosticKind.CompilerSyntax Then expectedCount += 1 expectedDiagnosticIds.Add("CS1513") End If - If (diagnosticKinds And DiagnosticKinds.CompilerSemantic) <> 0 Then + If all OrElse diagnosticKind = DiagnosticKind.CompilerSemantic Then expectedCount += 1 expectedDiagnosticIds.Add("CS0219") End If - If (diagnosticKinds And DiagnosticKinds.AnalyzerSyntax) <> 0 Then + If all OrElse diagnosticKind = DiagnosticKind.AnalyzerSyntax Then expectedCount += 1 expectedDiagnosticIds.Add("ID0001") End If - If (diagnosticKinds And DiagnosticKinds.AnalyzerSemantic) <> 0 Then + If all OrElse diagnosticKind = DiagnosticKind.AnalyzerSemantic Then expectedCount += 1 expectedDiagnosticIds.Add("ID0002") End If @@ -2443,27 +2437,27 @@ class MyClass Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) - ' Get diagnostics for span for fine grained DiagnosticKinds in random order + ' Get diagnostics for span for fine grained DiagnosticKind in random order Dim document = project.Documents.Single() Dim root = Await document.GetSyntaxRootAsync() ' Compiler semantic - Dim diagnostics = Await diagnosticService.GetDiagnosticsForSpanAsync(document, root.FullSpan, diagnosticKinds:=DiagnosticKinds.CompilerSemantic) + Dim diagnostics = Await diagnosticService.GetDiagnosticsForSpanAsync(document, root.FullSpan, diagnosticKind:=DiagnosticKind.CompilerSemantic) Dim diagnostic = Assert.Single(diagnostics) Assert.Equal("CS0219", diagnostic.Id) ' Compiler syntax - diagnostics = Await diagnosticService.GetDiagnosticsForSpanAsync(document, root.FullSpan, diagnosticKinds:=DiagnosticKinds.CompilerSyntax) + diagnostics = Await diagnosticService.GetDiagnosticsForSpanAsync(document, root.FullSpan, diagnosticKind:=DiagnosticKind.CompilerSyntax) diagnostic = Assert.Single(diagnostics) Assert.Equal("CS1513", diagnostic.Id) ' Analyzer syntax - diagnostics = Await diagnosticService.GetDiagnosticsForSpanAsync(document, root.FullSpan, diagnosticKinds:=DiagnosticKinds.AnalyzerSyntax) + diagnostics = Await diagnosticService.GetDiagnosticsForSpanAsync(document, root.FullSpan, diagnosticKind:=DiagnosticKind.AnalyzerSyntax) diagnostic = Assert.Single(diagnostics) Assert.Equal("ID0001", diagnostic.Id) ' Analyzer semantic - diagnostics = Await diagnosticService.GetDiagnosticsForSpanAsync(document, root.FullSpan, diagnosticKinds:=DiagnosticKinds.AnalyzerSemantic) + diagnostics = Await diagnosticService.GetDiagnosticsForSpanAsync(document, root.FullSpan, diagnosticKind:=DiagnosticKind.AnalyzerSemantic) diagnostic = Assert.Single(diagnostics) Assert.Equal("ID0002", diagnostic.Id) End Using diff --git a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb index ff543c0efbd38..38cbcb9eb9e22 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb @@ -8885,108 +8885,6 @@ public class AA End Using End Function - - Public Async Function TestTypeImportCompletionAfterScoped(showCompletionInArgumentLists As Boolean) As Task - Using state = TestStateFactory.CreateCSharpTestState( - -namespace MyNamespace -{ - public ref struct MyRefStruct { } -} - -namespace Test -{ - class Program - { - public static void Main() - { - scoped $$ - } - } -} -, - showCompletionInArgumentLists:=showCompletionInArgumentLists) - - state.Workspace.GlobalOptions.SetGlobalOption(New OptionKey(CompletionOptionsStorage.ForceExpandedCompletionIndexCreation), True) - state.Workspace.GlobalOptions.SetGlobalOption( - New OptionKey(CompletionOptionsStorage.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp), True) - - state.SendInvokeCompletionList() - Await state.WaitForAsynchronousOperationsAsync() - Await state.WaitForUIRenderedAsync() - - ' Make sure expander is selected - Await state.SetCompletionItemExpanderStateAndWaitForUiRenderAsync(isSelected:=True) - - Dim expectedText = " -using MyNamespace; - -namespace MyNamespace -{ - public ref struct MyRefStruct { } -} - -namespace Test -{ - class Program - { - public static void Main() - { - scoped MyRefStruct - } - } -} -" - state.SendTypeChars("MyR") - state.SendSelectCompletionItem("MyRefStruct") - state.SendTypeChars(" ") - Assert.Equal(expectedText, state.GetDocumentText()) - Await state.AssertLineTextAroundCaret(expectedTextBeforeCaret:=" scoped MyRefStruct ", expectedTextAfterCaret:="") - End Using - End Function - - - Public Async Function TestTypeImportCompletionAfterScopedInTopLevel(showCompletionInArgumentLists As Boolean) As Task - Using state = TestStateFactory.CreateCSharpTestState( - -scoped $$ - -namespace MyNamespace -{ - public ref struct MyRefStruct { } -} -, - showCompletionInArgumentLists:=showCompletionInArgumentLists) - - state.Workspace.GlobalOptions.SetGlobalOption(New OptionKey(CompletionOptionsStorage.ForceExpandedCompletionIndexCreation), True) - state.Workspace.GlobalOptions.SetGlobalOption( - New OptionKey(CompletionOptionsStorage.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp), True) - - state.SendInvokeCompletionList() - Await state.WaitForAsynchronousOperationsAsync() - Await state.WaitForUIRenderedAsync() - - ' Make sure expander is selected - Await state.SetCompletionItemExpanderStateAndWaitForUiRenderAsync(isSelected:=True) - - Dim expectedText = " -using MyNamespace; - -scoped MyRefStruct - -namespace MyNamespace -{ - public ref struct MyRefStruct { } -} -" - state.SendTypeChars("MyR") - state.SendSelectCompletionItem("MyRefStruct") - state.SendTypeChars(" ") - Assert.Equal(expectedText, state.GetDocumentText()) - Await state.AssertLineTextAroundCaret(expectedTextBeforeCaret:="scoped MyRefStruct ", expectedTextAfterCaret:="") - End Using - End Function - Public Async Function TestCompleteParenthesisForMethodUnderNameofContext(showCompletionInArgumentLists As Boolean) As Task Using state = TestStateFactory.CreateCSharpTestState( diff --git a/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs b/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs index 3133644f1d5bc..c026f366dad9e 100644 --- a/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs +++ b/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs @@ -21,7 +21,7 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics { internal class DiagnosticTaggerWrapper : IDisposable - where TProvider : AbstractDiagnosticsTaggerProvider + where TProvider : AbstractAggregateDiagnosticsTaggerProvider where TTag : ITag { private readonly TestWorkspace _workspace; diff --git a/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs b/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs index 33c151c9bca4e..9647f5a121e14 100644 --- a/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs +++ b/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; @@ -20,14 +21,24 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics [Export(typeof(IDiagnosticAnalyzerService)), Shared, PartNotDiscoverable] internal class MockDiagnosticAnalyzerService : IDiagnosticAnalyzerService { + private readonly ArrayBuilder<(DiagnosticData Diagnostic, DiagnosticKind KindFilter)> _diagnosticsWithKindFilter; public readonly List DocumentsToReanalyze = new(); - public ImmutableArray Diagnostics; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public MockDiagnosticAnalyzerService(IGlobalOptionService globalOptions) { GlobalOptions = globalOptions; + _diagnosticsWithKindFilter = ArrayBuilder<(DiagnosticData Diagnostic, DiagnosticKind KindFilter)>.GetInstance(); + } + + public void AddDiagnostic(DiagnosticData diagnostic, DiagnosticKind diagnosticKind) + => _diagnosticsWithKindFilter.Add((diagnostic, diagnosticKind)); + + public void AddDiagnostics(ImmutableArray diagnostics, DiagnosticKind diagnosticKind) + { + foreach (var diagnostic in diagnostics) + AddDiagnostic(diagnostic, diagnosticKind); } public void Reanalyze(Workspace workspace, IEnumerable? projectIds = null, IEnumerable? documentIds = null, bool highPriority = false) @@ -53,8 +64,8 @@ public Task> GetDiagnosticsAsync(Solution solutio public Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId = null, DocumentId? documentId = null, ImmutableHashSet? diagnosticIds = null, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default) => throw new NotImplementedException(); - public Task> GetDiagnosticsForSpanAsync(TextDocument document, TextSpan? range, Func? shouldIncludeDiagnostic, bool includeCompilerDiagnostics, bool includeSuppressedDiagnostics = true, CodeActionRequestPriority priority = CodeActionRequestPriority.None, Func? addOperationScope = null, DiagnosticKinds diagnosticKinds = DiagnosticKinds.All, CancellationToken cancellationToken = default) - => !Diagnostics.IsDefault ? Task.FromResult(Diagnostics) : throw new NotImplementedException(); + public Task> GetDiagnosticsForSpanAsync(TextDocument document, TextSpan? range, Func? shouldIncludeDiagnostic, bool includeCompilerDiagnostics, bool includeSuppressedDiagnostics = true, CodeActionRequestPriority priority = CodeActionRequestPriority.None, Func? addOperationScope = null, DiagnosticKind diagnosticKind = DiagnosticKind.All, CancellationToken cancellationToken = default) + => Task.FromResult(_diagnosticsWithKindFilter.Where(d => diagnosticKind == d.KindFilter).Select(d => d.Diagnostic).ToImmutableArray()); public Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId = null, ImmutableHashSet? diagnosticIds = null, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default) => throw new NotImplementedException(); @@ -62,7 +73,7 @@ public Task> GetProjectDiagnosticsForIdsAsync(Sol public Task> GetSpecificCachedDiagnosticsAsync(Workspace workspace, object id, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default) => throw new NotImplementedException(); - public Task<(ImmutableArray diagnostics, bool upToDate)> TryGetDiagnosticsForSpanAsync(TextDocument document, TextSpan range, Func? shouldIncludeDiagnostic, bool includeSuppressedDiagnostics = false, CodeActionRequestPriority priority = CodeActionRequestPriority.None, DiagnosticKinds diagnosticKinds = DiagnosticKinds.All, CancellationToken cancellationToken = default) + public Task<(ImmutableArray diagnostics, bool upToDate)> TryGetDiagnosticsForSpanAsync(TextDocument document, TextSpan range, Func? shouldIncludeDiagnostic, bool includeSuppressedDiagnostics = false, CodeActionRequestPriority priority = CodeActionRequestPriority.None, DiagnosticKind diagnosticKind = DiagnosticKind.All, CancellationToken cancellationToken = default) => throw new NotImplementedException(); } } diff --git a/src/EditorFeatures/TestUtilities/Squiggles/SquiggleUtilities.cs b/src/EditorFeatures/TestUtilities/Squiggles/SquiggleUtilities.cs index 254f5faa5998e..91d38903e71b8 100644 --- a/src/EditorFeatures/TestUtilities/Squiggles/SquiggleUtilities.cs +++ b/src/EditorFeatures/TestUtilities/Squiggles/SquiggleUtilities.cs @@ -35,14 +35,17 @@ public static class SquiggleUtilities where TTag : class, ITag { using var wrapper = new DiagnosticTaggerWrapper(workspace, analyzerMap); - var tagger = wrapper.TaggerProvider.CreateTagger(workspace.Documents.First().GetTextBuffer()); + + var firstDocument = workspace.Documents.First(); + var textBuffer = firstDocument.GetTextBuffer(); + var tagger = wrapper.TaggerProvider.CreateTagger(textBuffer); using var disposable = tagger as IDisposable; await wrapper.WaitForTags(); var analyzerDiagnostics = await wrapper.AnalyzerService.GetDiagnosticsAsync(workspace.CurrentSolution); - var snapshot = workspace.Documents.First().GetTextBuffer().CurrentSnapshot; + var snapshot = textBuffer.CurrentSnapshot; var spans = tagger.GetTags(snapshot.GetSnapshotSpanCollection()).ToImmutableArray(); return (analyzerDiagnostics, spans); diff --git a/src/EditorFeatures/TestUtilities/Squiggles/TestDiagnosticTagProducer.cs b/src/EditorFeatures/TestUtilities/Squiggles/TestDiagnosticTagProducer.cs index 0122c087d6860..1bfc21a9200c3 100644 --- a/src/EditorFeatures/TestUtilities/Squiggles/TestDiagnosticTagProducer.cs +++ b/src/EditorFeatures/TestUtilities/Squiggles/TestDiagnosticTagProducer.cs @@ -32,24 +32,25 @@ internal sealed class TestDiagnosticTagProducer return SquiggleUtilities.GetDiagnosticsAndErrorSpansAsync(workspace, analyzerMap); } - internal static async Task>> GetErrorsFromUpdateSource(TestWorkspace workspace, DiagnosticsUpdatedArgs updateArgs) + internal static async Task>> GetErrorsFromUpdateSource(TestWorkspace workspace, DiagnosticsUpdatedArgs updateArgs, DiagnosticKind diagnosticKind) { var globalOptions = workspace.GetService(); var source = new TestDiagnosticUpdateSource(globalOptions); using var wrapper = new DiagnosticTaggerWrapper(workspace, updateSource: source); - var tagger = wrapper.TaggerProvider.CreateTagger(workspace.Documents.First().GetTextBuffer()); + var firstDocument = workspace.Documents.First(); + var tagger = wrapper.TaggerProvider.CreateTagger(firstDocument.GetTextBuffer()); using var disposable = (IDisposable)tagger; var analyzerServer = (MockDiagnosticAnalyzerService)workspace.GetService(); - analyzerServer.Diagnostics = updateArgs.GetAllDiagnosticsRegardlessOfPushPullSetting(); + analyzerServer.AddDiagnostics(updateArgs.GetAllDiagnosticsRegardlessOfPushPullSetting(), diagnosticKind); source.RaiseDiagnosticsUpdated(updateArgs); await wrapper.WaitForTags(); - var snapshot = workspace.Documents.First().GetTextBuffer().CurrentSnapshot; + var snapshot = firstDocument.GetTextBuffer().CurrentSnapshot; var spans = tagger.GetTags(snapshot.GetSnapshotSpanCollection()).ToImmutableArray(); return spans; diff --git a/src/EditorFeatures/VisualBasicTest/Squiggles/ErrorSquiggleProducerTests.vb b/src/EditorFeatures/VisualBasicTest/Squiggles/ErrorSquiggleProducerTests.vb index 0758f233cee0b..7b502cccfcd77 100644 --- a/src/EditorFeatures/VisualBasicTest/Squiggles/ErrorSquiggleProducerTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Squiggles/ErrorSquiggleProducerTests.vb @@ -21,7 +21,7 @@ Imports Microsoft.VisualStudio.Text.Tagging Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Squiggles <[UseExportProvider]> - + Public Class ErrorSquiggleProducerTests Private Shared Async Function ProduceSquiggles(content As String) As Task(Of ImmutableArray(Of ITagSpan(Of IErrorTag))) @@ -70,7 +70,7 @@ End Class") End Class") Dim diagnosticsAndSpans = Await TestDiagnosticTagProducer(Of DiagnosticsSquiggleTaggerProvider, IErrorTag).GetDiagnosticsAndErrorSpans(workspace) - Dim spans = diagnosticsAndSpans.Item1.Zip(diagnosticsAndSpans.Item2, Function(diagostic, span) (diagostic, span)).OrderBy(Function(s) s.span.Span.Span.Start).ToImmutableArray() + Dim spans = diagnosticsAndSpans.Item1.Zip(diagnosticsAndSpans.Item2, Function(diagnostic, span) (diagnostic, span)).OrderBy(Function(s) s.span.Span.Span.Start).ToImmutableArray() Assert.Equal(1, spans.Count()) @@ -83,7 +83,7 @@ End Class") New ClassifiedTextRun(ClassificationTypeNames.Text, "BC30002", QuickInfoHyperLink.TestAccessor.CreateNavigationAction(New Uri("https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(BC30002)", UriKind.Absolute)), "https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(BC30002)"), New ClassifiedTextRun(ClassificationTypeNames.Punctuation, ":"), New ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "), - New ClassifiedTextRun(ClassificationTypeNames.Text, firstSpan.diagostic.Message))) + New ClassifiedTextRun(ClassificationTypeNames.Text, firstSpan.diagnostic.Message))) ToolTipAssert.EqualContent(expectedToolTip, firstSpan.span.Tag.ToolTipContent) End Using @@ -123,7 +123,7 @@ End Class" New CodeStyleOption2(Of Boolean)(value:=True, notification:=NotificationOption2.Error)) Dim diagnosticsAndSpans = Await TestDiagnosticTagProducer(Of DiagnosticsSquiggleTaggerProvider, IErrorTag).GetDiagnosticsAndErrorSpans(workspace, analyzerMap) - Dim spans = diagnosticsAndSpans.Item1.Zip(diagnosticsAndSpans.Item2, Function(diagostic, span) (diagostic, span)).OrderBy(Function(s) s.span.Span.Span.Start).ToImmutableArray() + Dim spans = diagnosticsAndSpans.Item1.Zip(diagnosticsAndSpans.Item2, Function(diagnostic, span) (diagnostic, span)).OrderBy(Function(s) s.span.Span.Span.Start).ToImmutableArray() Assert.Equal(2, spans.Length) Dim first = spans(0) diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/GlobalKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/GlobalKeywordRecommender.cs index 430822733b537..2f87981cf6899 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/GlobalKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/GlobalKeywordRecommender.cs @@ -32,9 +32,13 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context } return - context.IsTypeContext || + context.IsStatementContext || + context.IsGlobalStatementContext || UsingKeywordRecommender.IsUsingDirectiveContext(context, forGlobalKeyword: true, cancellationToken) || context.IsAnyExpressionContext || + context.IsObjectCreationTypeContext || + context.IsIsOrAsTypeContext || + context.IsFunctionPointerTypeArgumentContext || syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) || syntaxTree.IsAfterKeyword(position, SyntaxKind.RefKeyword, cancellationToken) || syntaxTree.IsAfterKeyword(position, SyntaxKind.ReadOnlyKeyword, cancellationToken) || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/InKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/InKeywordRecommender.cs index 99c38072b920c..0f2353cdc2ea9 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/InKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/InKeywordRecommender.cs @@ -36,7 +36,7 @@ private static bool IsInParameterModifierContext(int position, CSharpSyntaxConte if (context.SyntaxTree.IsParameterModifierContext( position, context.LeftToken, includeOperators: true, out var parameterIndex, out var previousModifier)) { - if (previousModifier is SyntaxKind.None or SyntaxKind.ScopedKeyword) + if (previousModifier == SyntaxKind.None) { return true; } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OutKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OutKeywordRecommender.cs index 3dc302e095c0a..e04a64f83af4a 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OutKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OutKeywordRecommender.cs @@ -31,7 +31,7 @@ private static bool IsOutParameterModifierContext(int position, CSharpSyntaxCont { return context.SyntaxTree.IsParameterModifierContext( position, context.LeftToken, includeOperators: false, out _, out var previousModifier) && - previousModifier is SyntaxKind.None or SyntaxKind.ScopedKeyword; + previousModifier == SyntaxKind.None; } } } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RefKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RefKeywordRecommender.cs index b54dff97bf265..8b369352c738d 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RefKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RefKeywordRecommender.cs @@ -93,7 +93,7 @@ private static bool IsRefParameterModifierContext(int position, CSharpSyntaxCont if (context.SyntaxTree.IsParameterModifierContext( position, context.LeftToken, includeOperators: false, out var parameterIndex, out var previousModifier)) { - if (previousModifier is SyntaxKind.None or SyntaxKind.ScopedKeyword) + if (previousModifier == SyntaxKind.None) { return true; } @@ -150,11 +150,6 @@ private static bool IsValidRefExpressionContext(CSharpSyntaxContext context) case SyntaxKind.ReturnKeyword: return true; - // scoped ref ... - case SyntaxKind.ScopedKeyword: - case SyntaxKind.IdentifierToken when token.Text == "scoped": - return true; - // { // () => ref ... // diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ScopedKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ScopedKeywordRecommender.cs deleted file mode 100644 index ded0f3e44fcb0..0000000000000 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ScopedKeywordRecommender.cs +++ /dev/null @@ -1,56 +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.Threading; -using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; -using System.Collections.Generic; -using Microsoft.CodeAnalysis.CSharp.Utilities; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders -{ - internal class ScopedKeywordRecommender : AbstractSyntacticSingleKeywordRecommender - { - public ScopedKeywordRecommender() - : base(SyntaxKind.ScopedKeyword) - { - } - - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) - { - var syntaxTree = context.SyntaxTree; - return - syntaxTree.IsParameterModifierContext(position, context.LeftToken, includeOperators: true, out _, out _) || - syntaxTree.IsAnonymousMethodParameterModifierContext(position, context.LeftToken) || - syntaxTree.IsPossibleLambdaParameterModifierContext(position, context.LeftToken, cancellationToken) || - IsValidScopedLocalContext(context); - } - - private static bool IsValidScopedLocalContext(CSharpSyntaxContext context) - { - // scoped ref var x ... - if (context.IsStatementContext || context.IsGlobalStatementContext) - { - return true; - } - - var token = context.TargetToken; - switch (token.Kind()) - { - // for (scoped ref var x ... - // foreach (scoped ... - case SyntaxKind.OpenParenToken: - var previous = token.GetPreviousToken(includeSkipped: true); - return previous.Kind() is SyntaxKind.ForKeyword or SyntaxKind.ForEachKeyword; - - // M(out scoped ..) - case SyntaxKind.OutKeyword: - return token.Parent is ArgumentSyntax; - } - - return false; - } - } -} diff --git a/src/Features/CSharp/Portable/FullyQualify/CSharpFullyQualifyCodeFixProvider.cs b/src/Features/CSharp/Portable/FullyQualify/CSharpFullyQualifyCodeFixProvider.cs index 5e47883c0cd09..e11e370790a2f 100644 --- a/src/Features/CSharp/Portable/FullyQualify/CSharpFullyQualifyCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/FullyQualify/CSharpFullyQualifyCodeFixProvider.cs @@ -18,7 +18,7 @@ namespace Microsoft.CodeAnalysis.CSharp.CodeFixes.FullyQualify { [ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.FullyQualify), Shared] [ExtensionOrder(After = PredefinedCodeFixProviderNames.AddImport)] - internal class CSharpFullyQualifyCodeFixProvider : AbstractFullyQualifyCodeFixProvider + internal class CSharpFullyQualifyCodeFixProvider : AbstractFullyQualifyCodeFixProvider { /// /// name does not exist in context @@ -51,45 +51,32 @@ public CSharpFullyQualifyCodeFixProvider() { } - public override ImmutableArray FixableDiagnosticIds - { - get { return ImmutableArray.Create(CS0103, CS0104, CS0246, CS0305, CS0308, IDEDiagnosticIds.UnboundIdentifierId); } - } - - protected override bool IgnoreCase => false; + public override ImmutableArray FixableDiagnosticIds { get; } = + ImmutableArray.Create(CS0103, CS0104, CS0246, CS0305, CS0308, IDEDiagnosticIds.UnboundIdentifierId); - protected override bool CanFullyQualify(Diagnostic diagnostic, ref SyntaxNode node) + protected override bool CanFullyQualify(Diagnostic diagnostic, SyntaxNode node, [NotNullWhen(true)] out SimpleNameSyntax? simpleName) { - if (node is not SimpleNameSyntax simpleName) - { + simpleName = node as SimpleNameSyntax; + if (simpleName is null) return false; - } if (!simpleName.LooksLikeStandaloneTypeName()) - { return false; - } if (!simpleName.CanBeReplacedWithAnyName()) - { return false; - } return true; } - protected override async Task ReplaceNodeAsync(SyntaxNode node, string containerName, bool resultingSymbolIsType, CancellationToken cancellationToken) + protected override async Task ReplaceNodeAsync(SimpleNameSyntax simpleName, string containerName, bool resultingSymbolIsType, CancellationToken cancellationToken) { - var simpleName = (SimpleNameSyntax)node; - var leadingTrivia = simpleName.GetLeadingTrivia(); var newName = simpleName.WithLeadingTrivia(SyntaxTriviaList.Empty); - var qualifiedName = SyntaxFactory.QualifiedName( - SyntaxFactory.ParseName(containerName), newName); - - qualifiedName = qualifiedName.WithLeadingTrivia(leadingTrivia); - qualifiedName = qualifiedName.WithAdditionalAnnotations(Formatter.Annotation); + var qualifiedName = SyntaxFactory.QualifiedName(SyntaxFactory.ParseName(containerName), newName) + .WithLeadingTrivia(leadingTrivia) + .WithAdditionalAnnotations(Formatter.Annotation); var syntaxTree = simpleName.SyntaxTree; var root = await syntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); @@ -99,7 +86,7 @@ protected override async Task ReplaceNodeAsync(SyntaxNode node, stri // CS0138 that would result from the former. Don't do this for using aliases though as `static` and using // aliases cannot be combined. if (resultingSymbolIsType && - node.Parent is UsingDirectiveSyntax { Alias: null, StaticKeyword.RawKind: 0 } usingDirective) + simpleName.Parent is UsingDirectiveSyntax { Alias: null, StaticKeyword.RawKind: 0 } usingDirective) { var newUsingDirective = usingDirective .WithStaticKeyword(SyntaxFactory.Token(SyntaxKind.StaticKeyword)) diff --git a/src/Features/Core/Portable/AddImport/SearchScopes/SourceSymbolsProjectSearchScope.cs b/src/Features/Core/Portable/AddImport/SearchScopes/SourceSymbolsProjectSearchScope.cs index 287d8a99be90e..022ecd693b905 100644 --- a/src/Features/Core/Portable/AddImport/SearchScopes/SourceSymbolsProjectSearchScope.cs +++ b/src/Features/Core/Portable/AddImport/SearchScopes/SourceSymbolsProjectSearchScope.cs @@ -52,16 +52,13 @@ protected override async Task> FindDeclarationsAsync( searchQuery, lazyAssembly, filter, CancellationToken).ConfigureAwait(false); return declarations; - } - private static AsyncLazy CreateLazyAssembly(Project project) - { - return new AsyncLazy( - async c => - { - var compilation = await project.GetRequiredCompilationAsync(c).ConfigureAwait(false); - return compilation.Assembly; - }, cacheResult: true); + static AsyncLazy CreateLazyAssembly(Project project) + => new(async c => + { + var compilation = await project.GetRequiredCompilationAsync(c).ConfigureAwait(false); + return compilation.Assembly; + }, cacheResult: true); } } } diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticKind.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticKind.cs new file mode 100644 index 0000000000000..bc97619a79475 --- /dev/null +++ b/src/Features/Core/Portable/Diagnostics/DiagnosticKind.cs @@ -0,0 +1,16 @@ +// 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; + +namespace Microsoft.CodeAnalysis.Diagnostics; + +internal enum DiagnosticKind +{ + All = 0, + CompilerSyntax = 1, + CompilerSemantic = 2, + AnalyzerSyntax = 3, + AnalyzerSemantic = 4, +} diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticKinds.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticKinds.cs deleted file mode 100644 index 98f5c8100914a..0000000000000 --- a/src/Features/Core/Portable/Diagnostics/DiagnosticKinds.cs +++ /dev/null @@ -1,22 +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; - -namespace Microsoft.CodeAnalysis.Diagnostics; - -[Flags] -internal enum DiagnosticKinds -{ - CompilerSyntax = 1 << 0, - AnalyzerSyntax = 1 << 1, - CompilerSemantic = 1 << 2, - AnalyzerSemantic = 1 << 3, - - AllCompiler = CompilerSyntax | CompilerSemantic, - AllAnalyzer = AnalyzerSyntax | AnalyzerSemantic, - AllSyntax = CompilerSyntax | AnalyzerSyntax, - AllSemantic = CompilerSemantic | AnalyzerSemantic, - All = AllSyntax | AllSemantic -} diff --git a/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs index 11a513b4a6061..6f8908045bc20 100644 --- a/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs @@ -74,14 +74,14 @@ internal interface IDiagnosticAnalyzerService /// This API will only force complete analyzers that support span based analysis, i.e. compiler analyzer and /// s that support . /// For the rest of the analyzers, it will only return diagnostics if the analyzer has already been executed. - /// Use + /// Use /// if you want to force complete all analyzers and get up-to-date diagnostics for all analyzers for the given span. /// Task<(ImmutableArray diagnostics, bool upToDate)> TryGetDiagnosticsForSpanAsync( TextDocument document, TextSpan range, Func? shouldIncludeDiagnostic, bool includeSuppressedDiagnostics = false, CodeActionRequestPriority priority = CodeActionRequestPriority.None, - DiagnosticKinds diagnosticKinds = DiagnosticKinds.All, + DiagnosticKind diagnosticKind = DiagnosticKind.All, CancellationToken cancellationToken = default); /// @@ -97,7 +97,7 @@ Task> GetDiagnosticsForSpanAsync( bool includeCompilerDiagnostics, bool includeSuppressedDiagnostics = false, CodeActionRequestPriority priority = CodeActionRequestPriority.None, Func? addOperationScope = null, - DiagnosticKinds diagnosticKinds = DiagnosticKinds.All, + DiagnosticKind diagnosticKind = DiagnosticKind.All, CancellationToken cancellationToken = default); } @@ -106,9 +106,9 @@ internal static class IDiagnosticAnalyzerServiceExtensions public static Task> GetDiagnosticsForSpanAsync(this IDiagnosticAnalyzerService service, TextDocument document, TextSpan range, string? diagnosticId = null, bool includeSuppressedDiagnostics = false, Func? addOperationScope = null, - DiagnosticKinds diagnosticKinds = DiagnosticKinds.All, + DiagnosticKind diagnosticKind = DiagnosticKind.All, CancellationToken cancellationToken = default) - => service.GetDiagnosticsForSpanAsync(document, range, diagnosticId, includeSuppressedDiagnostics, CodeActionRequestPriority.None, addOperationScope, diagnosticKinds, cancellationToken); + => service.GetDiagnosticsForSpanAsync(document, range, diagnosticId, includeSuppressedDiagnostics, CodeActionRequestPriority.None, addOperationScope, diagnosticKind, cancellationToken); /// /// Return up to date diagnostics for the given span for the document @@ -123,13 +123,13 @@ public static Task> GetDiagnosticsForSpanAsync(th bool includeSuppressedDiagnostics = false, CodeActionRequestPriority priority = CodeActionRequestPriority.None, Func? addOperationScope = null, - DiagnosticKinds diagnosticKinds = DiagnosticKinds.All, + DiagnosticKind diagnosticKind = DiagnosticKind.All, CancellationToken cancellationToken = default) { Func? shouldIncludeDiagnostic = diagnosticId != null ? id => id == diagnosticId : null; return service.GetDiagnosticsForSpanAsync(document, range, shouldIncludeDiagnostic, includeCompilerDiagnostics: true, includeSuppressedDiagnostics, priority, - addOperationScope, diagnosticKinds, cancellationToken); + addOperationScope, diagnosticKind, cancellationToken); } } } diff --git a/src/Features/Core/Portable/FullyQualify/AbstractFullyQualifyCodeFixProvider.SymbolResult.cs b/src/Features/Core/Portable/FullyQualify/AbstractFullyQualifyCodeFixProvider.SymbolResult.cs index da3ebf2cb035a..89cac899b1af2 100644 --- a/src/Features/Core/Portable/FullyQualify/AbstractFullyQualifyCodeFixProvider.SymbolResult.cs +++ b/src/Features/Core/Portable/FullyQualify/AbstractFullyQualifyCodeFixProvider.SymbolResult.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.CodeFixes.FullyQualify { - internal abstract partial class AbstractFullyQualifyCodeFixProvider : CodeFixProvider + internal abstract partial class AbstractFullyQualifyCodeFixProvider { private readonly struct SymbolResult : IEquatable, IComparable { diff --git a/src/Features/Core/Portable/FullyQualify/AbstractFullyQualifyCodeFixProvider.cs b/src/Features/Core/Portable/FullyQualify/AbstractFullyQualifyCodeFixProvider.cs index 709b926bd8245..e703c00d1d99a 100644 --- a/src/Features/Core/Portable/FullyQualify/AbstractFullyQualifyCodeFixProvider.cs +++ b/src/Features/Core/Portable/FullyQualify/AbstractFullyQualifyCodeFixProvider.cs @@ -5,22 +5,32 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.FindSymbols; +using Microsoft.CodeAnalysis.FindSymbols.SymbolTree; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; using static Microsoft.CodeAnalysis.Shared.Utilities.EditorBrowsableHelpers; namespace Microsoft.CodeAnalysis.CodeFixes.FullyQualify { - internal abstract partial class AbstractFullyQualifyCodeFixProvider : CodeFixProvider + /// + /// Exists only for interactive to do a type check for this precise fixer. + /// + internal abstract class AbstractFullyQualifyCodeFixProvider : CodeFixProvider + { + // Just to silence analyzer. + public abstract override FixAllProvider? GetFixAllProvider(); + } + + internal abstract partial class AbstractFullyQualifyCodeFixProvider : AbstractFullyQualifyCodeFixProvider + where TSimpleNameSyntax : SyntaxNode { private const int MaxResults = 3; @@ -28,10 +38,6 @@ internal abstract partial class AbstractFullyQualifyCodeFixProvider : CodeFixPro private const int TypeWeight = 1; private const int NamespaceWithErrorsWeight = 2; - protected AbstractFullyQualifyCodeFixProvider() - { - } - public override FixAllProvider? GetFixAllProvider() { // Fix All is not supported by this code fix @@ -39,80 +45,162 @@ protected AbstractFullyQualifyCodeFixProvider() return null; } - protected abstract bool IgnoreCase { get; } - protected abstract bool CanFullyQualify(Diagnostic diagnostic, ref SyntaxNode node); - protected abstract Task ReplaceNodeAsync(SyntaxNode node, string containerName, bool resultingSymbolIsType, CancellationToken cancellationToken); + protected abstract bool CanFullyQualify(Diagnostic diagnostic, SyntaxNode node, [NotNullWhen(true)] out TSimpleNameSyntax? simpleName); + protected abstract Task ReplaceNodeAsync(TSimpleNameSyntax simpleName, string containerName, bool resultingSymbolIsType, CancellationToken cancellationToken); public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { - var document = context.Document; - var span = context.Span; - var diagnostics = context.Diagnostics; var cancellationToken = context.CancellationToken; - + var document = context.Document; var project = document.Project; - var diagnostic = diagnostics.First(); - var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var node = root.FindToken(span.Start).GetAncestors().First(n => n.Span.Contains(span)); + var syntaxFacts = document.GetRequiredLanguageService(); using (Logger.LogBlock(FunctionId.Refactoring_FullyQualify, cancellationToken)) { + var span = context.Span; + var diagnostics = context.Diagnostics; + + var diagnostic = diagnostics.First(); + + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var node = root.FindToken(span.Start).GetAncestors().First(n => n.Span.Contains(span)); + // Has to be a simple identifier or generic name. - if (node == null || !CanFullyQualify(diagnostic, ref node)) - { + if (node == null || !CanFullyQualify(diagnostic, node, out var simpleName)) return; - } - var hideAdvancedMembers = context.Options.GetOptions(document.Project.Services).HideAdvancedMembers; - var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var ignoreCase = !syntaxFacts.IsCaseSensitive; + syntaxFacts.GetNameAndArityOfSimpleName(simpleName, out var name, out _); + var inAttributeContext = syntaxFacts.IsAttributeName(simpleName); - var matchingTypes = await GetMatchingTypesAsync(document, semanticModel, node, hideAdvancedMembers, cancellationToken).ConfigureAwait(false); - var matchingNamespaces = await GetMatchingNamespacesAsync(project, semanticModel, node, cancellationToken).ConfigureAwait(false); + var matchingTypes = await FindAsync(name, ignoreCase, SymbolFilter.Type).ConfigureAwait(false); + var matchingAttributeTypes = inAttributeContext ? await FindAsync(name + nameof(Attribute), ignoreCase, SymbolFilter.Type).ConfigureAwait(false) : ImmutableArray.Empty; + var matchingNamespaces = inAttributeContext ? ImmutableArray.Empty : await FindAsync(name, ignoreCase, SymbolFilter.Namespace).ConfigureAwait(false); - if (matchingTypes.IsEmpty && matchingNamespaces.IsEmpty) - { + if (matchingTypes.IsEmpty && matchingAttributeTypes.IsEmpty && matchingNamespaces.IsEmpty) return; - } - var matchingTypeContainers = FilterAndSort(GetContainers(matchingTypes, semanticModel.Compilation)); - var matchingNamespaceContainers = FilterAndSort(GetContainers(matchingNamespaces, semanticModel.Compilation)); + // We found some matches for the name alone. Do some more checks to see if those matches are applicable in this location. + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + + var matchingTypeSearchResults = GetTypeSearchResults(semanticModel, simpleName, matchingTypes.Concat(matchingAttributeTypes)); + var matchingNamespaceSearchResults = GetNamespaceSearchResults(semanticModel, simpleName, matchingNamespaces); + if (matchingTypeSearchResults.IsEmpty && matchingNamespaceSearchResults.IsEmpty) + return; - var proposedContainers = - matchingTypeContainers.Concat(matchingNamespaceContainers) - .Distinct() - .Take(MaxResults); + var matchingTypeContainers = FilterAndSort(GetContainers(matchingTypeSearchResults, semanticModel.Compilation)); + var matchingNamespaceContainers = FilterAndSort(GetContainers(matchingNamespaceSearchResults, semanticModel.Compilation)); - var codeActions = CreateActions(document, node, semanticModel, proposedContainers).ToImmutableArray(); + var proposedContainers = matchingTypeContainers + .Concat(matchingNamespaceContainers) + .Distinct() + .Take(MaxResults); + var codeActions = CreateActions(document, semanticModel, simpleName, name, proposedContainers).ToImmutableArray(); if (codeActions.Length > 1) { // Wrap the spell checking actions into a single top level suggestion // so as to not clutter the list. - context.RegisterCodeFix(new GroupingCodeAction( - string.Format(FeaturesResources.Fully_qualify_0, GetNodeName(document, node)), - codeActions), context.Diagnostics); + context.RegisterCodeFix(CodeAction.Create( + string.Format(FeaturesResources.Fully_qualify_0, name), + codeActions, + isInlinable: true), context.Diagnostics); } else { context.RegisterFixes(codeActions, context.Diagnostics); } } + + async Task> FindAsync(string name, bool ignoreCase, SymbolFilter filter) + { + return await DeclarationFinder.FindAllDeclarationsWithNormalQueryAsync( + project, SearchQuery.Create(name, ignoreCase), filter, cancellationToken).ConfigureAwait(false); + } + + ImmutableArray GetTypeSearchResults( + SemanticModel semanticModel, + TSimpleNameSyntax simpleName, + ImmutableArray matchingTypes) + { + var editorBrowserInfo = new EditorBrowsableInfo(semanticModel.Compilation); + + var hideAdvancedMembers = context.Options.GetOptions(document.Project.Services).HideAdvancedMembers; + var looksGeneric = syntaxFacts.LooksGeneric(simpleName); + + var inAttributeContext = syntaxFacts.IsAttributeName(simpleName); + syntaxFacts.GetNameAndArityOfSimpleName(simpleName, out var name, out var arity); + + var validSymbols = matchingTypes + .OfType() + .Where(s => + IsValidNamedTypeSearchResult(semanticModel, arity, inAttributeContext, looksGeneric, s) && + s.IsEditorBrowsable(hideAdvancedMembers, semanticModel.Compilation, editorBrowserInfo)) + .ToImmutableArray(); + + // Check what the current node binds to. If it binds to any symbols, but with + // the wrong arity, then we don't want to suggest fully qualifying to the same + // type that we're already binding to. That won't address the WrongArity problem. + var currentSymbolInfo = semanticModel.GetSymbolInfo(simpleName, cancellationToken); + if (currentSymbolInfo.CandidateReason == CandidateReason.WrongArity) + { + validSymbols = validSymbols.WhereAsArray( + s => !currentSymbolInfo.CandidateSymbols.Contains(s)); + } + + return validSymbols.SelectAsArray(s => new SymbolResult(s, weight: TypeWeight)); + } + + ImmutableArray GetNamespaceSearchResults( + SemanticModel semanticModel, + TSimpleNameSyntax simpleName, + ImmutableArray symbols) + { + // There might be multiple namespaces that this name will resolve successfully in. + // Some of them may be 'better' results than others. For example, say you have + // Y.Z and Y exists in both X1 and X2 + // We'll want to order them such that we prefer the namespace that will correctly + // bind Z off of Y as well. + + string? rightName = null; + var isAttributeName = false; + if (syntaxFacts.IsLeftSideOfDot(simpleName)) + { + var rightSide = syntaxFacts.GetRightSideOfDot(simpleName.Parent); + Contract.ThrowIfNull(rightSide); + + syntaxFacts.GetNameAndArityOfSimpleName(rightSide, out rightName, out _); + isAttributeName = syntaxFacts.IsAttributeName(rightSide); + } + + return symbols + .OfType() + .Where(n => !n.IsGlobalNamespace && HasAccessibleTypes(n, semanticModel, cancellationToken)) + .Select(n => new SymbolResult(n, + BindsWithoutErrors(n, rightName, isAttributeName) ? NamespaceWithNoErrorsWeight : NamespaceWithErrorsWeight)) + .ToImmutableArray(); + } } private IEnumerable CreateActions( - Document document, SyntaxNode node, SemanticModel semanticModel, + Document document, + SemanticModel semanticModel, + TSimpleNameSyntax simpleName, + string name, IEnumerable proposedContainers) { + var syntaxFacts = document.GetRequiredLanguageService(); + var ignoreCase = !syntaxFacts.IsCaseSensitive; + foreach (var symbolResult in proposedContainers) { var container = symbolResult.Symbol; - var containerName = container.ToMinimalDisplayString(semanticModel, node.SpanStart); - - var name = GetNodeName(document, node); + Contract.ThrowIfNull(symbolResult.OriginalSymbol); + var containerName = container.ToMinimalDisplayString(semanticModel, simpleName.SpanStart); // Actual member name might differ by case. string memberName; - if (IgnoreCase) + if (ignoreCase) { var member = container.GetMembers(name).FirstOrDefault(); memberName = member != null ? member.Name : name; @@ -125,76 +213,19 @@ private IEnumerable CreateActions( var title = $"{containerName}.{memberName}"; var codeAction = CodeAction.Create( title, - c => ProcessNodeAsync(document, node, containerName, symbolResult.OriginalSymbol, c), + cancellationToken => ProcessNodeAsync(document, simpleName, containerName, symbolResult.OriginalSymbol, cancellationToken), title); yield return codeAction; } } - private static string GetNodeName(Document document, SyntaxNode node) - { - var syntaxFacts = document.GetRequiredLanguageService(); - syntaxFacts.GetNameAndArityOfSimpleName(node, out var name, out _); - - Contract.ThrowIfNull(name, "node isn't a SimpleNameSyntax? CanFullyQualify should have returned false."); - return name; - } - - private async Task ProcessNodeAsync(Document document, SyntaxNode node, string containerName, INamespaceOrTypeSymbol? originalSymbol, CancellationToken cancellationToken) + private async Task ProcessNodeAsync(Document document, TSimpleNameSyntax simpleName, string containerName, INamespaceOrTypeSymbol originalSymbol, CancellationToken cancellationToken) { - Contract.ThrowIfNull(originalSymbol, "Original symbol information missing. Haven't called GetContainers?"); - - var newRoot = await ReplaceNodeAsync(node, containerName, originalSymbol.IsType, cancellationToken).ConfigureAwait(false); + var newRoot = await ReplaceNodeAsync(simpleName, containerName, originalSymbol.IsType, cancellationToken).ConfigureAwait(false); return document.WithSyntaxRoot(newRoot); } - private async Task> GetMatchingTypesAsync( - Document document, SemanticModel semanticModel, SyntaxNode node, bool hideAdvancedMembers, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var project = document.Project; - var syntaxFacts = project.Services.GetRequiredService(); - - syntaxFacts.GetNameAndArityOfSimpleName(node, out var name, out var arity); - var looksGeneric = syntaxFacts.LooksGeneric(node); - - var symbols = await DeclarationFinder.FindAllDeclarationsWithNormalQueryAsync( - project, SearchQuery.Create(name, IgnoreCase), - SymbolFilter.Type, cancellationToken).ConfigureAwait(false); - - // also lookup type symbols with the "Attribute" suffix. - var inAttributeContext = syntaxFacts.IsAttributeName(node); - if (inAttributeContext) - { - var attributeSymbols = await DeclarationFinder.FindAllDeclarationsWithNormalQueryAsync( - project, SearchQuery.Create(name + "Attribute", IgnoreCase), - SymbolFilter.Type, cancellationToken).ConfigureAwait(false); - symbols = symbols.Concat(attributeSymbols); - } - - var editorBrowserInfo = new EditorBrowsableInfo(semanticModel.Compilation); - - var validSymbols = symbols - .OfType() - .Where(s => IsValidNamedTypeSearchResult(semanticModel, arity, inAttributeContext, looksGeneric, s) && - s.IsEditorBrowsable(hideAdvancedMembers, semanticModel.Compilation, editorBrowserInfo)) - .ToImmutableArray(); - - // Check what the current node binds to. If it binds to any symbols, but with - // the wrong arity, then we don't want to suggest fully qualifying to the same - // type that we're already binding to. That won't address the WrongArity problem. - var currentSymbolInfo = semanticModel.GetSymbolInfo(node, cancellationToken); - if (currentSymbolInfo.CandidateReason == CandidateReason.WrongArity) - { - validSymbols = validSymbols.WhereAsArray( - s => !currentSymbolInfo.CandidateSymbols.Contains(s)); - } - - return validSymbols.SelectAsArray(s => new SymbolResult(s, weight: TypeWeight)); - } - private static bool IsValidNamedTypeSearchResult( SemanticModel semanticModel, int arity, bool inAttributeContext, bool looksGeneric, INamedTypeSymbol searchResult) @@ -233,82 +264,26 @@ private static bool IsValidNamedTypeSearchResult( } private static bool HasValidContainer(ISymbol symbol) - { - var container = symbol.ContainingSymbol; - return container is INamespaceSymbol || - (container is INamedTypeSymbol parentType && !parentType.IsGenericType); - } - - private async Task> GetMatchingNamespacesAsync( - Project project, - SemanticModel semanticModel, - SyntaxNode simpleName, - CancellationToken cancellationToken) - { - var syntaxFacts = project.Services.GetRequiredService(); - if (syntaxFacts.IsAttributeName(simpleName)) - { - return ImmutableArray.Empty; - } - - syntaxFacts.GetNameAndArityOfSimpleName(simpleName, out var name, out var arityUnused); - if (cancellationToken.IsCancellationRequested) - { - return ImmutableArray.Empty; - } - - var symbols = await DeclarationFinder.FindAllDeclarationsWithNormalQueryAsync( - project, SearchQuery.Create(name, IgnoreCase), - SymbolFilter.Namespace, cancellationToken).ConfigureAwait(false); - - // There might be multiple namespaces that this name will resolve successfully in. - // Some of them may be 'better' results than others. For example, say you have - // Y.Z and Y exists in both X1 and X2 - // We'll want to order them such that we prefer the namespace that will correctly - // bind Z off of Y as well. - - string? rightName = null; - var isAttributeName = false; - if (syntaxFacts.IsLeftSideOfDot(simpleName)) - { - var rightSide = syntaxFacts.GetRightSideOfDot(simpleName.Parent); - Contract.ThrowIfNull(rightSide); - - syntaxFacts.GetNameAndArityOfSimpleName(rightSide, out rightName, out arityUnused); - isAttributeName = syntaxFacts.IsAttributeName(rightSide); - } - - var namespaces = symbols - .OfType() - .Where(n => !n.IsGlobalNamespace && HasAccessibleTypes(n, semanticModel, cancellationToken)) - .Select(n => new SymbolResult(n, - BindsWithoutErrors(n, rightName, isAttributeName) ? NamespaceWithNoErrorsWeight : NamespaceWithErrorsWeight)); - - return namespaces.ToImmutableArray(); - } + => symbol.ContainingSymbol is INamespaceSymbol or INamedTypeSymbol { IsGenericType: false }; private bool BindsWithoutErrors(INamespaceSymbol ns, string? rightName, bool isAttributeName) { // If there was no name on the right, then this binds without any problems. if (rightName == null) - { return true; - } // Otherwise, see if the namespace we will bind this contains a member with the same // name as the name on the right. var types = ns.GetMembers(rightName); if (types.Any()) - { return true; - } if (!isAttributeName) { return false; } - return BindsWithoutErrors(ns, rightName + "Attribute", isAttributeName: false); + return BindsWithoutErrors(ns, rightName + nameof(Attribute), isAttributeName: false); } private static bool HasAccessibleTypes(INamespaceSymbol @namespace, SemanticModel model, CancellationToken cancellationToken) @@ -334,15 +309,7 @@ private static IEnumerable GetContainers( private static IEnumerable FilterAndSort(IEnumerable symbols) => symbols.Distinct() - .Where(n => n.Symbol is INamedTypeSymbol || !((INamespaceSymbol)n.Symbol).IsGlobalNamespace) + .Where(n => n.Symbol is INamedTypeSymbol or INamespaceSymbol { IsGlobalNamespace: false }) .Order(); - - private class GroupingCodeAction : CodeAction.CodeActionWithNestedActions - { - public GroupingCodeAction(string title, ImmutableArray nestedActions) - : base(title, nestedActions, isInlinable: true) - { - } - } } } diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index c933b5277c3e7..7cef36bbd94a4 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -74,7 +74,7 @@ public void Reanalyze(Workspace workspace, IEnumerable? projectIds = Func? shouldIncludeDiagnostic, bool includeSuppressedDiagnostics = false, CodeActionRequestPriority priority = CodeActionRequestPriority.None, - DiagnosticKinds diagnosticKinds = DiagnosticKinds.All, + DiagnosticKind diagnosticKinds = DiagnosticKind.All, CancellationToken cancellationToken = default) { if (_map.TryGetValue(document.Project.Solution.Workspace, out var analyzer)) @@ -102,7 +102,7 @@ public Task> GetDiagnosticsForSpanAsync( bool includeSuppressedDiagnostics, CodeActionRequestPriority priority, Func? addOperationScope, - DiagnosticKinds diagnosticKinds, + DiagnosticKind diagnosticKinds, CancellationToken cancellationToken) { if (_map.TryGetValue(document.Project.Solution.Workspace, out var analyzer)) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs index 9dcfe24495ace..2908a0cf5a410 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs @@ -24,7 +24,7 @@ internal partial class DiagnosticIncrementalAnalyzer public async Task TryAppendDiagnosticsForSpanAsync( TextDocument document, TextSpan? range, ArrayBuilder result, Func? shouldIncludeDiagnostic, bool includeSuppressedDiagnostics, bool includeCompilerDiagnostics, CodeActionRequestPriority priority, bool blockForData, - Func? addOperationScope, DiagnosticKinds diagnosticKinds, CancellationToken cancellationToken) + Func? addOperationScope, DiagnosticKind diagnosticKinds, CancellationToken cancellationToken) { var getter = await LatestDiagnosticsForSpanGetter.CreateAsync( this, document, range, blockForData, addOperationScope, includeSuppressedDiagnostics, includeCompilerDiagnostics, @@ -41,7 +41,7 @@ public async Task> GetDiagnosticsForSpanAsync( CodeActionRequestPriority priority, bool blockForData, Func? addOperationScope, - DiagnosticKinds diagnosticKinds, + DiagnosticKind diagnosticKinds, CancellationToken cancellationToken) { using var _ = ArrayBuilder.GetInstance(out var list); @@ -78,7 +78,7 @@ private sealed class LatestDiagnosticsForSpanGetter private readonly Func? _addOperationScope; private readonly bool _cacheFullDocumentDiagnostics; private readonly bool _logPerformanceInfo; - private readonly DiagnosticKinds _diagnosticKinds; + private readonly DiagnosticKind _diagnosticKind; private delegate Task> DiagnosticsGetterAsync(DiagnosticAnalyzer analyzer, DocumentAnalysisExecutor executor, CancellationToken cancellationToken); @@ -92,7 +92,7 @@ public static async Task CreateAsync( bool includeCompilerDiagnostics, CodeActionRequestPriority priority, Func? shouldIncludeDiagnostic, - DiagnosticKinds diagnosticKinds, + DiagnosticKind diagnosticKinds, CancellationToken cancellationToken) { var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); @@ -158,7 +158,7 @@ private LatestDiagnosticsForSpanGetter( CodeActionRequestPriority priority, bool cacheFullDocumentDiagnostics, bool logPerformanceInfo, - DiagnosticKinds diagnosticKinds) + DiagnosticKind diagnosticKind) { _owner = owner; _compilationWithAnalyzers = compilationWithAnalyzers; @@ -174,7 +174,7 @@ private LatestDiagnosticsForSpanGetter( _priority = priority; _cacheFullDocumentDiagnostics = cacheFullDocumentDiagnostics; _logPerformanceInfo = logPerformanceInfo; - _diagnosticKinds = diagnosticKinds; + _diagnosticKind = diagnosticKind; } public async Task TryGetAsync(ArrayBuilder list, CancellationToken cancellationToken) @@ -194,15 +194,15 @@ public async Task TryGetAsync(ArrayBuilder list, Cancellat continue; bool includeSyntax = true, includeSemantic = true; - if (_diagnosticKinds != DiagnosticKinds.All) + if (_diagnosticKind != DiagnosticKind.All) { var isCompilerAnalyzer = analyzer.IsCompilerAnalyzer(); includeSyntax = isCompilerAnalyzer - ? (_diagnosticKinds & DiagnosticKinds.CompilerSyntax) != 0 - : (_diagnosticKinds & DiagnosticKinds.AnalyzerSyntax) != 0; + ? _diagnosticKind == DiagnosticKind.CompilerSyntax + : _diagnosticKind == DiagnosticKind.AnalyzerSyntax; includeSemantic = isCompilerAnalyzer - ? (_diagnosticKinds & DiagnosticKinds.CompilerSemantic) != 0 - : (_diagnosticKinds & DiagnosticKinds.AnalyzerSemantic) != 0; + ? _diagnosticKind == DiagnosticKind.CompilerSemantic + : _diagnosticKind == DiagnosticKind.AnalyzerSemantic; } if (includeSyntax && !await TryAddCachedDocumentDiagnosticsAsync(stateSet, AnalysisKind.Syntax, list, cancellationToken).ConfigureAwait(false)) diff --git a/src/Features/VisualBasic/Portable/FullyQualify/VisualBasicFullyQualifyCodeFixProvider.vb b/src/Features/VisualBasic/Portable/FullyQualify/VisualBasicFullyQualifyCodeFixProvider.vb index 5d3b2831c7dfd..63fd5b2d398b3 100644 --- a/src/Features/VisualBasic/Portable/FullyQualify/VisualBasicFullyQualifyCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/FullyQualify/VisualBasicFullyQualifyCodeFixProvider.vb @@ -18,57 +18,48 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.FullyQualify Friend Class VisualBasicFullyQualifyCodeFixProvider - Inherits AbstractFullyQualifyCodeFixProvider + Inherits AbstractFullyQualifyCodeFixProvider(Of SimpleNameSyntax) ''' ''' Type xxx is not defined ''' - Friend Const BC30002 = "BC30002" + Private Const BC30002 = "BC30002" ''' ''' Error 'x' is not declared ''' - Friend Const BC30451 = "BC30451" + Private Const BC30451 = "BC30451" ''' ''' 'reference' is an ambiguous reference between 'identifier' and 'identifier' ''' - Friend Const BC30561 = "BC30561" + Private Const BC30561 = "BC30561" ''' ''' Namespace or type specified in imports cannot be found ''' - Friend Const BC40056 = "BC40056" + Private Const BC40056 = "BC40056" ''' ''' 'A' has no type parameters and so cannot have type arguments. ''' - Friend Const BC32045 = "BC32045" + Private Const BC32045 = "BC32045" Public Sub New() End Sub - Public Overrides ReadOnly Property FixableDiagnosticIds As ImmutableArray(Of String) - Get - Return ImmutableArray.Create(BC30002, IDEDiagnosticIds.UnboundIdentifierId, BC30451, BC30561, BC40056, BC32045) - End Get - End Property + Public Overrides ReadOnly Property FixableDiagnosticIds As ImmutableArray(Of String) = + ImmutableArray.Create(BC30002, IDEDiagnosticIds.UnboundIdentifierId, BC30451, BC30561, BC40056, BC32045) - Protected Overrides ReadOnly Property IgnoreCase As Boolean - Get - Return True - End Get - End Property - - Protected Overrides Function CanFullyQualify(diagnostic As Diagnostic, ByRef node As SyntaxNode) As Boolean + Protected Overrides Function CanFullyQualify(diagnostic As Diagnostic, node As SyntaxNode, ByRef simpleName As SimpleNameSyntax) As Boolean Dim qn = TryCast(node, QualifiedNameSyntax) If qn IsNot Nothing Then node = GetLeftMostSimpleName(qn) End If - Dim simpleName = TryCast(node, SimpleNameSyntax) + simpleName = TryCast(node, SimpleNameSyntax) If simpleName Is Nothing Then Return False End If @@ -98,15 +89,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.FullyQualify Return Nothing End Function - Protected Overrides Async Function ReplaceNodeAsync(node As SyntaxNode, containerName As String, resultingSymbolIsType As Boolean, cancellationToken As CancellationToken) As Task(Of SyntaxNode) - Dim simpleName = DirectCast(node, SimpleNameSyntax) - + Protected Overrides Async Function ReplaceNodeAsync(simpleName As SimpleNameSyntax, containerName As String, resultingSymbolIsType As Boolean, cancellationToken As CancellationToken) As Task(Of SyntaxNode) Dim leadingTrivia = simpleName.GetLeadingTrivia() Dim newName = simpleName.WithLeadingTrivia(CType(Nothing, SyntaxTriviaList)) - Dim qualifiedName = SyntaxFactory.QualifiedName(left:=SyntaxFactory.ParseName(containerName), right:=newName) - qualifiedName = qualifiedName.WithLeadingTrivia(leadingTrivia) - qualifiedName = qualifiedName.WithAdditionalAnnotations(Formatter.Annotation, CaseCorrector.Annotation) + Dim qualifiedName = SyntaxFactory.QualifiedName(left:=SyntaxFactory.ParseName(containerName), right:=newName). + WithLeadingTrivia(leadingTrivia). + WithAdditionalAnnotations(Formatter.Annotation, CaseCorrector.Annotation) Dim tree = simpleName.SyntaxTree Dim root = Await tree.GetRootAsync(cancellationToken).ConfigureAwait(False) diff --git a/src/VisualStudio/CSharp/Test/PersistentStorage/AbstractPersistentStorageTests.cs b/src/VisualStudio/CSharp/Test/PersistentStorage/AbstractPersistentStorageTests.cs index 60e1ed8d19548..9d7ee0129af82 100644 --- a/src/VisualStudio/CSharp/Test/PersistentStorage/AbstractPersistentStorageTests.cs +++ b/src/VisualStudio/CSharp/Test/PersistentStorage/AbstractPersistentStorageTests.cs @@ -10,12 +10,15 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Storage; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.VisualStudio.LanguageServices.UnitTests; using Roslyn.Test.Utilities; +using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.UnitTests.WorkspaceServices @@ -870,6 +873,46 @@ public async Task PersistentService_ReadByteTwice(Size size, bool withChecksum, } } + [Theory, CombinatorialData] + public async Task TestPersistSyntaxTreeIndex([CombinatorialRange(0, Iterations)] int iteration) + { + _ = iteration; + var solution = CreateOrOpenSolution(); + var id = DocumentId.CreateNewId(solution.Projects.Single().Id); + solution = solution.AddDocument(id, "file.cs", "class C { void M() }", filePath: @"c:\temp\file.cs"); + + var document = solution.GetRequiredDocument(id); + + await using (var storage = await GetStorageAsync(solution)) + { + var index = await SyntaxTreeIndex.GetRequiredIndexAsync(document, default); + await index.SaveAsync(_storageService!, document, default); + + var index2 = await SyntaxTreeIndex.LoadAsync(_storageService!, DocumentKey.ToDocumentKey(document), checksum: null, new StringTable(), default); + Assert.NotNull(index2); + } + } + + [Theory, CombinatorialData] + public async Task TestPersistTopLevelSyntaxTreeIndex([CombinatorialRange(0, Iterations)] int iteration) + { + _ = iteration; + var solution = CreateOrOpenSolution(); + var id = DocumentId.CreateNewId(solution.Projects.Single().Id); + solution = solution.AddDocument(id, "file.cs", "class C { void M() }", filePath: @"c:\temp\file.cs"); + + var document = solution.GetRequiredDocument(id); + + await using (var storage = await GetStorageAsync(solution)) + { + var index = await TopLevelSyntaxTreeIndex.GetRequiredIndexAsync(document, default); + await index.SaveAsync(_storageService!, document, default); + + var index2 = await TopLevelSyntaxTreeIndex.LoadAsync(_storageService!, DocumentKey.ToDocumentKey(document), checksum: null, new StringTable(), default); + Assert.NotNull(index2); + } + } + private static void DoSimultaneousReads(Func> read, string expectedValue) { var barrier = new Barrier(NumThreads); diff --git a/src/VisualStudio/Core/Test/Diagnostics/DefaultDiagnosticUpdateSourceTests.vb b/src/VisualStudio/Core/Test/Diagnostics/DefaultDiagnosticUpdateSourceTests.vb index 17615cb995ecb..443e66e4d4304 100644 --- a/src/VisualStudio/Core/Test/Diagnostics/DefaultDiagnosticUpdateSourceTests.vb +++ b/src/VisualStudio/Core/Test/Diagnostics/DefaultDiagnosticUpdateSourceTests.vb @@ -20,7 +20,7 @@ Imports Roslyn.Test.Utilities Imports Roslyn.Utilities Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics - <[UseExportProvider]> + Public Class DefaultDiagnosticUpdateSourceTests Private Shared ReadOnly s_compositionWithMockDiagnosticUpdateSourceRegistrationService As TestComposition = EditorTestCompositions.EditorFeatures _ .AddExcludedPartTypes(GetType(IDiagnosticUpdateSourceRegistrationService)) _ diff --git a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb index 8093dd308b37a..468a1210b68ea 100644 --- a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb +++ b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb @@ -667,7 +667,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub Reanalyze(workspace As Workspace, Optional projectIds As IEnumerable(Of ProjectId) = Nothing, Optional documentIds As IEnumerable(Of DocumentId) = Nothing, Optional highPriority As Boolean = False) Implements IDiagnosticAnalyzerService.Reanalyze End Sub - Public Function GetDiagnosticsForSpanAsync(document As TextDocument, range As TextSpan?, shouldIncludeDiagnostic As Func(Of String, Boolean), includeCompilerDiagnostics As Boolean, Optional includeSuppressedDiagnostics As Boolean = False, Optional priority As CodeActionRequestPriority = CodeActionRequestPriority.None, Optional addOperationScope As Func(Of String, IDisposable) = Nothing, Optional diagnosticKinds As DiagnosticKinds = DiagnosticKinds.All, Optional cancellationToken As CancellationToken = Nothing) As Task(Of ImmutableArray(Of DiagnosticData)) Implements IDiagnosticAnalyzerService.GetDiagnosticsForSpanAsync + Public Function GetDiagnosticsForSpanAsync(document As TextDocument, range As TextSpan?, shouldIncludeDiagnostic As Func(Of String, Boolean), includeCompilerDiagnostics As Boolean, Optional includeSuppressedDiagnostics As Boolean = False, Optional priority As CodeActionRequestPriority = CodeActionRequestPriority.None, Optional addOperationScope As Func(Of String, IDisposable) = Nothing, Optional diagnosticKinds As DiagnosticKind = DiagnosticKind.All, Optional cancellationToken As CancellationToken = Nothing) As Task(Of ImmutableArray(Of DiagnosticData)) Implements IDiagnosticAnalyzerService.GetDiagnosticsForSpanAsync Return SpecializedTasks.EmptyImmutableArray(Of DiagnosticData) End Function @@ -699,7 +699,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Throw New NotImplementedException() End Function - Public Function TryGetDiagnosticsForSpanAsync(document As TextDocument, range As TextSpan, shouldIncludeDiagnostic As Func(Of String, Boolean), Optional includeSuppressedDiagnostics As Boolean = False, Optional priority As CodeActionRequestPriority = CodeActionRequestPriority.None, Optional diagnosticKinds As DiagnosticKinds = DiagnosticKinds.All, Optional cancellationToken As CancellationToken = Nothing) As Task(Of (diagnostics As ImmutableArray(Of DiagnosticData), upToDate As Boolean)) Implements IDiagnosticAnalyzerService.TryGetDiagnosticsForSpanAsync + Public Function TryGetDiagnosticsForSpanAsync(document As TextDocument, range As TextSpan, shouldIncludeDiagnostic As Func(Of String, Boolean), Optional includeSuppressedDiagnostics As Boolean = False, Optional priority As CodeActionRequestPriority = CodeActionRequestPriority.None, Optional diagnosticKinds As DiagnosticKind = DiagnosticKind.All, Optional cancellationToken As CancellationToken = Nothing) As Task(Of (diagnostics As ImmutableArray(Of DiagnosticData), upToDate As Boolean)) Implements IDiagnosticAnalyzerService.TryGetDiagnosticsForSpanAsync Return Task.FromResult((ImmutableArray(Of DiagnosticData).Empty, False)) End Function End Class diff --git a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs index 1f5269be22c56..b918f11ed2b5d 100644 --- a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs +++ b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs @@ -302,7 +302,6 @@ private static bool IsInVarContext(NameSyntax name) { return name.CheckParent(v => v.Type == name) || - name.CheckParent(v => v.Type == name) || name.CheckParent(f => f.Type == name) || name.CheckParent(v => v.Type == name) || name.CheckParent(v => v.Type == name) || diff --git a/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs b/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs index 68cf6581698cf..f64de24fa331f 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs @@ -66,10 +66,13 @@ internal partial class AbstractSyntaxIndex : IObjectWritable // attempt to load from persisted state using var stream = await storage.ReadStreamAsync(documentKey, s_persistenceName, checksum, cancellationToken).ConfigureAwait(false); - using var gzipStream = new GZipStream(stream, CompressionMode.Decompress, leaveOpen: true); - using var reader = ObjectReader.TryGetReader(stream, cancellationToken: cancellationToken); - if (reader != null) - return read(stringTable, reader, checksum); + if (stream != null) + { + using var gzipStream = new GZipStream(stream, CompressionMode.Decompress, leaveOpen: true); + using var reader = ObjectReader.TryGetReader(gzipStream, cancellationToken: cancellationToken); + if (reader != null) + return read(stringTable, reader, checksum); + } } catch (Exception e) when (IOUtilities.IsNormalIOException(e)) { @@ -119,11 +122,18 @@ internal partial class AbstractSyntaxIndex : IObjectWritable return (textChecksum, textAndDirectivesChecksum); } - private async Task SaveAsync( + private Task SaveAsync( Document document, CancellationToken cancellationToken) { var solution = document.Project.Solution; var persistentStorageService = solution.Services.GetPersistentStorageService(); + return SaveAsync(persistentStorageService, document, cancellationToken); + } + + public async Task SaveAsync( + IChecksummedPersistentStorageService persistentStorageService, Document document, CancellationToken cancellationToken) + { + var solution = document.Project.Solution; try { diff --git a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage.cs b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage.cs index b8de1dbff14e1..f85bfc31ed333 100644 --- a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage.cs +++ b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage.cs @@ -45,6 +45,7 @@ internal sealed partial class SQLitePersistentStorage : AbstractPersistentStorag private readonly string _insert_into_string_table_values_0 = $"insert into {StringInfoTableName}({DataColumnName}) values (?)"; private readonly string _select_star_from_string_table_where_0_limit_one = $"select * from {StringInfoTableName} where ({DataColumnName} = ?) limit 1"; + private readonly string _select_star_from_string_table = $"select * from {StringInfoTableName}"; private SQLitePersistentStorage( SQLiteConnectionPoolService connectionPoolService, @@ -189,6 +190,12 @@ private void Initialize(SqlConnection connection, CancellationToken cancellation EnsureTables(connection, Database.Main); EnsureTables(connection, Database.WriteCache); + // Bulk load all the existing string/id pairs in the DB at once. In a solution like roslyn, there are + // roughly 20k of these strings. Doing it as 20k individual reads adds more than a second of work time + // reading in all the data. This allows for a single query that can efficiently have the DB just stream the + // pages from disk and bulk read those in the cursor the query uses. + LoadExistingStringIds(connection); + return; void EnsureTables(SqlConnection connection, Database database) diff --git a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_StringIds.cs b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_StringIds.cs index 3953bc59e664e..28be0940069bb 100644 --- a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_StringIds.cs +++ b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_StringIds.cs @@ -170,5 +170,29 @@ private int InsertStringIntoDatabase_MustRunInTransaction(SqlConnection connecti // So how could we then not find the string in the table? throw new InvalidOperationException(); } + + private void LoadExistingStringIds(SqlConnection connection) + { + try + { + using var resettableStatement = connection.GetResettableStatement(_select_star_from_string_table); + var statement = resettableStatement.Statement; + + Result stepResult; + while ((stepResult = statement.Step()) == Result.ROW) + { + var id = statement.GetInt32At(columnIndex: 0); + var value = statement.GetStringAt(columnIndex: 1); + _stringToIdMap.TryAdd(value, id); + } + } + catch (Exception ex) + { + // If we simply failed to even talk to the DB then we have to bail out. There's + // nothing we can accomplish at this point. + StorageDatabaseLogger.LogException(ex); + return; + } + } } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs index fa55c9f470955..8c156ed97e27d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs @@ -334,17 +334,10 @@ public static bool IsWrittenTo( // most cases of `ref x` will count as a potential write of `x`. An important exception is: // `ref readonly y = ref x`. In that case, because 'y' can't be written to, this would not // be a write of 'x'. - if (refParent.Parent is EqualsValueClauseSyntax { Parent: VariableDeclaratorSyntax { Parent: VariableDeclarationSyntax { Type: { } variableDeclarationType } } }) + if (refParent.Parent is EqualsValueClauseSyntax { Parent: VariableDeclaratorSyntax { Parent: VariableDeclarationSyntax { Type: RefTypeSyntax refType } } } + && refType.ReadOnlyKeyword != default) { - if (variableDeclarationType is ScopedTypeSyntax scopedType) - { - variableDeclarationType = scopedType.Type; - } - - if (variableDeclarationType is RefTypeSyntax refType && refType.ReadOnlyKeyword != default) - { - return false; - } + return false; } return true; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs index 5ce100d79990e..df763c1f698be 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs @@ -1083,22 +1083,13 @@ parameter2.Parent is ParameterListSyntax parameterList2 && return true; } - ParameterSyntax? parameter3 = null; - if (token.Kind() is SyntaxKind.RefKeyword or SyntaxKind.InKeyword or SyntaxKind.OutKeyword or SyntaxKind.ThisKeyword or SyntaxKind.ParamsKeyword or SyntaxKind.ScopedKeyword) - { - parameter3 = token.Parent as ParameterSyntax; - previousModifier = token.Kind(); - } - else if (token.IsKind(SyntaxKind.IdentifierToken) && token.Text == "scoped" && token.Parent is IdentifierNameSyntax scopedIdentifierName) - { - parameter3 = scopedIdentifierName.Parent as ParameterSyntax; - previousModifier = SyntaxKind.ScopedKeyword; - } - - if (parameter3 is { Parent: ParameterListSyntax parameterList3 } && + if (token.Kind() is SyntaxKind.RefKeyword or SyntaxKind.InKeyword or SyntaxKind.OutKeyword or SyntaxKind.ThisKeyword or SyntaxKind.ParamsKeyword && + token.Parent is ParameterSyntax parameter3 && + parameter3.Parent is ParameterListSyntax parameterList3 && parameterList3.IsDelegateOrConstructorOrLocalFunctionOrMethodOrOperatorParameterList(includeOperators)) { parameterIndex = parameterList3.Parameters.IndexOf(parameter3); + previousModifier = token.Kind(); return true; } @@ -1239,25 +1230,14 @@ public static bool IsAnonymousMethodParameterModifierContext( var token = tokenOnLeftOfPosition; token = token.GetPreviousTokenIfTouchingWord(position); - SyntaxNode? parent; - if (token.Kind() is SyntaxKind.OpenParenToken or SyntaxKind.CommaToken) - { - parent = token.Parent; - } - else if (token.IsKind(SyntaxKind.ScopedKeyword) && token.Parent.IsKind(SyntaxKind.Parameter)) - { - parent = token.Parent.Parent; - } - else if (token.IsKind(SyntaxKind.IdentifierToken) && token.Text == "scoped" && token.Parent is IdentifierNameSyntax scopedIdentifierName && scopedIdentifierName.Parent.IsKind(SyntaxKind.Parameter)) - { - parent = scopedIdentifierName.Parent.Parent; - } - else + if (token.Kind() is SyntaxKind.OpenParenToken or SyntaxKind.CommaToken && + token.Parent.IsKind(SyntaxKind.ParameterList) && + token.Parent.IsParentKind(SyntaxKind.AnonymousMethodExpression)) { - return false; + return true; } - return parent.IsKind(SyntaxKind.ParameterList) && parent.IsParentKind(SyntaxKind.AnonymousMethodExpression); + return false; } public static bool IsPossibleLambdaOrAnonymousMethodParameterTypeContext( @@ -1782,7 +1762,6 @@ public static bool IsLocalVariableDeclarationContext( // join var // using var // await using var - // scoped var var token = tokenOnLeftOfPosition.GetPreviousTokenIfTouchingWord(position); @@ -1865,23 +1844,6 @@ token.Parent is ArgumentSyntax argument && return true; } - // scoped | - // The compiler parses this as an identifier whose parent is: - // - ExpressionStatementSyntax when in method declaration. - // - IncompleteMemberSyntax when in top-level code and there are no class declarations after it. - // - BaseTypeDeclarationSyntax if it comes after scoped - // - VariableDeclarationSyntax for `scoped X` inside method declaration - if (token.IsKind(SyntaxKind.IdentifierToken) && token.Text == "scoped" && token.Parent.IsKind(SyntaxKind.IdentifierName) && token.Parent.Parent is VariableDeclarationSyntax or ExpressionStatementSyntax or IncompleteMemberSyntax) - { - return true; - } - - // scoped v| - if (token.IsKind(SyntaxKind.ScopedKeyword) && token.Parent is IncompleteMemberSyntax) - { - return true; - } - return false; }