diff --git a/eng/build.sh b/eng/build.sh index 56b795582c062..3dfb1abe19534 100755 --- a/eng/build.sh +++ b/eng/build.sh @@ -134,7 +134,7 @@ while [[ $# > 0 ]]; do # Bootstrap requires restore restore=true ;; - --runAnalyzers) + --runanalyzers) run_analyzers=true ;; --preparemachine) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 737db36bc298b..8b4f2f79ef5a2 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -6161,9 +6161,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ 'RefKind.Out' is not a valid ref kind for a return type. - Cannot convert a & method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. - Cannot convert & method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index bb399a899a306..b496637f10230 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -108,6 +108,8 @@ static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.BinaryPattern(Microsoft.CodeA static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ImplicitObjectCreationExpression() -> Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitObjectCreationExpressionSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ImplicitObjectCreationExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax argumentList, Microsoft.CodeAnalysis.CSharp.Syntax.InitializerExpressionSyntax initializer) -> Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitObjectCreationExpressionSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ImplicitObjectCreationExpression(Microsoft.CodeAnalysis.SyntaxToken newKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax argumentList, Microsoft.CodeAnalysis.CSharp.Syntax.InitializerExpressionSyntax initializer) -> Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitObjectCreationExpressionSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ParseTypeName(string text, int offset = 0, Microsoft.CodeAnalysis.CSharp.CSharpParseOptions options = null, bool consumeFullText = true) -> Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ParseTypeName(string text, int offset, bool consumeFullText) -> Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitFunctionPointerType(Microsoft.CodeAnalysis.CSharp.Syntax.FunctionPointerTypeSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitFunctionPointerType(Microsoft.CodeAnalysis.CSharp.Syntax.FunctionPointerTypeSyntax node) -> TResult static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ParenthesizedPattern(Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax pattern) -> Microsoft.CodeAnalysis.CSharp.Syntax.ParenthesizedPatternSyntax @@ -122,6 +124,7 @@ static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.UnaryPattern(Microsoft.CodeAn *REMOVED*Microsoft.CodeAnalysis.CSharp.Syntax.ObjectCreationExpressionSyntax.ArgumentList.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax *REMOVED*Microsoft.CodeAnalysis.CSharp.Syntax.ObjectCreationExpressionSyntax.Initializer.get -> Microsoft.CodeAnalysis.CSharp.Syntax.InitializerExpressionSyntax *REMOVED*Microsoft.CodeAnalysis.CSharp.Syntax.ObjectCreationExpressionSyntax.NewKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken +*REMOVED*static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ParseTypeName(string text, int offset = 0, bool consumeFullText = true) -> Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitBinaryPattern(Microsoft.CodeAnalysis.CSharp.Syntax.BinaryPatternSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitImplicitObjectCreationExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitObjectCreationExpressionSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitParenthesizedPattern(Microsoft.CodeAnalysis.CSharp.Syntax.ParenthesizedPatternSyntax node) -> void diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs index b0db58c46bfaa..f5cdcce077d8d 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs @@ -1693,9 +1693,19 @@ public static NameSyntax ParseName(string text, int offset = 0, bool consumeFull /// /// Parse a TypeNameSyntax node using the grammar rule for type names. /// - public static TypeSyntax ParseTypeName(string text, int offset = 0, bool consumeFullText = true) + // Backcompat overload, do not remove + [EditorBrowsable(EditorBrowsableState.Never)] + public static TypeSyntax ParseTypeName(string text, int offset, bool consumeFullText) { - using (var lexer = MakeLexer(text, offset)) + return ParseTypeName(text, offset, options: null, consumeFullText); + } + + /// + /// Parse a TypeNameSyntax node using the grammar rule for type names. + /// + public static TypeSyntax ParseTypeName(string text, int offset = 0, CSharpParseOptions? options = null, bool consumeFullText = true) + { + using (var lexer = MakeLexer(text, offset, options)) using (var parser = MakeParser(lexer)) { var node = parser.ParseTypeName(); diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index a1924c6ebf5a1..1a8004fc3f922 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -23,8 +23,8 @@ - Cannot convert & method group '{0}' to non-function pointer type '{1}'. - Cannot convert & method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. @@ -123,8 +123,8 @@ - Cannot convert a & method group '{0}' to delegate type '{0}'. - Cannot convert a & method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index d9a530e84eb07..03c3c6663f339 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -23,8 +23,8 @@ - Cannot convert & method group '{0}' to non-function pointer type '{1}'. - Cannot convert & method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. @@ -123,8 +123,8 @@ - Cannot convert a & method group '{0}' to delegate type '{0}'. - Cannot convert a & method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 632b32d3d72a5..b9c7f9bf939c1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -23,8 +23,8 @@ - Cannot convert & method group '{0}' to non-function pointer type '{1}'. - Cannot convert & method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. @@ -123,8 +123,8 @@ - Cannot convert a & method group '{0}' to delegate type '{0}'. - Cannot convert a & method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 9cd5d7f83c014..763f00a2cd940 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -23,8 +23,8 @@ - Cannot convert & method group '{0}' to non-function pointer type '{1}'. - Cannot convert & method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. @@ -123,8 +123,8 @@ - Cannot convert a & method group '{0}' to delegate type '{0}'. - Cannot convert a & method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index b21f9bcb5040d..63fbd921641ae 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -23,8 +23,8 @@ - Cannot convert & method group '{0}' to non-function pointer type '{1}'. - Cannot convert & method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. @@ -123,8 +123,8 @@ - Cannot convert a & method group '{0}' to delegate type '{0}'. - Cannot convert a & method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 20c77c2f6ab39..a1ed2032dc585 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -23,8 +23,8 @@ - Cannot convert & method group '{0}' to non-function pointer type '{1}'. - Cannot convert & method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. @@ -123,8 +123,8 @@ - Cannot convert a & method group '{0}' to delegate type '{0}'. - Cannot convert a & method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 2d014f4cf7182..ca1ef88e8b19a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -23,8 +23,8 @@ - Cannot convert & method group '{0}' to non-function pointer type '{1}'. - Cannot convert & method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. @@ -123,8 +123,8 @@ - Cannot convert a & method group '{0}' to delegate type '{0}'. - Cannot convert a & method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 8ba6cb43d24f7..c92dc56d99995 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -23,8 +23,8 @@ - Cannot convert & method group '{0}' to non-function pointer type '{1}'. - Cannot convert & method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. @@ -123,8 +123,8 @@ - Cannot convert a & method group '{0}' to delegate type '{0}'. - Cannot convert a & method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 99fb70350029a..94c8ad1e39ddc 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -23,8 +23,8 @@ - Cannot convert & method group '{0}' to non-function pointer type '{1}'. - Cannot convert & method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. @@ -123,8 +123,8 @@ - Cannot convert a & method group '{0}' to delegate type '{0}'. - Cannot convert a & method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 912a29e99895f..7a89322f9afe5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -23,8 +23,8 @@ - Cannot convert & method group '{0}' to non-function pointer type '{1}'. - Cannot convert & method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. @@ -123,8 +123,8 @@ - Cannot convert a & method group '{0}' to delegate type '{0}'. - Cannot convert a & method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 621451fb9231a..c3d8f5337bba6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -23,8 +23,8 @@ - Cannot convert & method group '{0}' to non-function pointer type '{1}'. - Cannot convert & method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. @@ -123,8 +123,8 @@ - Cannot convert a & method group '{0}' to delegate type '{0}'. - Cannot convert a & method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index e359cb495481e..63a9a0ec7a770 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -23,8 +23,8 @@ - Cannot convert & method group '{0}' to non-function pointer type '{1}'. - Cannot convert & method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. @@ -123,8 +123,8 @@ - Cannot convert a & method group '{0}' to delegate type '{0}'. - Cannot convert a & method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 2f2b8ba804289..46ed73372fee8 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -23,8 +23,8 @@ - Cannot convert & method group '{0}' to non-function pointer type '{1}'. - Cannot convert & method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. + Cannot convert &method group '{0}' to non-function pointer type '{1}'. @@ -123,8 +123,8 @@ - Cannot convert a & method group '{0}' to delegate type '{0}'. - Cannot convert a & method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. + Cannot convert &method group '{0}' to delegate type '{0}'. diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs index cce15a5950a60..00b9ffd547b0e 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs @@ -3670,10 +3670,10 @@ static void M() }"); comp.VerifyDiagnostics( - // (6,22): error CS8801: Cannot convert & method group 'M' to non-function pointer type 'void*'. + // (6,22): error CS8801: Cannot convert &method group 'M' to non-function pointer type 'void*'. // void* ptr1 = &M; Diagnostic(ErrorCode.ERR_AddressOfToNonFunctionPointer, "&M").WithArguments("M", "void*").WithLocation(6, 22), - // (7,22): error CS8801: Cannot convert & method group 'M' to non-function pointer type 'void*'. + // (7,22): error CS8801: Cannot convert &method group 'M' to non-function pointer type 'void*'. // void* ptr2 = (void*)&M; Diagnostic(ErrorCode.ERR_AddressOfToNonFunctionPointer, "(void*)&M").WithArguments("M", "void*").WithLocation(7, 22) ); @@ -3722,10 +3722,10 @@ unsafe void M() // (8,24): error CS0119: 'Action' is a type, which is not valid in the given context // Action ptr1 = (Action)&M; Diagnostic(ErrorCode.ERR_BadSKunknown, "Action").WithArguments("System.Action", "type").WithLocation(8, 24), - // (9,23): error CS8800: Cannot convert a & method group 'M' to delegate type 'M'. + // (9,23): error CS8800: Cannot convert &method group 'M' to delegate type 'M'. // Action ptr2 = (Action)(&M); Diagnostic(ErrorCode.ERR_CannotConvertAddressOfToDelegate, "(Action)(&M)").WithArguments("M", "System.Action").WithLocation(9, 23), - // (10,23): error CS8800: Cannot convert a & method group 'M' to delegate type 'M'. + // (10,23): error CS8800: Cannot convert &method group 'M' to delegate type 'M'. // Action ptr3 = &M; Diagnostic(ErrorCode.ERR_CannotConvertAddressOfToDelegate, "&M").WithArguments("M", "System.Action").WithLocation(10, 23) ); @@ -3774,10 +3774,10 @@ unsafe void M() // (7,19): error CS0119: 'C' is a type, which is not valid in the given context // C ptr1 = (C)&M; Diagnostic(ErrorCode.ERR_BadSKunknown, "C").WithArguments("C", "type").WithLocation(7, 19), - // (8,18): error CS8801: Cannot convert & method group 'M' to non-function pointer type 'C'. + // (8,18): error CS8801: Cannot convert &method group 'M' to non-function pointer type 'C'. // C ptr2 = (C)(&M); Diagnostic(ErrorCode.ERR_AddressOfToNonFunctionPointer, "(C)(&M)").WithArguments("M", "C").WithLocation(8, 18), - // (9,18): error CS8801: Cannot convert & method group 'M' to non-function pointer type 'C'. + // (9,18): error CS8801: Cannot convert &method group 'M' to non-function pointer type 'C'. // C ptr3 = &M; Diagnostic(ErrorCode.ERR_AddressOfToNonFunctionPointer, "&M").WithArguments("M", "C").WithLocation(9, 18) ); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs index 6a06bee1c0ffb..9802202ffe8e2 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs @@ -29,6 +29,28 @@ private CompilationVerifier CompileAndVerifyFunctionPointers(CSharpCompilation c return CompileAndVerify(compilation, verify: Verification.Skipped, expectedOutput: expectedOutput); } + [Fact] + public void UsingAliasTest() + { + var comp = CreateCompilationWithFunctionPointers(@" +using s = delegate*;"); + + comp.VerifyDiagnostics( + // (2,1): hidden CS8019: Unnecessary using directive. + // using s = delegate*; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using s = ").WithLocation(2, 1), + // (2,11): error CS1041: Identifier expected; 'delegate' is a keyword + // using s = delegate*; + Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "delegate").WithArguments("", "delegate").WithLocation(2, 11), + // (2,25): error CS0116: A namespace cannot directly contain members such as fields or methods + // using s = delegate*; + Diagnostic(ErrorCode.ERR_NamespaceUnexpected, ">").WithLocation(2, 25), + // (2,26): error CS1022: Type or namespace definition, or end-of-file expected + // using s = delegate*; + Diagnostic(ErrorCode.ERR_EOFExpected, ";").WithLocation(2, 26) + ); + } + [Fact] public void ImplicitConversionToVoid() { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs index 92f9545ced62a..d8ad314d4da38 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs @@ -3598,7 +3598,7 @@ enum Color // (40,15): error CS0211: Cannot take the address of the given expression // p = &(() => 1); //CS0211 Diagnostic(ErrorCode.ERR_InvalidAddrOp, "() => 1").WithLocation(40, 15), - // (41,13): error CS8801: Cannot convert & method group 'M' to non-function pointer type 'int*'. + // (41,13): error CS8801: Cannot convert &method group 'M' to non-function pointer type 'int*'. // p = &M; //CS0211 Diagnostic(ErrorCode.ERR_AddressOfToNonFunctionPointer, "&M").WithArguments("M", "int*").WithLocation(41, 13), // (42,15): error CS0211: Cannot take the address of the given expression diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/FunctionPointerTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/FunctionPointerTests.cs index 382022f27aef5..665c142977acf 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/FunctionPointerTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/FunctionPointerTests.cs @@ -3106,5 +3106,44 @@ public void FunctionPointerArrayInTypeArgument() } EOF(); } + + [Fact] + public void UsingAlias() + { + UsingNode("using t = delegate*;", options: TestOptions.RegularPreview, + // (1,11): error CS1041: Identifier expected; 'delegate' is a keyword + // using t = delegate*; + Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "delegate").WithArguments("", "delegate").WithLocation(1, 11), + // (1,25): error CS0116: A namespace cannot directly contain members such as fields or methods + // using t = delegate*; + Diagnostic(ErrorCode.ERR_NamespaceUnexpected, ">").WithLocation(1, 25), + // (1,26): error CS1022: Type or namespace definition, or end-of-file expected + // using t = delegate*; + Diagnostic(ErrorCode.ERR_EOFExpected, ";").WithLocation(1, 26) + ); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.UsingDirective); + { + N(SyntaxKind.UsingKeyword); + N(SyntaxKind.NameEquals); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.EqualsToken); + } + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxFactoryTests.cs b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxFactoryTests.cs index bac8d43fd3701..12bf83026ee57 100644 --- a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxFactoryTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxFactoryTests.cs @@ -588,5 +588,21 @@ public void TestParenthesizedLambdaNoParameterList_ExpressionBody() body: SyntaxFactory.LiteralExpression(SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(1))); Assert.Equal(fullySpecified.ToFullString(), lambda.ToFullString()); } + + [Fact] + public void TestParseNameWithOptions() + { + var type = "delegate*"; + + var parsedWith8 = SyntaxFactory.ParseTypeName(type, options: TestOptions.Regular8); + parsedWith8.GetDiagnostics().Verify( + // (1,1): error CS8652: The feature 'function pointers' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // delegate* + Diagnostic(ErrorCode.ERR_FeatureInPreview, "delegate*").WithArguments("function pointers").WithLocation(1, 1) + ); + + var parsedWithPreview = SyntaxFactory.ParseTypeName(type, options: TestOptions.RegularPreview); + parsedWithPreview.GetDiagnostics().Verify(); + } } } diff --git a/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt b/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt index 8b137891791fe..3be2c921b772c 100644 --- a/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt @@ -1 +1,3 @@ - +Shared Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory.ParseTypeName(text As String, offset As Integer = 0, options As Microsoft.CodeAnalysis.VisualBasic.VisualBasicParseOptions = Nothing, consumeFullText As Boolean = True) -> Microsoft.CodeAnalysis.VisualBasic.Syntax.TypeSyntax +Shared Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory.ParseTypeName(text As String, offset As Integer, consumeFullText As Boolean) -> Microsoft.CodeAnalysis.VisualBasic.Syntax.TypeSyntax +*REMOVED*Shared Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory.ParseTypeName(text As String, offset As Integer = 0, consumeFullText As Boolean = True) -> Microsoft.CodeAnalysis.VisualBasic.Syntax.TypeSyntax diff --git a/src/Compilers/VisualBasic/Portable/Syntax/SyntaxNodeFactories.vb b/src/Compilers/VisualBasic/Portable/Syntax/SyntaxNodeFactories.vb index 4a064d6849090..5b17efc0222db 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/SyntaxNodeFactories.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/SyntaxNodeFactories.vb @@ -15,6 +15,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.SyntaxFacts Imports InternalSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Imports Microsoft.CodeAnalysis.Syntax Imports System.Collections.Immutable +Imports System.ComponentModel Namespace Microsoft.CodeAnalysis.VisualBasic @@ -191,14 +192,25 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ''' ''' The input string ''' The starting offset in the string - Public Shared Function ParseTypeName(text As String, Optional offset As Integer = 0, Optional consumeFullText As Boolean = True) As TypeSyntax - Using p = New InternalSyntax.Parser(MakeSourceText(text, offset), VisualBasicParseOptions.Default) + Public Shared Function ParseTypeName(text As String, Optional offset As Integer = 0, Optional options As VisualBasicParseOptions = Nothing, Optional consumeFullText As Boolean = True) As TypeSyntax + Using p = New InternalSyntax.Parser(MakeSourceText(text, offset), If(options, VisualBasicParseOptions.Default)) p.GetNextToken() Dim node = p.ParseGeneralType() Return DirectCast(If(consumeFullText, p.ConsumeUnexpectedTokens(node), node).CreateRed(Nothing, 0), TypeSyntax) End Using End Function + '' Backcompat overload, do not touch + ''' + ''' Parse a type name. + ''' + ''' The input string + ''' The starting offset in the string + + Public Shared Function ParseTypeName(text As String, offset As Integer, consumeFullText As Boolean) As TypeSyntax + Return ParseTypeName(text, offset, options:=Nothing, consumeFullText) + End Function + ''' ''' Parse an expression. ''' diff --git a/src/Compilers/VisualBasic/Test/Syntax/Syntax/SyntaxFactoryTests.vb b/src/Compilers/VisualBasic/Test/Syntax/Syntax/SyntaxFactoryTests.vb index 0eb9346399c19..429e675ddb546 100644 --- a/src/Compilers/VisualBasic/Test/Syntax/Syntax/SyntaxFactoryTests.vb +++ b/src/Compilers/VisualBasic/Test/Syntax/Syntax/SyntaxFactoryTests.vb @@ -109,5 +109,22 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Assert.Equal(expected, literalMethod.Invoke(Nothing, {value}).ToString()) End Sub + + + Public Shared Sub TestParseTypeNameOptions() + Dim options As VisualBasicParseOptions = TestOptions.Regular + Dim code = " +#If Variable +String +#Else +Integer +#End If" + + Dim type1 = SyntaxFactory.ParseTypeName(code, options:=options.WithPreprocessorSymbols(New KeyValuePair(Of String, Object)("Variable", "True"))) + Assert.Equal("String", type1.ToString()) + + Dim type2 = SyntaxFactory.ParseTypeName(code, options:=options) + Assert.Equal("Integer", type2.ToString()) + End Sub End Class End Namespace diff --git a/src/EditorFeatures/CSharp/AutomaticCompletion/Sessions/LessAndGreaterThanCompletionSession.cs b/src/EditorFeatures/CSharp/AutomaticCompletion/Sessions/LessAndGreaterThanCompletionSession.cs index 0fec74e8a9c4c..efe914d0f9f85 100644 --- a/src/EditorFeatures/CSharp/AutomaticCompletion/Sessions/LessAndGreaterThanCompletionSession.cs +++ b/src/EditorFeatures/CSharp/AutomaticCompletion/Sessions/LessAndGreaterThanCompletionSession.cs @@ -35,6 +35,7 @@ public override bool CheckOpeningPoint(IBraceCompletionSession session, Cancella // type argument or parameter list if (!token.CheckParent(n => n.LessThanToken == token) && !token.CheckParent(n => n.LessThanToken == token) && + !token.CheckParent(n => n.LessThanToken == token) && !PossibleTypeArgument(snapshot, token, cancellationToken)) { return false; diff --git a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLessAndGreaterThanCompletionTests.cs b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLessAndGreaterThanCompletionTests.cs index da55d50df7de0..7eb9a06a509dd 100644 --- a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLessAndGreaterThanCompletionTests.cs +++ b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLessAndGreaterThanCompletionTests.cs @@ -392,6 +392,19 @@ public void Method() { } CheckStart(session.Session); } + [WpfFact, Trait(Traits.Feature, Traits.Features.AutomaticCompletion)] + public void FunctionPointerStartSession() + { + var code = @" +class C +{ + delegate*$$"; + + using var session = CreateSession(code); + Assert.NotNull(session); + CheckStart(session.Session); + } + internal Holder CreateSession(string code) { return CreateSession( diff --git a/src/EditorFeatures/CSharpTest/ExtractMethod/ExtractMethodTests.cs b/src/EditorFeatures/CSharpTest/ExtractMethod/ExtractMethodTests.cs index b3abe0fe98fe4..bcdc962400e3f 100644 --- a/src/EditorFeatures/CSharpTest/ExtractMethod/ExtractMethodTests.cs +++ b/src/EditorFeatures/CSharpTest/ExtractMethod/ExtractMethodTests.cs @@ -11109,17 +11109,17 @@ public async Task ExtractMethodInInterface() var code = @" interface Program { - void Foo(); + void Goo(); void Test() { - [|Foo();|] + [|Goo();|] } }"; var expected = @" interface Program { - void Foo(); + void Goo(); void Test() { @@ -11128,7 +11128,7 @@ void Test() void NewMethod() { - Foo(); + Goo(); } }"; await TestExtractMethodAsync(code, expected); @@ -11139,18 +11139,18 @@ void NewMethod() public async Task ExtractMethodInExpressionBodiedConstructors() { var code = @" -class Foo +class Goo { private readonly string _bar; - private Foo(string bar) => _bar = [|bar|]; + private Goo(string bar) => _bar = [|bar|]; }"; var expected = @" -class Foo +class Goo { private readonly string _bar; - private Foo(string bar) => _bar = GetBar(bar); + private Goo(string bar) => _bar = GetBar(bar); private static string GetBar(string bar) { @@ -11165,18 +11165,18 @@ private static string GetBar(string bar) public async Task ExtractMethodInExpressionBodiedFinalizers() { var code = @" -class Foo +class Goo { bool finalized; - ~Foo() => finalized = [|true|]; + ~Goo() => finalized = [|true|]; }"; var expected = @" -class Foo +class Goo { bool finalized; - ~Foo() => finalized = NewMethod(); + ~Goo() => finalized = NewMethod(); private static bool NewMethod() { @@ -11185,5 +11185,65 @@ private static bool NewMethod() }"; await TestExtractMethodAsync(code, expected); } + + [Fact, Trait(Traits.Feature, Traits.Features.ExtractMethod)] + public async Task ExtractMethodInvolvingFunctionPointer() + { + var code = @" +class C +{ + void M(delegate*> ptr1) + { + string s = null; + _ = [|ptr1()|](ref s); + } +}"; + + var expected = @" +class C +{ + void M(delegate*> ptr1) + { + string s = null; + _ = NewMethod(ptr1)(ref s); + } + + private static delegate* NewMethod(delegate*> ptr1) + { + return ptr1(); + } +}"; + + await TestExtractMethodAsync(code, expected); + } + + [Fact, Trait(Traits.Feature, Traits.Features.ExtractMethod)] + public async Task ExtractMethodInvolvingFunctionPointerWithTypeParameter() + { + var code = @" +class C +{ + void M(delegate* ptr1) + { + _ = [|ptr1|](); + } +}"; + + var expected = @" +class C +{ + void M(delegate* ptr1) + { + _ = GetPtr1(ptr1)(); + } + + private static delegate* GetPtr1(delegate* ptr1) + { + return ptr1; + } +}"; + + await TestExtractMethodAsync(code, expected); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/BoolKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/BoolKeywordRecommenderTests.cs index bf7bf2133a5df..91b826f74bf10 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/BoolKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/BoolKeywordRecommenderTests.cs @@ -716,5 +716,41 @@ void Method() } }"); } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestInFunctionPointerType() + { + await VerifyKeywordAsync(@" +class C +{ + delegate*<$$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestInFunctionPointerTypeAfterComma() + { + await VerifyKeywordAsync(@" +class C +{ + delegate* await VerifyAbsenceAsync(@"sealed $$"); [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotAfterStatic() - => await VerifyAbsenceAsync(@"static $$"); + public async Task TestAfterStatic() + { + await VerifyAbsenceAsync(SourceCodeKind.Regular, @"static $$"); + await VerifyKeywordAsync(SourceCodeKind.Script, @"static $$"); + } [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotAfterStaticPublic() - => await VerifyAbsenceAsync(@"static public $$"); + public async Task TestAfterStaticPublic() + { + await VerifyAbsenceAsync(SourceCodeKind.Regular, @"static public $$"); + await VerifyKeywordAsync(SourceCodeKind.Script, @"static public $$"); + } [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotAfterDelegate() - => await VerifyAbsenceAsync(@"delegate $$"); + public async Task TestAfterDelegate() + => await VerifyKeywordAsync(@"delegate $$"); [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestDelegateAsArgument() @@ -297,25 +312,25 @@ await VerifyKeywordAsync( [WorkItem(538804, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538804")] [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotInTypeOf() + public async Task TestInTypeOf() { - await VerifyAbsenceAsync(AddInsideMethod( + await VerifyKeywordAsync(AddInsideMethod( @"typeof($$")); } [WorkItem(538804, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538804")] [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotInDefault() + public async Task TestInDefault() { - await VerifyAbsenceAsync(AddInsideMethod( + await VerifyKeywordAsync(AddInsideMethod( @"default($$")); } [WorkItem(538804, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538804")] [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotInSizeOf() + public async Task TestInSizeOf() { - await VerifyAbsenceAsync(AddInsideMethod( + await VerifyKeywordAsync(AddInsideMethod( @"sizeof($$")); } @@ -345,15 +360,24 @@ void M() Action a = async $$"); } - [WorkItem(607197, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/607197")] [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotAfterAsyncInMemberDeclaration() + public async Task TestAfterAsyncInMemberDeclaration() { - await VerifyAbsenceAsync(@" + await VerifyKeywordAsync(@" using System; class C { async $$"); } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestInFunctionPointerTypeList() + { + await VerifyKeywordAsync(@" +using System; +class C +{ + delegate*<$$"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/DoubleKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/DoubleKeywordRecommenderTests.cs index 8980c66c24379..d46f9d7045d6c 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/DoubleKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/DoubleKeywordRecommenderTests.cs @@ -716,5 +716,41 @@ void Method() } }"); } + + [Fact] + public async Task TestInFunctionPointerType() + { + await VerifyKeywordAsync(@" +class Program +{ + delegate*<$$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestInFunctionPointerTypeAfterComma() + { + await VerifyKeywordAsync(@" +class C +{ + delegate* await VerifyAbsenceAsync(@"class c { async async $$ }"); + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestInFunctionPointerType() + { + await VerifyKeywordAsync(@" +class C +{ + delegate*<$$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestInFunctionPointerTypeAfterComma() + { + await VerifyKeywordAsync(@" +class C +{ + delegate* a) } }"); } + + [Fact] + [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestInFunctionPointer01() + { + await VerifyKeywordAsync(@" +class C +{ + delegate*<$$"); + } + + [Fact] + [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestInFunctionPointer02() + { + await VerifyKeywordAsync(@" +class C +{ + C()) || + context.IsFunctionPointerTypeArgumentContext || context.IsIsOrAsTypeContext || context.IsLocalVariableDeclarationContext || context.IsFixedVariableDeclarationContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ByteKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ByteKeywordRecommender.cs index 8dbf7de3c6521..b5081139e73cf 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ByteKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ByteKeywordRecommender.cs @@ -29,6 +29,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsGlobalStatementContext || context.IsObjectCreationTypeContext || (context.IsGenericTypeArgumentContext && !context.TargetToken.Parent.HasAncestor()) || + context.IsFunctionPointerTypeArgumentContext || context.IsEnumBaseListContext || context.IsIsOrAsTypeContext || context.IsLocalVariableDeclarationContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CharKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CharKeywordRecommender.cs index db432b9629afa..f39a27021420c 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CharKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CharKeywordRecommender.cs @@ -28,6 +28,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsGlobalStatementContext || context.IsObjectCreationTypeContext || (context.IsGenericTypeArgumentContext && !context.TargetToken.Parent.HasAncestor()) || + context.IsFunctionPointerTypeArgumentContext || context.IsIsOrAsTypeContext || context.IsLocalVariableDeclarationContext || context.IsFixedVariableDeclarationContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DecimalKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DecimalKeywordRecommender.cs index b8b32d1cb216f..df9ea614df9a3 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DecimalKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DecimalKeywordRecommender.cs @@ -28,6 +28,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsGlobalStatementContext || context.IsObjectCreationTypeContext || (context.IsGenericTypeArgumentContext && !context.TargetToken.Parent.HasAncestor()) || + context.IsFunctionPointerTypeArgumentContext || context.IsIsOrAsTypeContext || context.IsLocalVariableDeclarationContext || context.IsFixedVariableDeclarationContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DelegateKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DelegateKeywordRecommender.cs index b886fdc317d11..5aa496c15e460 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DelegateKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DelegateKeywordRecommender.cs @@ -30,13 +30,18 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context { return context.IsGlobalStatementContext || - (context.IsNonAttributeExpressionContext && !context.IsConstantExpressionContext) || + ValidTypeContext(context) || IsAfterAsyncKeywordInExpressionContext(context, cancellationToken) || context.IsTypeDeclarationContext( validModifiers: s_validModifiers, validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken); + + static bool ValidTypeContext(CSharpSyntaxContext context) + => (context.IsNonAttributeExpressionContext || context.IsTypeContext) + && !context.IsConstantExpressionContext + && !context.LeftToken.IsTopLevelOfUsingAliasDirective(); } private static bool IsAfterAsyncKeywordInExpressionContext(CSharpSyntaxContext context, CancellationToken cancellationToken) diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DoubleKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DoubleKeywordRecommender.cs index 231cb1cdabb6c..c3a3bc1b062af 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DoubleKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DoubleKeywordRecommender.cs @@ -28,6 +28,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsGlobalStatementContext || context.IsObjectCreationTypeContext || (context.IsGenericTypeArgumentContext && !context.TargetToken.Parent.HasAncestor()) || + context.IsFunctionPointerTypeArgumentContext || context.IsIsOrAsTypeContext || context.IsLocalVariableDeclarationContext || context.IsFixedVariableDeclarationContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DynamicKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DynamicKeywordRecommender.cs index 62e20da2ca7a2..6d20d03968bf9 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DynamicKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DynamicKeywordRecommender.cs @@ -53,6 +53,7 @@ protected static bool IsDynamicTypeContext( syntaxTree.IsPossibleCastTypeContext(position, context.LeftToken, cancellationToken) || context.IsObjectCreationTypeContext || context.IsGenericTypeArgumentContext || + context.IsFunctionPointerTypeArgumentContext || context.IsIsOrAsTypeContext || syntaxTree.IsDefaultExpressionContext(position, context.LeftToken) || syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FloatKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FloatKeywordRecommender.cs index f48d39680e2c2..fd53561cc484e 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FloatKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FloatKeywordRecommender.cs @@ -28,6 +28,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsGlobalStatementContext || context.IsObjectCreationTypeContext || (context.IsGenericTypeArgumentContext && !context.TargetToken.Parent.HasAncestor()) || + context.IsFunctionPointerTypeArgumentContext || context.IsIsOrAsTypeContext || context.IsLocalVariableDeclarationContext || context.IsFixedVariableDeclarationContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/GlobalKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/GlobalKeywordRecommender.cs index 928b3bb852378..c34583e01b4f0 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/GlobalKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/GlobalKeywordRecommender.cs @@ -37,6 +37,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context 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/IntKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IntKeywordRecommender.cs index 57e0242b0b096..c5339a86c4ef1 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IntKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IntKeywordRecommender.cs @@ -28,6 +28,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsGlobalStatementContext || context.IsObjectCreationTypeContext || (context.IsGenericTypeArgumentContext && !context.TargetToken.Parent.HasAncestor()) || + context.IsFunctionPointerTypeArgumentContext || context.IsEnumBaseListContext || context.IsIsOrAsTypeContext || context.IsLocalVariableDeclarationContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LongKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LongKeywordRecommender.cs index 2ff8ac4878d6f..92d34e3becdba 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LongKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LongKeywordRecommender.cs @@ -28,6 +28,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsGlobalStatementContext || context.IsObjectCreationTypeContext || (context.IsGenericTypeArgumentContext && !context.TargetToken.Parent.HasAncestor()) || + context.IsFunctionPointerTypeArgumentContext || context.IsEnumBaseListContext || context.IsIsOrAsTypeContext || context.IsLocalVariableDeclarationContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ObjectKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ObjectKeywordRecommender.cs index 4479016e01d4f..d469abd5acbb3 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ObjectKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ObjectKeywordRecommender.cs @@ -28,6 +28,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsGlobalStatementContext || context.IsObjectCreationTypeContext || (context.IsGenericTypeArgumentContext && !context.TargetToken.Parent.HasAncestor()) || + context.IsFunctionPointerTypeArgumentContext || context.IsIsOrAsTypeContext || context.IsLocalVariableDeclarationContext || context.IsParameterTypeContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ReadOnlyKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ReadOnlyKeywordRecommender.cs index c9399497b6609..56ca9f17bd512 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ReadOnlyKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ReadOnlyKeywordRecommender.cs @@ -42,7 +42,8 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context private static bool IsRefReadOnlyContext(CSharpSyntaxContext context) => context.TargetToken.IsKind(SyntaxKind.RefKeyword) && - context.TargetToken.Parent.IsKind(SyntaxKind.RefType); + (context.TargetToken.Parent.IsKind(SyntaxKind.RefType) || + (context.TargetToken.Parent.IsKind(SyntaxKind.Parameter) && context.IsFunctionPointerTypeArgumentContext)); private static bool IsValidContextForType(CSharpSyntaxContext context, CancellationToken cancellationToken) { diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SByteKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SByteKeywordRecommender.cs index dcc3a37da2b21..a9b60e470b6ef 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SByteKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SByteKeywordRecommender.cs @@ -28,6 +28,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsGlobalStatementContext || context.IsObjectCreationTypeContext || (context.IsGenericTypeArgumentContext && !context.TargetToken.Parent.HasAncestor()) || + context.IsFunctionPointerTypeArgumentContext || context.IsEnumBaseListContext || context.IsIsOrAsTypeContext || context.IsLocalVariableDeclarationContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ShortKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ShortKeywordRecommender.cs index c7d29756fc646..99583e36c6920 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ShortKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ShortKeywordRecommender.cs @@ -28,6 +28,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsGlobalStatementContext || context.IsObjectCreationTypeContext || (context.IsGenericTypeArgumentContext && !context.TargetToken.Parent.HasAncestor()) || + context.IsFunctionPointerTypeArgumentContext || context.IsEnumBaseListContext || context.IsIsOrAsTypeContext || context.IsLocalVariableDeclarationContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StringKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StringKeywordRecommender.cs index 1f9f1a9492010..426356918afd9 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StringKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StringKeywordRecommender.cs @@ -28,6 +28,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsGlobalStatementContext || context.IsObjectCreationTypeContext || (context.IsGenericTypeArgumentContext && !context.TargetToken.Parent.HasAncestor()) || + context.IsFunctionPointerTypeArgumentContext || context.IsIsOrAsTypeContext || context.IsLocalVariableDeclarationContext || context.IsParameterTypeContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UIntKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UIntKeywordRecommender.cs index 2ce88ad7a9cfd..b2c9399b27e2a 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UIntKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UIntKeywordRecommender.cs @@ -28,6 +28,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsGlobalStatementContext || context.IsObjectCreationTypeContext || (context.IsGenericTypeArgumentContext && !context.TargetToken.Parent.HasAncestor()) || + context.IsFunctionPointerTypeArgumentContext || context.IsEnumBaseListContext || context.IsIsOrAsTypeContext || context.IsLocalVariableDeclarationContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ULongKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ULongKeywordRecommender.cs index cd604bffd5304..46d37b1f13c3e 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ULongKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ULongKeywordRecommender.cs @@ -28,6 +28,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsGlobalStatementContext || context.IsObjectCreationTypeContext || (context.IsGenericTypeArgumentContext && !context.TargetToken.Parent.HasAncestor()) || + context.IsFunctionPointerTypeArgumentContext || context.IsEnumBaseListContext || context.IsIsOrAsTypeContext || context.IsLocalVariableDeclarationContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UShortKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UShortKeywordRecommender.cs index 9f544c325f9ff..40d5c34ab1c5f 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UShortKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UShortKeywordRecommender.cs @@ -28,6 +28,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsGlobalStatementContext || context.IsObjectCreationTypeContext || (context.IsGenericTypeArgumentContext && !context.TargetToken.Parent.HasAncestor()) || + context.IsFunctionPointerTypeArgumentContext || context.IsEnumBaseListContext || context.IsIsOrAsTypeContext || context.IsLocalVariableDeclarationContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/VoidKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/VoidKeywordRecommender.cs index b71d225e8c693..669b793586557 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/VoidKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/VoidKeywordRecommender.cs @@ -43,6 +43,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsTypeOfExpressionContext || syntaxTree.IsSizeOfExpressionContext(position, context.LeftToken) || context.IsDelegateReturnTypeContext || + context.IsFunctionPointerTypeArgumentContext || IsUnsafeLocalVariableDeclarationContext(context) || IsUnsafeParameterTypeContext(context) || IsUnsafeCastTypeContext(context) || diff --git a/src/Features/Core/Portable/ExtractMethod/MethodExtractor.TypeParameterCollector.cs b/src/Features/Core/Portable/ExtractMethod/MethodExtractor.TypeParameterCollector.cs index bf7c8639091f3..0cbdef5ec3ed0 100644 --- a/src/Features/Core/Portable/ExtractMethod/MethodExtractor.TypeParameterCollector.cs +++ b/src/Features/Core/Portable/ExtractMethod/MethodExtractor.TypeParameterCollector.cs @@ -29,6 +29,15 @@ public override void VisitDynamicType(IDynamicTypeSymbol dynamicTypeSymbol) { } + public override void VisitFunctionPointerType(IFunctionPointerTypeSymbol symbol) + { + symbol.Signature.ReturnType.Accept(this); + foreach (var param in symbol.Signature.Parameters) + { + param.Type.Accept(this); + } + } + public override void VisitArrayType(IArrayTypeSymbol arrayTypeSymbol) => arrayTypeSymbol.ElementType.Accept(this); diff --git a/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs b/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs index c9957ea9c5baf..071b9cee1ef01 100644 --- a/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs +++ b/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs @@ -129,6 +129,9 @@ public static Glyph GetGlyph(this ISymbol symbol) case SymbolKind.PointerType: return ((IPointerTypeSymbol)symbol).PointedAtType.GetGlyph(); + case SymbolKind.FunctionPointer: + return Glyph.Intrinsic; + case SymbolKind.Property: { var propertySymbol = (IPropertySymbol)symbol; diff --git a/src/VisualStudio/CSharp/Impl/ChangeSignature/CSharpChangeSignatureViewModelFactoryService.cs b/src/VisualStudio/CSharp/Impl/ChangeSignature/CSharpChangeSignatureViewModelFactoryService.cs index b6bdedfb1bb1d..cc8873e430c61 100644 --- a/src/VisualStudio/CSharp/Impl/ChangeSignature/CSharpChangeSignatureViewModelFactoryService.cs +++ b/src/VisualStudio/CSharp/Impl/ChangeSignature/CSharpChangeSignatureViewModelFactoryService.cs @@ -16,6 +16,8 @@ namespace Microsoft.VisualStudio.LanguageServices.CSharp.ChangeSignature [ExportLanguageService(typeof(IChangeSignatureViewModelFactoryService), LanguageNames.CSharp), Shared] internal class CSharpChangeSignatureViewModelFactoryService : ChangeSignatureViewModelFactoryService { + private static readonly CSharpParseOptions s_langVersionLatestParseOptions = new CSharpParseOptions(LanguageVersion.Preview); + [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public CSharpChangeSignatureViewModelFactoryService() @@ -44,7 +46,9 @@ public override SymbolDisplayPart[] GeneratePreviewDisplayParts(AddedParameterVi return parts.ToArray(); } - public override bool IsTypeNameValid(string typeName) => !SyntaxFactory.ParseTypeName(typeName).ContainsDiagnostics; + // Use LangVersion Preview to ensure that all types parse correctly. If the user types in a type only available + // in a preview version, they'll get a diagnostic after everything is generated + public override bool IsTypeNameValid(string typeName) => !SyntaxFactory.ParseTypeName(typeName, options: s_langVersionLatestParseOptions).ContainsDiagnostics; public override SyntaxNode GetTypeNode(string typeName) => SyntaxFactory.ParseTypeName(typeName); } diff --git a/src/VisualStudio/VisualBasic/Impl/ChangeSignature/VisualBasicChangeSignatureViewModelFactoryService.vb b/src/VisualStudio/VisualBasic/Impl/ChangeSignature/VisualBasicChangeSignatureViewModelFactoryService.vb index a232641052967..52b2a5fb64948 100644 --- a/src/VisualStudio/VisualBasic/Impl/ChangeSignature/VisualBasicChangeSignatureViewModelFactoryService.vb +++ b/src/VisualStudio/VisualBasic/Impl/ChangeSignature/VisualBasicChangeSignatureViewModelFactoryService.vb @@ -41,7 +41,8 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.ChangeSignature End Function Public Overrides Function IsTypeNameValid(typeName As String) As Boolean - Return Not SyntaxFactory.ParseTypeName(typeName).ContainsDiagnostics + Static visualBasicParseOptions As New VisualBasicParseOptions(LanguageVersion.Latest) + Return Not SyntaxFactory.ParseTypeName(typeName, options:=visualBasicParseOptions).ContainsDiagnostics End Function Public Overrides Function GetTypeNode(typeName As String) As SyntaxNode diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs index 151186d7377e7..bbf110a00e076 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs @@ -200,13 +200,17 @@ public override SyntaxNode ParameterDeclaration(string name, SyntaxNode type, Sy initializer != null ? SyntaxFactory.EqualsValueClause((ExpressionSyntax)initializer) : null); } - internal static SyntaxTokenList GetParameterModifiers(RefKind refKind) + internal static SyntaxTokenList GetParameterModifiers(RefKind refKind, bool forFunctionPointerReturnParameter = false) => refKind switch { RefKind.None => new SyntaxTokenList(), RefKind.Out => SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.OutKeyword)), RefKind.Ref => SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.RefKeyword)), - RefKind.In => SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.InKeyword)), + // Note: RefKind.RefReadonly == RefKind.In. Function Pointers must use the correct + // ref kind syntax when generating for the return parameter vs other parameters. + // The return parameter must use ref readonly, like regular methods. + RefKind.In when !forFunctionPointerReturnParameter => SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.InKeyword)), + RefKind.RefReadOnly when forFunctionPointerReturnParameter => SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.RefKeyword), SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)), _ => throw ExceptionUtilities.UnexpectedValue(refKind), }; diff --git a/src/Workspaces/CSharpTest/Formatting/FormattingTests_FunctionPointers.cs b/src/Workspaces/CSharpTest/Formatting/FormattingTests_FunctionPointers.cs index 8f0d8f4ea71a4..7f9bc9ed9bab1 100644 --- a/src/Workspaces/CSharpTest/Formatting/FormattingTests_FunctionPointers.cs +++ b/src/Workspaces/CSharpTest/Formatting/FormattingTests_FunctionPointers.cs @@ -29,5 +29,24 @@ unsafe class C await AssertFormatAsync(expected, content); } + + [Fact] + public async Task FormatFunctionPointerWithCallingConvention() + { + // TODO(https://github.com/dotnet/roslyn/issues/44312): add a space after the "int"s in the baseline and make this test still pass + var content = @" +unsafe class C +{ + delegate *cdecl < int, int> functionPointer; +}"; + + var expected = @" +unsafe class C +{ + delegate* cdecl functionPointer; +}"; + + await AssertFormatAsync(expected, content); + } } } diff --git a/src/Workspaces/Core/Portable/SymbolKey/SymbolKey.FunctionPointerTypeSymbolKey.cs b/src/Workspaces/Core/Portable/SymbolKey/SymbolKey.FunctionPointerTypeSymbolKey.cs new file mode 100644 index 0000000000000..31e9b64f12fc3 --- /dev/null +++ b/src/Workspaces/Core/Portable/SymbolKey/SymbolKey.FunctionPointerTypeSymbolKey.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis +{ + internal partial struct SymbolKey + { + private static class FunctionPointerTypeSymbolKey + { + public static void Create(IFunctionPointerTypeSymbol symbol, SymbolKeyWriter visitor) + { + visitor.WriteRefKind(symbol.Signature.RefKind); + visitor.WriteSymbolKey(symbol.Signature.ReturnType); + visitor.WriteRefKindArray(symbol.Signature.Parameters); + visitor.WriteParameterTypesArray(symbol.Signature.Parameters); + } + + public static SymbolKeyResolution Resolve(SymbolKeyReader reader) + { + var returnRefKind = reader.ReadRefKind(); + var returnType = reader.ReadSymbolKey(); + using var paramRefKinds = reader.ReadRefKindArray(); + using var paramTypes = reader.ReadSymbolKeyArray(); + + if (paramTypes.IsDefault || !(returnType.GetAnySymbol() is ITypeSymbol returnTypeSymbol)) + { + return default; + } + + return new SymbolKeyResolution(reader.Compilation.CreateFunctionPointerTypeSymbol( + returnTypeSymbol, returnRefKind, paramTypes.ToImmutable(), paramRefKinds.ToImmutable())); + } + } + } +} diff --git a/src/Workspaces/Core/Portable/SymbolKey/SymbolKey.SymbolKeyReader.cs b/src/Workspaces/Core/Portable/SymbolKey/SymbolKey.SymbolKeyReader.cs index cbf4cdc0388b0..0249a8db8a077 100644 --- a/src/Workspaces/Core/Portable/SymbolKey/SymbolKey.SymbolKeyReader.cs +++ b/src/Workspaces/Core/Portable/SymbolKey/SymbolKey.SymbolKeyReader.cs @@ -38,7 +38,7 @@ public Reader() { _readString = ReadString; _readBoolean = ReadBoolean; - _readRefKind = () => (RefKind)ReadInteger(); + _readRefKind = ReadRefKind; } protected virtual void Initialize(string data, CancellationToken cancellationToken) @@ -192,6 +192,11 @@ public PooledArrayBuilder ReadArray(Func readFunction) EatCloseParen(); return builder; } + + public RefKind ReadRefKind() + { + return (RefKind)ReadInteger(); + } } private class RemoveAssemblySymbolKeysReader : Reader @@ -418,6 +423,7 @@ private SymbolKeyResolution ReadWorker(SymbolKeyType type) SymbolKeyType.NamedType => NamedTypeSymbolKey.Resolve(this), SymbolKeyType.ErrorType => ErrorTypeSymbolKey.Resolve(this), SymbolKeyType.Field => FieldSymbolKey.Resolve(this), + SymbolKeyType.FunctionPointer => FunctionPointerTypeSymbolKey.Resolve(this), SymbolKeyType.DynamicType => DynamicTypeSymbolKey.Resolve(this), SymbolKeyType.Method => MethodSymbolKey.Resolve(this), SymbolKeyType.Namespace => NamespaceSymbolKey.Resolve(this), diff --git a/src/Workspaces/Core/Portable/SymbolKey/SymbolKey.SymbolKeyWriter.cs b/src/Workspaces/Core/Portable/SymbolKey/SymbolKey.SymbolKeyWriter.cs index d644ddf0c0714..c8d40ca3387dc 100644 --- a/src/Workspaces/Core/Portable/SymbolKey/SymbolKey.SymbolKeyWriter.cs +++ b/src/Workspaces/Core/Portable/SymbolKey/SymbolKey.SymbolKeyWriter.cs @@ -25,6 +25,7 @@ private enum SymbolKeyType NamedType = 'D', ErrorType = 'E', Field = 'F', + FunctionPointer = 'G', DynamicType = 'I', Method = 'M', Namespace = 'N', @@ -76,7 +77,7 @@ public SymbolKeyWriter() _writeLocation = WriteLocation; _writeBoolean = WriteBoolean; _writeParameterType = p => WriteSymbolKey(p.Type); - _writeRefKind = p => WriteInteger((int)p.RefKind); + _writeRefKind = p => WriteRefKind(p.RefKind); } public void Dispose() @@ -298,6 +299,8 @@ private void WriteArray(ImmutableArray array, Action writeValue) EndKey(); } + internal void WriteRefKind(RefKind refKind) => WriteInteger((int)refKind); + public override object VisitAlias(IAliasSymbol aliasSymbol) { WriteType(SymbolKeyType.Alias); @@ -455,6 +458,13 @@ public override object VisitPointerType(IPointerTypeSymbol pointerTypeSymbol) return null; } + public override object VisitFunctionPointerType(IFunctionPointerTypeSymbol symbol) + { + WriteType(SymbolKeyType.FunctionPointer); + FunctionPointerTypeSymbolKey.Create(symbol, this); + return null; + } + public override object VisitProperty(IPropertySymbol propertySymbol) { WriteType(SymbolKeyType.Property); diff --git a/src/Workspaces/CoreTest/SymbolKeyTests.cs b/src/Workspaces/CoreTest/SymbolKeyTests.cs index b9419cef5e785..c8df9a6ab88da 100644 --- a/src/Workspaces/CoreTest/SymbolKeyTests.cs +++ b/src/Workspaces/CoreTest/SymbolKeyTests.cs @@ -48,6 +48,7 @@ public class B { }; public B this[B b] { get { return b; } } public event D E; public event D E2 { add; remove; } + public delegate* Ptr; } "; var compilation = GetCompilation(source, LanguageNames.CSharp); @@ -783,6 +784,21 @@ void Method((C a, int b) t) Assert.True(method.Parameters[0].Type.IsTupleType); } + [Fact] + public void TestFunctionPointerTypeSymbols() + { + var source = @" +class C +{ + public delegate* ptr1; + public delegate* ptr1; +}"; + + var comp = GetCompilation(source, LanguageNames.CSharp); + var fields = GetDeclaredSymbols(comp).OfType().Select(f => f.Type); + TestRoundTrip(fields, comp); + } + private void TestRoundTrip(IEnumerable symbols, Compilation compilation, Func fnId = null) { foreach (var symbol in symbols) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs index 6e38b9ac5a9e4..d22af9db817e3 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs @@ -381,6 +381,7 @@ private static bool CanReplace(ISymbol symbol) case SymbolKind.Parameter: case SymbolKind.Property: case SymbolKind.RangeVariable: + case SymbolKindEx.FunctionPointer: return true; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxNodeExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxNodeExtensions.cs index 56761202f745e..7f8c9f893db62 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxNodeExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxNodeExtensions.cs @@ -1031,6 +1031,14 @@ public static bool IsInDeconstructionLeft( return false; } + public static bool IsTopLevelOfUsingAliasDirective(this SyntaxToken node) + => node switch + { + { Parent: NameEqualsSyntax { Parent: UsingDirectiveSyntax _ } } => true, + { Parent: IdentifierNameSyntax { Parent: UsingDirectiveSyntax _ } } => true, + _ => false + }; + public static T WithCommentsFrom(this T node, SyntaxToken leadingToken, SyntaxToken trailingToken) where T : SyntaxNode => node.WithCommentsFrom( diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxTreeExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxTreeExtensions.cs index 39f1a18d7ea38..e49297856326d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxTreeExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxTreeExtensions.cs @@ -51,6 +51,8 @@ public static ISet GetPrecedingModifiers( case SyntaxKind.UnsafeKeyword: case SyntaxKind.AsyncKeyword: case SyntaxKind.RefKeyword: + case SyntaxKind.OutKeyword: + case SyntaxKind.InKeyword: result.Add(token.Kind()); token = token.GetPreviousToken(includeSkipped: true); continue; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SpacingFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SpacingFormattingRule.cs index f9f13a6eb6957..6ce31e81cb688 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SpacingFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SpacingFormattingRule.cs @@ -320,6 +320,35 @@ previousToken.Parent is AssignmentExpressionSyntax || } } + // Function pointer type adjustments + if (previousParentKind == SyntaxKindEx.FunctionPointerType && currentParentKind == SyntaxKindEx.FunctionPointerType) + { + // No spacing between delegate and * + if (currentKind == SyntaxKind.AsteriskToken && previousKind == SyntaxKind.DelegateKeyword) + { + return CreateAdjustSpacesOperation(0, AdjustSpacesOption.ForceSpacesIfOnSingleLine); + } + + // Force a space between * and the calling convention + if (currentKind == SyntaxKind.IdentifierToken && previousKind == SyntaxKind.AsteriskToken) + { + return CreateAdjustSpacesOperation(1, AdjustSpacesOption.ForceSpacesIfOnSingleLine); + } + + if (currentKind == SyntaxKind.LessThanToken) + { + switch (previousKind) + { + // No spacing between the * and < tokens if there is no calling convention + case SyntaxKind.AsteriskToken: + // No spacing between the calling convention and opening angle bracket of function pointer types: + // delegate* cdecl< + case SyntaxKind.IdentifierToken: + return CreateAdjustSpacesOperation(0, AdjustSpacesOption.ForceSpacesIfOnSingleLine); + } + } + } + // For spacing after the 'not' pattern operator if (previousToken.Parent.IsKind(SyntaxKindEx.NotPattern)) { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/TokenBasedFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/TokenBasedFormattingRule.cs index f3445d8632fb9..d0a8d6aa8bc2f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/TokenBasedFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/TokenBasedFormattingRule.cs @@ -516,13 +516,6 @@ private static SyntaxList GetUsings(SyntaxNode node) return CreateAdjustSpacesOperation(0, AdjustSpacesOption.ForceSpacesIfOnSingleLine); } - // no spaces around the * in a function pointer - if ((currentToken.IsKind(SyntaxKind.AsteriskToken) && currentToken.Parent.IsKind(SyntaxKindEx.FunctionPointerType)) || - (previousToken.IsKind(SyntaxKind.AsteriskToken) && previousToken.Parent.IsKind(SyntaxKindEx.FunctionPointerType))) - { - return CreateAdjustSpacesOperation(0, AdjustSpacesOption.ForceSpacesIfOnSingleLine); - } - // ~ * case if (previousToken.Kind() == SyntaxKind.TildeToken && (previousToken.Parent is PrefixUnaryExpressionSyntax || previousToken.Parent is DestructorDeclarationSyntax)) { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems index 930bb9ff5e7ec..ad62c07b78fd2 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems @@ -190,7 +190,9 @@ + + diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions_Accessibility.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions_Accessibility.cs index 24a578c5a220e..69bb90e8bcc85 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions_Accessibility.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions_Accessibility.cs @@ -103,6 +103,27 @@ private static bool IsSymbolAccessibleCore( case SymbolKind.PointerType: return IsSymbolAccessibleCore(((IPointerTypeSymbol)symbol).PointedAtType, within, null, out failedThroughTypeCheck); + case SymbolKindEx.FunctionPointer: +#if !CODE_STYLE + var funcPtrSignature = ((IFunctionPointerTypeSymbol)symbol).Signature; + if (!IsSymbolAccessibleCore(funcPtrSignature.ReturnType, within, null, out failedThroughTypeCheck)) + { + return false; + } + + foreach (var param in funcPtrSignature.Parameters) + { + if (!IsSymbolAccessibleCore(param.Type, within, null, out failedThroughTypeCheck)) + { + return false; + } + } + + return true; +#else + return false; +#endif + case SymbolKind.NamedType: return IsNamedTypeAccessible((INamedTypeSymbol)symbol, within); @@ -143,6 +164,11 @@ private static bool IsSymbolAccessibleCore( } // If it's a synthesized operator on a pointer, use the pointer's PointedAtType. + // Note: there are currently no synthesized operators on function pointer types. If that + // ever changes, updated the below assert and fix the code +#if !CODE_STYLE + Debug.Assert(!(symbol.IsKind(SymbolKind.Method) && ((IMethodSymbol)symbol).MethodKind == MethodKind.BuiltinOperator && symbol.ContainingSymbol.IsKind(SymbolKind.FunctionPointer))); +#endif if (symbol.IsKind(SymbolKind.Method) && ((IMethodSymbol)symbol).MethodKind == MethodKind.BuiltinOperator && symbol.ContainingSymbol.IsKind(SymbolKind.PointerType)) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/MethodKindEx.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/MethodKindEx.cs new file mode 100644 index 0000000000000..803762cf6aa78 --- /dev/null +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/MethodKindEx.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +#nullable enable + +using System.Diagnostics.CodeAnalysis; + +namespace Microsoft.CodeAnalysis.Shared.Extensions +{ + /// + /// This class allows the Code Style layer to light up support for new language features when available at runtime + /// while compiling against an older version of the Roslyn assemblies. + /// + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Private constants are used for static assertions.")] + internal static class MethodKindEx + { + public const MethodKind FunctionPointerSignature = (MethodKind)18; + +#if !CODE_STYLE + // This will overflow if the kinds don't match up. + private const uint FunctionPointerValueAssertion = -(FunctionPointerSignature - MethodKind.FunctionPointerSignature); +#endif + } +} diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SymbolKindEx.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SymbolKindEx.cs new file mode 100644 index 0000000000000..d3261cb809d4c --- /dev/null +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SymbolKindEx.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable enable + +using System.Diagnostics.CodeAnalysis; + +namespace Microsoft.CodeAnalysis.Shared.Extensions +{ + /// + /// This class allows the Code Style layer to light up support for new language features when available at runtime + /// while compiling against an older version of the Roslyn assemblies. + /// + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Private constants are used for static assertions.")] + internal static class SymbolKindEx + { + public const SymbolKind FunctionPointer = (SymbolKind)20; + +#if !CODE_STYLE + // This will overflow if the kinds don't match up. + private const uint FunctionPointerValueAssertion = -(FunctionPointer - SymbolKind.FunctionPointer); +#endif + } +} diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.EquivalenceVisitor.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.EquivalenceVisitor.cs index 5a2e9d5892c1e..fa37c52aa349b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.EquivalenceVisitor.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.EquivalenceVisitor.cs @@ -141,6 +141,12 @@ private bool AreEquivalentWorker(ISymbol x, ISymbol y, SymbolKind k, Dictionary< SymbolKind.RangeVariable => RangeVariablesAreEquivalent((IRangeVariableSymbol)x, (IRangeVariableSymbol)y), SymbolKind.TypeParameter => TypeParametersAreEquivalent((ITypeParameterSymbol)x, (ITypeParameterSymbol)y, equivalentTypesWithDifferingAssemblies), SymbolKind.Preprocessing => PreprocessingSymbolsAreEquivalent((IPreprocessingSymbol)x, (IPreprocessingSymbol)y), + SymbolKindEx.FunctionPointer => +#if !CODE_STYLE + FunctionPointerTypesAreEquivalent((IFunctionPointerTypeSymbol)x, (IFunctionPointerTypeSymbol)y, equivalentTypesWithDifferingAssemblies), +#else + false, +#endif _ => false, }; } @@ -174,7 +180,7 @@ private bool LabelsAreEquivalent(ILabelSymbol x, ILabelSymbol y) private bool LocalsAreEquivalent(ILocalSymbol x, ILocalSymbol y) => HaveSameLocation(x, y); - private bool MethodsAreEquivalent(IMethodSymbol x, IMethodSymbol y, Dictionary equivalentTypesWithDifferingAssemblies) + private bool MethodsAreEquivalent(IMethodSymbol x, IMethodSymbol y, Dictionary equivalentTypesWithDifferingAssemblies, bool considerReturnRefKinds = false) { if (!AreCompatibleMethodKinds(x.MethodKind, y.MethodKind)) { @@ -237,6 +243,11 @@ private bool MethodsAreEquivalent(IMethodSymbol x, IMethodSymbol y, Dictionary equivalentTypesWithDifferingAssemblies) + => MethodsAreEquivalent(x.Signature, y.Signature, equivalentTypesWithDifferingAssemblies); +#endif + private bool PropertiesAreEquivalent(IPropertySymbol x, IPropertySymbol y, Dictionary equivalentTypesWithDifferingAssemblies) { if (x.ContainingType.IsAnonymousType && y.ContainingType.IsAnonymousType) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.cs index 31440aec6b0f9..c113e1cf5e345 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.cs @@ -184,6 +184,12 @@ private static bool CheckContainingType(IMethodSymbol x) { return false; } + else if (x.MethodKind == MethodKindEx.FunctionPointerSignature) + { + // We use the signature of a function pointer type to determine equivalence, but + // function pointer types do not have containing types. + return false; + } return true; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/CSharpSyntaxContext.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/CSharpSyntaxContext.cs index f8e9e67c8bec9..00bcccb795677 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/CSharpSyntaxContext.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/CSharpSyntaxContext.cs @@ -51,6 +51,7 @@ internal sealed class CSharpSyntaxContext : SyntaxContext public readonly bool IsCatchFilterContext; public readonly bool IsDestructorTypeContext; public readonly bool IsLeftSideOfImportAliasDirective; + public readonly bool IsFunctionPointerTypeArgumentContext; private CSharpSyntaxContext( Workspace workspace, @@ -106,6 +107,7 @@ private CSharpSyntaxContext( bool isAfterPatternContext, bool isRightSideOfNumericType, bool isInArgumentList, + bool isFunctionPointerTypeArgumentContext, CancellationToken cancellationToken) : base(workspace, semanticModel, position, leftToken, targetToken, isTypeContext, isNamespaceContext, isNamespaceDeclarationNameContext, @@ -147,6 +149,7 @@ private CSharpSyntaxContext( this.IsCatchFilterContext = isCatchFilterContext; this.IsDestructorTypeContext = isDestructorTypeContext; this.IsLeftSideOfImportAliasDirective = isLeftSideOfImportAliasDirective; + this.IsFunctionPointerTypeArgumentContext = isFunctionPointerTypeArgumentContext; } public static CSharpSyntaxContext CreateContext(Workspace workspace, SemanticModel semanticModel, int position, CancellationToken cancellationToken) @@ -265,6 +268,7 @@ private static CSharpSyntaxContext CreateContextWorker(Workspace workspace, Sema isAfterPatternContext: syntaxTree.IsAtEndOfPattern(leftToken, position), isRightSideOfNumericType: isRightSideOfNumericType, isInArgumentList: isArgumentListToken, + isFunctionPointerTypeArgumentContext: syntaxTree.IsFunctionPointerTypeArgumentContext(position, leftToken, cancellationToken), cancellationToken: cancellationToken); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs index b985e82672c65..a04d84b02e539 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; +using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Utilities; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -719,6 +720,7 @@ public static bool IsTypeContext( syntaxTree.IsExpressionContext(position, tokenOnLeftOfPosition, attributes: true, cancellationToken: cancellationToken, semanticModelOpt: semanticModelOpt) || syntaxTree.IsPrimaryFunctionExpressionContext(position, tokenOnLeftOfPosition) || syntaxTree.IsGenericTypeArgumentContext(position, tokenOnLeftOfPosition, cancellationToken, semanticModelOpt) || + syntaxTree.IsFunctionPointerTypeArgumentContext(position, tokenOnLeftOfPosition, cancellationToken) || syntaxTree.IsFixedVariableDeclarationContext(position, tokenOnLeftOfPosition) || syntaxTree.IsImplicitOrExplicitOperatorTypeContext(position, tokenOnLeftOfPosition) || syntaxTree.IsIsOrAsTypeContext(position, tokenOnLeftOfPosition) || @@ -902,6 +904,30 @@ public static bool IsSizeOfExpressionContext( return false; } + public static bool IsFunctionPointerTypeArgumentContext( + this SyntaxTree syntaxTree, + int position, + SyntaxToken tokenOnLeftOfPosition, + CancellationToken cancellationToken) + { +#if !CODE_STYLE + var token = tokenOnLeftOfPosition; + token = token.GetPreviousTokenIfTouchingWord(position); + + // https://github.com/dotnet/roslyn/issues/39865: When the syntax rewrite is done, the parents here will need to change. + switch (token.Kind()) + { + case SyntaxKind.LessThanToken: + case SyntaxKind.CommaToken: + return token.Parent is FunctionPointerTypeSyntax; + } + + return token.Parent is ParameterSyntax { Parent: FunctionPointerTypeSyntax _ }; +#else + return false; +#endif + } + public static bool IsGenericTypeArgumentContext( this SyntaxTree syntaxTree, int position, @@ -1000,6 +1026,12 @@ public static bool IsParameterModifierContext( return true; } + if (token.IsKind(SyntaxKind.LessThanToken) && token.Parent.IsKind(SyntaxKindEx.FunctionPointerType)) + { + parameterIndex = 0; + return true; + } + if (token.IsKind(SyntaxKind.CommaToken) && token.Parent.IsKind(SyntaxKind.ParameterList, out ParameterListSyntax parameterList) && parameterList.IsDelegateOrConstructorOrLocalFunctionOrMethodOrOperatorParameterList(includeOperators)) @@ -1010,6 +1042,17 @@ public static bool IsParameterModifierContext( return true; } +#if !CODE_STYLE + if (token.IsKind(SyntaxKind.CommaToken) && + token.Parent.IsKind(SyntaxKindEx.FunctionPointerType, out FunctionPointerTypeSyntax funcPtrType)) + { + var commaIndex = funcPtrType.Parameters.GetWithSeparators().IndexOf(token); + + parameterIndex = commaIndex / 2 + 1; + return true; + } +#endif + if (token.IsKind(SyntaxKind.CloseBracketToken) && token.Parent.IsKind(SyntaxKind.AttributeList) && token.Parent.IsParentKind(SyntaxKind.Parameter, out ParameterSyntax parameter) && diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.TypeSyntaxGeneratorVisitor.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.TypeSyntaxGeneratorVisitor.cs index 7e22b9beec5d0..24f59e6843adc 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.TypeSyntaxGeneratorVisitor.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.TypeSyntaxGeneratorVisitor.cs @@ -6,6 +6,9 @@ using System.Collections.Generic; using System.Linq; using System.Threading; +#if !CODE_STYLE +using Microsoft.CodeAnalysis.CSharp.CodeGeneration; +#endif using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -115,9 +118,11 @@ public override TypeSyntax VisitDynamicType(IDynamicTypeSymbol symbol) public override TypeSyntax VisitFunctionPointerType(IFunctionPointerTypeSymbol symbol) { // TODO(https://github.com/dotnet/roslyn/issues/39865): generate the calling convention once exposed through the API - var parameters = symbol.Signature.Parameters.Select(p => p.Type) - .Concat(SpecializedCollections.SingletonEnumerable(symbol.Signature.ReturnType)) - .SelectAsArray(t => SyntaxFactory.Parameter(SyntaxFactory.MissingToken(SyntaxKind.IdentifierToken)).WithType(t.GenerateTypeSyntax())); + var parameters = symbol.Signature.Parameters.Select(p => (p.Type, RefKindModifiers: CSharpSyntaxGenerator.GetParameterModifiers(p.RefKind))) + .Concat(SpecializedCollections.SingletonEnumerable(( + Type: symbol.Signature.ReturnType, + RefKindModifiers: CSharpSyntaxGenerator.GetParameterModifiers(symbol.Signature.RefKind, forFunctionPointerReturnParameter: true)))) + .SelectAsArray(t => SyntaxFactory.Parameter(SyntaxFactory.MissingToken(SyntaxKind.IdentifierToken)).WithModifiers(t.RefKindModifiers).WithType(t.Type.GenerateTypeSyntax())); return AddInformationTo( SyntaxFactory.FunctionPointerType(SyntaxFactory.SeparatedList(parameters)), symbol);