From 97c4b6499237737b492dddb8ecceef49ba42b9a7 Mon Sep 17 00:00:00 2001 From: Srivatsn Narayanan Date: Mon, 5 Nov 2018 14:57:59 -0800 Subject: [PATCH 01/10] Add IVTs to 16.0 version of RemoteLS --- .../Core/Microsoft.CodeAnalysis.EditorFeatures.csproj | 2 ++ .../Text/Microsoft.CodeAnalysis.EditorFeatures.Text.csproj | 2 ++ .../Core/Portable/Microsoft.CodeAnalysis.Features.csproj | 2 ++ .../Core/Def/Microsoft.VisualStudio.LanguageServices.csproj | 2 ++ ...icrosoft.VisualStudio.LanguageServices.Implementation.csproj | 2 ++ .../Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj | 2 ++ 6 files changed, 12 insertions(+) diff --git a/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj b/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj index 87a3c8ae6db22..d56ac4e0994d8 100644 --- a/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj +++ b/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj @@ -95,9 +95,11 @@ + + diff --git a/src/EditorFeatures/Text/Microsoft.CodeAnalysis.EditorFeatures.Text.csproj b/src/EditorFeatures/Text/Microsoft.CodeAnalysis.EditorFeatures.Text.csproj index 0256f604dcd00..0e2bd03d81a19 100644 --- a/src/EditorFeatures/Text/Microsoft.CodeAnalysis.EditorFeatures.Text.csproj +++ b/src/EditorFeatures/Text/Microsoft.CodeAnalysis.EditorFeatures.Text.csproj @@ -45,9 +45,11 @@ + + diff --git a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj index 9d180e3d4afbe..a248f2cd312de 100644 --- a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj +++ b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj @@ -83,9 +83,11 @@ + + diff --git a/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj b/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj index 6656596109930..dfe65f954efcd 100644 --- a/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj +++ b/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj @@ -124,9 +124,11 @@ + + diff --git a/src/VisualStudio/Core/Impl/Microsoft.VisualStudio.LanguageServices.Implementation.csproj b/src/VisualStudio/Core/Impl/Microsoft.VisualStudio.LanguageServices.Implementation.csproj index 68158ce182c7f..577610e52d03e 100644 --- a/src/VisualStudio/Core/Impl/Microsoft.VisualStudio.LanguageServices.Implementation.csproj +++ b/src/VisualStudio/Core/Impl/Microsoft.VisualStudio.LanguageServices.Implementation.csproj @@ -33,9 +33,11 @@ + + diff --git a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj index b3e29f3fcbd2b..a5f313cfeb18d 100644 --- a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj @@ -311,9 +311,11 @@ + + From 166349c8ce72f9b708779233fa7c9f561271f058 Mon Sep 17 00:00:00 2001 From: Srivatsn Narayanan Date: Wed, 7 Nov 2018 06:44:23 -0800 Subject: [PATCH 02/10] Cleanup IVTs for 15.7 and 15.8 since they won't be loaded in 16.0 --- .../Core/Microsoft.CodeAnalysis.EditorFeatures.csproj | 4 ---- .../Text/Microsoft.CodeAnalysis.EditorFeatures.Text.csproj | 4 ---- .../Core/Portable/Microsoft.CodeAnalysis.Features.csproj | 4 ---- .../Core/Def/Microsoft.VisualStudio.LanguageServices.csproj | 4 ---- ...rosoft.VisualStudio.LanguageServices.Implementation.csproj | 4 ---- .../Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj | 4 ---- 6 files changed, 24 deletions(-) diff --git a/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj b/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj index d56ac4e0994d8..8d52395a0df62 100644 --- a/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj +++ b/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj @@ -93,12 +93,8 @@ - - - - diff --git a/src/EditorFeatures/Text/Microsoft.CodeAnalysis.EditorFeatures.Text.csproj b/src/EditorFeatures/Text/Microsoft.CodeAnalysis.EditorFeatures.Text.csproj index 0e2bd03d81a19..fe1b4723e07f8 100644 --- a/src/EditorFeatures/Text/Microsoft.CodeAnalysis.EditorFeatures.Text.csproj +++ b/src/EditorFeatures/Text/Microsoft.CodeAnalysis.EditorFeatures.Text.csproj @@ -43,12 +43,8 @@ - - - - diff --git a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj index a248f2cd312de..703fb944777d4 100644 --- a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj +++ b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj @@ -81,12 +81,8 @@ - - - - diff --git a/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj b/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj index dfe65f954efcd..6906361569b14 100644 --- a/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj +++ b/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj @@ -122,12 +122,8 @@ - - - - diff --git a/src/VisualStudio/Core/Impl/Microsoft.VisualStudio.LanguageServices.Implementation.csproj b/src/VisualStudio/Core/Impl/Microsoft.VisualStudio.LanguageServices.Implementation.csproj index 577610e52d03e..d22b80d4fd020 100644 --- a/src/VisualStudio/Core/Impl/Microsoft.VisualStudio.LanguageServices.Implementation.csproj +++ b/src/VisualStudio/Core/Impl/Microsoft.VisualStudio.LanguageServices.Implementation.csproj @@ -31,12 +31,8 @@ - - - - diff --git a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj index a5f313cfeb18d..c228de9e776bb 100644 --- a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj @@ -309,12 +309,8 @@ - - - - From b940ca291632f1683059e516fede3bb4fe7f8bb2 Mon Sep 17 00:00:00 2001 From: HeeJae Chang Date: Fri, 9 Nov 2018 15:30:04 -0800 Subject: [PATCH 03/10] put internal API partner team is using (FSharp) --- .../Completion/CommonCompletionItem.cs | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Features/Core/Portable/Completion/CommonCompletionItem.cs b/src/Features/Core/Portable/Completion/CommonCompletionItem.cs index aab61ee7b3332..fb5a7e599c14a 100644 --- a/src/Features/Core/Portable/Completion/CommonCompletionItem.cs +++ b/src/Features/Core/Portable/Completion/CommonCompletionItem.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.Tags; @@ -7,8 +8,25 @@ namespace Microsoft.CodeAnalysis.Completion { - internal static class CommonCompletionItem + internal static class CommonCompletionItem { + [Obsolete("This is a compatibility shim for FSharp; please do not use it.", error: true)] + public static CompletionItem Create( + string displayText, + CompletionItemRules rules, + Glyph? glyph = null, + ImmutableArray description = default, + string sortText = null, + string filterText = null, + bool showsWarningIcon = false, + ImmutableDictionary properties = null, + ImmutableArray tags = default) + { + return Create( + displayText, displayTextSuffix: string.Empty, rules, + glyph, description, sortText, filterText, showsWarningIcon, properties, tags); + } + public static CompletionItem Create( string displayText, string displayTextSuffix, @@ -100,6 +118,6 @@ private static CompletionDescription DecodeDescription(string encoded) } return CompletionDescription.Create(builder.ToImmutable()); - } + } } } From 5458211dacee27200f3ab613c8221ce794054872 Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Mon, 12 Nov 2018 14:52:45 -0800 Subject: [PATCH 04/10] Block conversion of references to F# projects from C# projects This was a policy we previously had in place, but I accidentally deleted the tests covering it in 2d86a7b62ac93cc89a55a16f306a210d23c16f16. --- .../CPS/CSharpReferencesTests.cs | 22 ++++++ .../VisualStudioWorkspaceImpl.cs | 75 ++++++++++++++----- 2 files changed, 80 insertions(+), 17 deletions(-) diff --git a/src/VisualStudio/CSharp/Test/ProjectSystemShim/CPS/CSharpReferencesTests.cs b/src/VisualStudio/CSharp/Test/ProjectSystemShim/CPS/CSharpReferencesTests.cs index 2206aeed099fc..c67433a85b139 100644 --- a/src/VisualStudio/CSharp/Test/ProjectSystemShim/CPS/CSharpReferencesTests.cs +++ b/src/VisualStudio/CSharp/Test/ProjectSystemShim/CPS/CSharpReferencesTests.cs @@ -108,6 +108,28 @@ public void RemoveProjectConvertsProjectReferencesBack() } } + [WpfFact] + [WorkItem(461967, "https://devdiv.visualstudio.com/DevDiv/_workitems/edit/461967")] + [WorkItem(727173, "https://devdiv.visualstudio.com/DevDiv/_workitems/edit/727173")] + [Trait(Traits.Feature, Traits.Features.ProjectSystemShims)] + public void AddingMetadataReferenceToProjectThatCannotCompileInTheIdeKeepsMetadataReference() + { + using (var environment = new TestEnvironment()) + { + var project1 = CreateCSharpCPSProject(environment, "project1", commandLineArguments: @"/out:c:\project1.dll"); + var project2 = CreateNonCompilableProject(environment, "project2", @"C:\project2.fsproj"); + project2.BinOutputPath = "c:\\project2.dll"; + + project1.AddMetadataReference(project2.BinOutputPath, MetadataReferenceProperties.Assembly); + + // We should not have converted that to a project reference, because we would have no way to produce the compilation + Assert.Empty(environment.Workspace.CurrentSolution.GetProject(project1.Id).AllProjectReferences); + + project2.Dispose(); + project1.Dispose(); + } + } + [WpfFact] [Trait(Traits.Feature, Traits.Features.ProjectSystemShims)] public void AddRemoveAnalyzerReference_CPS() diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs index f15d645c9671e..cbf834e0017fa 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs @@ -1040,7 +1040,7 @@ internal override Guid GetProjectGuid(ProjectId projectId) internal override void SetDocumentContext(DocumentId documentId) { _foregroundObject.AssertIsForeground(); - + // Note: this method does not actually call into any workspace code here to change the workspace's context. The assumption is updating the running document table or // IVsHierarchies will raise the appropriate events which we are subscribed to. @@ -1372,6 +1372,11 @@ public void AddProjectOutputPath(ProjectId projectId, string outputPath) } } + /// + /// Attempts to convert all metadata references to to a project reference to . + /// + /// The of the project that could be referenced in place of the output path. + /// The output path to replace. private void ConvertMetadataReferencesToProjectReferences_NoLock(ProjectId projectId, string outputPath) { var modifiedSolution = this.CurrentSolution; @@ -1379,21 +1384,25 @@ private void ConvertMetadataReferencesToProjectReferences_NoLock(ProjectId proje foreach (var projectIdToRetarget in this.CurrentSolution.ProjectIds) { - foreach (PortableExecutableReference reference in modifiedSolution.GetProject(projectIdToRetarget).MetadataReferences) + if (CanConvertMetadataReferenceToProjectReference(projectIdToRetarget, referencedProjectId: projectId)) { - if (string.Equals(reference.FilePath, outputPath, StringComparison.OrdinalIgnoreCase)) + foreach (PortableExecutableReference reference in modifiedSolution.GetProject(projectIdToRetarget).MetadataReferences) { - var projectReference = new ProjectReference(projectId, reference.Properties.Aliases, reference.Properties.EmbedInteropTypes); - modifiedSolution = modifiedSolution.RemoveMetadataReference(projectIdToRetarget, reference) - .AddProjectReference(projectIdToRetarget, projectReference); + if (string.Equals(reference.FilePath, outputPath, StringComparison.OrdinalIgnoreCase)) + { - projectIdsChanged.Add(projectIdToRetarget); + var projectReference = new ProjectReference(projectId, reference.Properties.Aliases, reference.Properties.EmbedInteropTypes); + modifiedSolution = modifiedSolution.RemoveMetadataReference(projectIdToRetarget, reference) + .AddProjectReference(projectIdToRetarget, projectReference); - GetReferenceInfo_NoLock(projectIdToRetarget).ConvertedProjectReferences.Add( - (reference.FilePath, projectReference)); + projectIdsChanged.Add(projectIdToRetarget); - // We have converted one, but you could have more than one reference with different aliases - // that we need to convert, so we'll keep going + GetReferenceInfo_NoLock(projectIdToRetarget).ConvertedProjectReferences.Add( + (reference.FilePath, projectReference)); + + // We have converted one, but you could have more than one reference with different aliases + // that we need to convert, so we'll keep going + } } } } @@ -1401,6 +1410,29 @@ private void ConvertMetadataReferencesToProjectReferences_NoLock(ProjectId proje SetSolutionAndRaiseWorkspaceChanged_NoLock(modifiedSolution, projectIdsChanged); } + private bool CanConvertMetadataReferenceToProjectReference(ProjectId projectIdWithMetadataReference, ProjectId referencedProjectId) + { + var projectWithMetadataReference = CurrentSolution.GetProject(projectIdWithMetadataReference); + var referencedProject = CurrentSolution.GetProject(referencedProjectId); + + // We don't want to convert a metadata reference to a project reference if the project being referenced isn't something + // we can create a Compilation for. For example, if we have a C# project, and it's referencing a F# project via a metadata reference + // everything would be fine if we left it a metadata reference. Converting it to a project reference means we couldn't create a Compilation + // anymore in the IDE, since the C# compilation would need to reference an F# compilation. F# projects referencing other F# projects though + // do expect this to work, and so we'll always allow references through of the same language. + if (projectWithMetadataReference.Language != referencedProject.Language) + { + if (projectWithMetadataReference.LanguageServices.GetService() != null && + referencedProject.LanguageServices.GetService() == null) + { + // We're referencing something that we can't create a compilation from something that can, so keep the metadtata reference + return false; + } + } + + return true; + } + /// /// Finds all projects that had a project reference to and convert it back to a metadata reference. /// @@ -1449,14 +1481,23 @@ public ProjectReference TryCreateConvertedProjectReference(ProjectId referencing { if (_projectsByOutputPath.TryGetValue(path, out var ids) && ids.Distinct().Count() == 1) { - var projectReference = new ProjectReference( - ids.First(), - aliases: properties.Aliases, - embedInteropTypes: properties.EmbedInteropTypes); + var projectIdToReference = ids.First(); - GetReferenceInfo_NoLock(referencingProject).ConvertedProjectReferences.Add((path, projectReference)); + if (CanConvertMetadataReferenceToProjectReference(referencingProject, projectIdToReference)) + { + var projectReference = new ProjectReference( + projectIdToReference, + aliases: properties.Aliases, + embedInteropTypes: properties.EmbedInteropTypes); + + GetReferenceInfo_NoLock(referencingProject).ConvertedProjectReferences.Add((path, projectReference)); - return projectReference; + return projectReference; + } + else + { + return null; + } } else { From 3bc630fdf041b9400e49bf8c8f8431595e1360be Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Mon, 12 Nov 2018 14:53:12 -0800 Subject: [PATCH 05/10] Delete some trailing whitespace --- .../Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs index cbf834e0017fa..a09ab8b5aae6a 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs @@ -362,7 +362,7 @@ private bool TryGetProjectData(ProjectId projectId, out IVsHierarchy hierarchy, project = null; return - this.TryGetHierarchy(projectId, out hierarchy) && + this.TryGetHierarchy(projectId, out hierarchy) && hierarchy.TryGetProject(out project); } @@ -1452,7 +1452,7 @@ private void ConvertProjectReferencesToMetadataReferences_NoLock(ProjectId proje if (string.Equals(convertedReference.path, outputPath, StringComparison.OrdinalIgnoreCase) && convertedReference.projectReference.ProjectId == projectId) { - var metadataReference = + var metadataReference = CreateMetadataReference( convertedReference.path, new MetadataReferenceProperties( From c4598d72fb5031da798da0ff6e07d2b9d32c9851 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Tue, 13 Nov 2018 07:08:56 -0800 Subject: [PATCH 06/10] Parse array type in conditional operator (#31050) --- .../CSharp/Portable/Parser/LanguageParser.cs | 65 +- .../Semantics/NullableReferenceTypesTests.cs | 66 ++ .../Syntax/Parsing/NullableParsingTests.cs | 595 ++++++++++++++++++ 3 files changed, 700 insertions(+), 26 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 3c44fd3ac5fd9..9d452b4a57fe5 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -4524,7 +4524,7 @@ private static bool CanReuseVariableDeclarator(CSharp.Syntax.VariableDeclaratorS case SyntaxKind.OpenBracketToken: bool sawNonOmittedSize; _termState |= TerminatorState.IsPossibleEndOfVariableDeclaration; - var specifier = this.ParseArrayRankSpecifier(isArrayCreation: false, expectSizes: flags == VariableFlags.Fixed, allowQuestionToken: false, sawNonOmittedSize: out sawNonOmittedSize); + var specifier = this.ParseArrayRankSpecifier(isArrayCreation: false, expectSizes: flags == VariableFlags.Fixed, questionTokenModeOpt: null, sawNonOmittedSize: out sawNonOmittedSize); _termState = saveTerm; var open = specifier.OpenBracketToken; var sizes = specifier.Sizes; @@ -6214,7 +6214,6 @@ private enum ParseTypeMode ParseTypeMode mode, bool expectSizes) { - var isOrAs = mode == ParseTypeMode.AsExpression || mode == ParseTypeMode.AfterIs; NameOptions nameOptions; switch (mode) { @@ -6244,31 +6243,15 @@ private enum ParseTypeMode } var type = this.ParseUnderlyingType(parentIsParameter: mode == ParseTypeMode.Parameter, options: nameOptions); + Debug.Assert(type != null); - if (this.CurrentToken.Kind == SyntaxKind.QuestionToken && - // we do not permit nullable types in a declaration pattern - (mode != ParseTypeMode.AfterIs && mode != ParseTypeMode.AfterCase || !IsTrueIdentifier(this.PeekToken(1)))) + if (this.CurrentToken.Kind == SyntaxKind.QuestionToken) { - var resetPoint = this.GetResetPoint(); - try + var question = EatNullableQualifierIfApplicable(mode); + if (question != null) { - var question = this.EatToken(); - - if (isOrAs && (IsTerm() || IsPredefinedType(this.CurrentToken.Kind) || SyntaxFacts.IsAnyUnaryExpression(this.CurrentToken.Kind))) - { - this.Reset(ref resetPoint); - - Debug.Assert(type != null); - return type; - } - - question = CheckFeatureAvailability(question, MessageID.IDS_FeatureNullable); type = _syntaxFactory.NullableType(type, question); } - finally - { - this.Release(ref resetPoint); - } } switch (mode) @@ -6301,7 +6284,7 @@ private enum ParseTypeMode while (this.IsPossibleRankAndDimensionSpecifier()) { bool unused; - var rank = this.ParseArrayRankSpecifier(mode == ParseTypeMode.ArrayCreation, expectSizes, allowQuestionToken: true, out unused); + var rank = this.ParseArrayRankSpecifier(mode == ParseTypeMode.ArrayCreation, expectSizes, questionTokenModeOpt: mode, out unused); ranks.Add(rank); expectSizes = false; } @@ -6318,6 +6301,36 @@ private enum ParseTypeMode return type; } + private SyntaxToken EatNullableQualifierIfApplicable(ParseTypeMode mode) + { + Debug.Assert(this.CurrentToken.Kind == SyntaxKind.QuestionToken); + + // we do not permit nullable types in a declaration pattern + if (mode != ParseTypeMode.AfterIs && mode != ParseTypeMode.AfterCase || !IsTrueIdentifier(this.PeekToken(1))) + { + var resetPoint = this.GetResetPoint(); + try + { + var question = this.EatToken(); + + var isOrAs = mode == ParseTypeMode.AsExpression || mode == ParseTypeMode.AfterIs; + if (isOrAs && (IsTerm() || IsPredefinedType(this.CurrentToken.Kind) || SyntaxFacts.IsAnyUnaryExpression(this.CurrentToken.Kind))) + { + this.Reset(ref resetPoint); + return null; + } + + return CheckFeatureAvailability(question, MessageID.IDS_FeatureNullable); + } + finally + { + this.Release(ref resetPoint); + } + } + + return null; + } + private bool PointerTypeModsFollowedByRankAndDimensionSpecifier() { // Are pointer specifiers (if any) followed by an array specifier? @@ -6340,7 +6353,7 @@ private bool IsPossibleRankAndDimensionSpecifier() return this.CurrentToken.Kind == SyntaxKind.OpenBracketToken; } - private ArrayRankSpecifierSyntax ParseArrayRankSpecifier(bool isArrayCreation, bool expectSizes, bool allowQuestionToken, out bool sawNonOmittedSize) + private ArrayRankSpecifierSyntax ParseArrayRankSpecifier(bool isArrayCreation, bool expectSizes, ParseTypeMode? questionTokenModeOpt, out bool sawNonOmittedSize) { sawNonOmittedSize = false; bool sawOmittedSize = false; @@ -6407,9 +6420,9 @@ private ArrayRankSpecifierSyntax ParseArrayRankSpecifier(bool isArrayCreation, b var close = this.EatToken(SyntaxKind.CloseBracketToken); SyntaxToken questionToken = null; - if (allowQuestionToken && this.CurrentToken.Kind == SyntaxKind.QuestionToken) + if (questionTokenModeOpt != null && this.CurrentToken.Kind == SyntaxKind.QuestionToken) { - questionToken = this.EatToken(); + questionToken = EatNullableQualifierIfApplicable(questionTokenModeOpt.GetValueOrDefault()); } return _syntaxFactory.ArrayRankSpecifier(open, list, close, questionToken); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index 320612803cfa4..7a356df6548b6 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -456,6 +456,72 @@ static void F2(object? w) Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(14, 26)); } + [Fact] + public void NullableAndConditionalOperators() + { + var source = +@"class Program +{ + static void F1(object x) + { + _ = x is string? 1 : 2; + _ = x is string? ? 1 : 2; + _ = x is string ? ? 1 : 2; + _ = x as string?? x; + _ = x as string ? ?? x; + } + static void F2(object y) + { + _ = y is object[]? 1 : 2; + _ = y is object[]? ? 1 : 2; + _ = y is object[] ? ? 1 : 2; + _ = y as object[]?? y; + _ = y as object[] ? ?? y; + } + static void F3(object z) + { + _ = z is T[][]? 1 : 2; + _ = z is T[]?[] ? 1 : 2; + _ = z is T[] ? [] ? 1 : 2; + _ = z as T[][]?? z; + _ = z as T[] ? [] ?? z; + } +}"; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular7); + comp.VerifyDiagnostics( + // (6,24): error CS8107: Feature 'nullable reference types' is not available in C# 7.0. Please use language version 8.0 or greater. + // _ = x is string? ? 1 : 2; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "?").WithArguments("nullable reference types", "8.0").WithLocation(6, 24), + // (7,25): error CS8107: Feature 'nullable reference types' is not available in C# 7.0. Please use language version 8.0 or greater. + // _ = x is string ? ? 1 : 2; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "?").WithArguments("nullable reference types", "8.0").WithLocation(7, 25), + // (9,25): error CS8107: Feature 'nullable reference types' is not available in C# 7.0. Please use language version 8.0 or greater. + // _ = x as string ? ?? x; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "?").WithArguments("nullable reference types", "8.0").WithLocation(9, 25), + // (14,26): error CS8107: Feature 'nullable reference types' is not available in C# 7.0. Please use language version 8.0 or greater. + // _ = y is object[]? ? 1 : 2; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "?").WithArguments("nullable reference types", "8.0").WithLocation(14, 26), + // (15,27): error CS8107: Feature 'nullable reference types' is not available in C# 7.0. Please use language version 8.0 or greater. + // _ = y is object[] ? ? 1 : 2; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "?").WithArguments("nullable reference types", "8.0").WithLocation(15, 27), + // (17,27): error CS8107: Feature 'nullable reference types' is not available in C# 7.0. Please use language version 8.0 or greater. + // _ = y as object[] ? ?? y; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "?").WithArguments("nullable reference types", "8.0").WithLocation(17, 27), + // (22,21): error CS8107: Feature 'nullable reference types' is not available in C# 7.0. Please use language version 8.0 or greater. + // _ = z is T[]?[] ? 1 : 2; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "?").WithArguments("nullable reference types", "8.0").WithLocation(22, 21), + // (23,22): error CS8107: Feature 'nullable reference types' is not available in C# 7.0. Please use language version 8.0 or greater. + // _ = z is T[] ? [] ? 1 : 2; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "?").WithArguments("nullable reference types", "8.0").WithLocation(23, 22), + // (25,22): error CS8107: Feature 'nullable reference types' is not available in C# 7.0. Please use language version 8.0 or greater. + // _ = z as T[] ? [] ?? z; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "?").WithArguments("nullable reference types", "8.0").WithLocation(25, 22)); + + comp = CreateCompilation(source, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics(); + } + [Fact, WorkItem(29318, "https://github.com/dotnet/roslyn/issues/29318")] public void IsOperatorOnNonNullExpression() { diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/NullableParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/NullableParsingTests.cs index e67f0dbcccd85..81f760b5fd19e 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/NullableParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/NullableParsingTests.cs @@ -291,6 +291,601 @@ public void NullableArray_Cast_05() EOF(); } + [Fact] + public void ConditionalOperator_NotNullableType() + { + UsingExpression("x is T ? y : z"); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "z"); + } + } + EOF(); + } + + [Fact] + public void ConditionalOperator_NullableType() + { + UsingExpression("x is T ? ? y : z"); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.QuestionToken); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "z"); + } + } + EOF(); + } + + [Fact] + public void ConditionalOperator_NotNullableArray() + { + UsingExpression("x is T[] ? y : z"); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "z"); + } + } + EOF(); + } + + [Fact] + public void ConditionalOperator_NullableArray() + { + UsingExpression("x is T[] ? ? y : z"); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + N(SyntaxKind.QuestionToken); + } + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "z"); + } + } + EOF(); + } + + [Fact] + public void NullCoalesingOperator_NotNullableType() + { + UsingExpression("x as T?? y"); + N(SyntaxKind.CoalesceExpression); + { + N(SyntaxKind.AsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.AsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + } + N(SyntaxKind.QuestionQuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + EOF(); + } + + [Fact] + public void NullCoalesingOperator_NullableType() + { + UsingExpression("x as T? ?? y"); + N(SyntaxKind.CoalesceExpression); + { + N(SyntaxKind.AsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.AsKeyword); + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.QuestionToken); + } + } + N(SyntaxKind.QuestionQuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + EOF(); + } + + [Fact] + public void NullCoalesingOperator_NullableType_Invalid() + { + UsingExpression("x as T??? y", + // (1,9): error CS1525: Invalid expression term '?' + // x as T??? y + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "?").WithArguments("?").WithLocation(1, 9), + // (1,12): error CS1003: Syntax error, ':' expected + // x as T??? y + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(":", "").WithLocation(1, 12), + // (1,12): error CS1733: Expected expression + // x as T??? y + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 12)); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.CoalesceExpression); + { + N(SyntaxKind.AsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.AsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + } + N(SyntaxKind.QuestionQuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken); + } + } + EOF(); + } + + [Fact] + public void NullCoalesingOperator_NotNullableArray() + { + UsingExpression("x as T[] ?? y"); + N(SyntaxKind.CoalesceExpression); + { + N(SyntaxKind.AsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.AsKeyword); + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + } + N(SyntaxKind.QuestionQuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + EOF(); + } + + [Fact] + public void NullCoalesingOperator_NullableArray() + { + UsingExpression("x as T[] ? ?? y"); + N(SyntaxKind.CoalesceExpression); + { + N(SyntaxKind.AsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.AsKeyword); + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + N(SyntaxKind.QuestionToken); + } + } + } + N(SyntaxKind.QuestionQuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + EOF(); + } + + [Fact] + public void DeclarationPattern_NullableType() + { + UsingStatement("switch (e) { case T? t: break; }", + // (1,25): error CS1525: Invalid expression term 'break' + // switch (e) { case T? t: break; } + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "break").WithArguments("break").WithLocation(1, 25), + // (1,25): error CS1003: Syntax error, ':' expected + // switch (e) { case T? t: break; } + Diagnostic(ErrorCode.ERR_SyntaxError, "break").WithArguments(":", "break").WithLocation(1, 25)); + N(SyntaxKind.SwitchStatement); + { + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchSection); + { + N(SyntaxKind.CaseSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact] + public void DeclarationPattern_NullableArray() + { + UsingStatement("switch (e) { case T[]? t: break; }", + // (1,21): error CS0443: Syntax error; value expected + // switch (e) { case T[]? t: break; } + Diagnostic(ErrorCode.ERR_ValueExpected, "]").WithLocation(1, 21), + // (1,27): error CS1525: Invalid expression term 'break' + // switch (e) { case T[]? t: break; } + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "break").WithArguments("break").WithLocation(1, 27), + // (1,27): error CS1003: Syntax error, ':' expected + // switch (e) { case T[]? t: break; } + Diagnostic(ErrorCode.ERR_SyntaxError, "break").WithArguments(":", "break").WithLocation(1, 27)); + N(SyntaxKind.SwitchStatement); + { + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchSection); + { + N(SyntaxKind.CaseSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.ElementAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact] + public void DeclarationPattern_ArrayOfNullableType() + { + UsingStatement("switch (e) { case T?[] t: break; }"); + N(SyntaxKind.SwitchStatement); + { + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchSection); + { + N(SyntaxKind.CasePatternSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "t"); + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact] + public void DeclarationPattern_NullableArrayOfArray() + { + UsingStatement("switch (e) { case T[]?[] t: break; }"); + N(SyntaxKind.SwitchStatement); + { + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchSection); + { + N(SyntaxKind.CasePatternSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "t"); + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + [Fact] public void NullableArray_TypeArgument() { From ff8e1c75e0e1a6b57ef12990e74d52d47da53898 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 13 Nov 2018 10:03:10 -0800 Subject: [PATCH 07/10] Nullable analysis of yield return (#31072) --- .../CSharp/Portable/Binder/InMethodBinder.cs | 20 +- .../Portable/FlowAnalysis/NullableWalker.cs | 13 + .../Emit/CodeGen/CodeGenAsyncIteratorTests.cs | 307 ++---------- .../Semantics/NullableReferenceTypesTests.cs | 449 +++++++++++++++++- .../Test/Utilities/CSharp/CSharpTestBase.cs | 235 +++++++++ .../Portable/Traits/CompilerFeature.cs | 1 + 6 files changed, 752 insertions(+), 273 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/InMethodBinder.cs b/src/Compilers/CSharp/Portable/Binder/InMethodBinder.cs index fee66db548649..6a76ef8c943d8 100644 --- a/src/Compilers/CSharp/Portable/Binder/InMethodBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/InMethodBinder.cs @@ -176,6 +176,11 @@ internal override TypeSymbol GetIteratorElementType(YieldStatementSyntax node, D } private TypeSymbol GetIteratorElementTypeFromReturnType(RefKind refKind, TypeSymbol returnType, CSharpSyntaxNode errorLocationNode, DiagnosticBag diagnostics) + { + return GetIteratorElementTypeFromReturnType(Compilation, refKind, returnType, errorLocationNode, diagnostics).TypeSymbol; + } + + internal static TypeSymbolWithAnnotations GetIteratorElementTypeFromReturnType(CSharpCompilation compilation, RefKind refKind, TypeSymbol returnType, CSharpSyntaxNode errorLocationNode, DiagnosticBag diagnostics) { if (refKind == RefKind.None && returnType.Kind == SymbolKind.NamedType) { @@ -183,20 +188,25 @@ private TypeSymbol GetIteratorElementTypeFromReturnType(RefKind refKind, TypeSym { case SpecialType.System_Collections_IEnumerable: case SpecialType.System_Collections_IEnumerator: - return GetSpecialType(SpecialType.System_Object, diagnostics, errorLocationNode); + var objectType = compilation.GetSpecialType(SpecialType.System_Object); + if (diagnostics != null) + { + ReportUseSiteDiagnostics(objectType, diagnostics, errorLocationNode); + } + return TypeSymbolWithAnnotations.Create(objectType); case SpecialType.System_Collections_Generic_IEnumerable_T: case SpecialType.System_Collections_Generic_IEnumerator_T: - return ((NamedTypeSymbol)returnType).TypeArgumentsNoUseSiteDiagnostics[0].TypeSymbol; + return ((NamedTypeSymbol)returnType).TypeArgumentsNoUseSiteDiagnostics[0]; } - if (returnType.OriginalDefinition == Compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T)) + if (returnType.OriginalDefinition == compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T)) { - return ((NamedTypeSymbol)returnType).TypeArgumentsNoUseSiteDiagnostics[0].TypeSymbol; + return ((NamedTypeSymbol)returnType).TypeArgumentsNoUseSiteDiagnostics[0]; } } - return null; + return default; } internal override void LookupSymbolsInSingleBinder( diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 59270a46b3e20..3e87194309e36 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -4592,6 +4592,19 @@ public override BoundNode VisitThrowExpression(BoundThrowExpression node) return result; } + public override BoundNode VisitYieldReturnStatement(BoundYieldReturnStatement node) + { + BoundExpression expr = node.Expression; + if (expr == null) + { + return null; + } + var method = (MethodSymbol)_member; + TypeSymbolWithAnnotations elementType = InMethodBinder.GetIteratorElementTypeFromReturnType(compilation, RefKind.None, method.ReturnType.TypeSymbol, errorLocationNode: null, diagnostics: null); + VisitOptionalImplicitConversion(expr, elementType, useLegacyWarnings: false, AssignmentKind.Return); + return null; + } + #endregion Visitors protected override string Dump(LocalState state) diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs index 0e16f1039f70d..bf55c9cda5116 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs @@ -24,7 +24,7 @@ public class CodeGenAsyncIteratorTests : EmitMetadataTestBase { private void VerifyMissingMember(WellKnownMember member, params DiagnosticDescription[] expected) { - var lib = CreateCompilationWithTasksExtensions(s_common); + var lib = CreateCompilationWithAsyncIterator(""); var lib_ref = lib.EmitToImageReference(); string source = @" @@ -41,7 +41,7 @@ class C private void VerifyMissingType(WellKnownType type, params DiagnosticDescription[] expected) { - var lib = CreateCompilationWithTasksExtensions(s_common); + var lib = CreateCompilationWithAsyncIterator(""); var lib_ref = lib.EmitToImageReference(); string source = @" @@ -57,7 +57,7 @@ class C } private CSharpCompilation CreateCompilationWithAsyncIterator(string source, CSharpCompilationOptions options = null) - => CreateCompilationWithTasksExtensions(new[] { source, s_common }, options: options); + => CreateCompilationWithTasksExtensions(new[] { source, AsyncStreamsTypes }, options: options); [Fact] [WorkItem(30566, "https://github.com/dotnet/roslyn/issues/30566")] @@ -315,8 +315,14 @@ static async System.Collections.Generic.IAsyncEnumerable M() yield break; } }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }, parseOptions: TestOptions.Regular7_3); + var comp = CreateCompilationWithTasksExtensions(new[] { source }, parseOptions: TestOptions.Regular7_3); comp.VerifyDiagnostics( + // (4,45): error CS0234: The type or namespace name 'IAsyncEnumerable<>' does not exist in the namespace 'System.Collections.Generic' (are you missing an assembly reference?) + // static async System.Collections.Generic.IAsyncEnumerable M() + Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "IAsyncEnumerable").WithArguments("IAsyncEnumerable<>", "System.Collections.Generic").WithLocation(4, 45), + // (4,67): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, or IAsyncEnumerable + // static async System.Collections.Generic.IAsyncEnumerable M() + Diagnostic(ErrorCode.ERR_BadAsyncReturn, "M").WithLocation(4, 67), // (4,67): error CS8370: Feature 'async streams' is not available in C# 7.3. Please use language version 8.0 or greater. // static async System.Collections.Generic.IAsyncEnumerable M() Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "M").WithArguments("async streams", "8.0").WithLocation(4, 67), @@ -347,7 +353,7 @@ await foreach (var s in M()) ref struct S { }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }, options: TestOptions.DebugExe); + var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics( // (4,65): error CS0306: The type 'S' may not be used as a type argument // static async System.Collections.Generic.IAsyncEnumerable M() @@ -371,7 +377,7 @@ static async System.Collections.Generic.IAsyncEnumerable M() yield return 42; } }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }); + var comp = CreateCompilationWithAsyncIterator(source); comp.VerifyDiagnostics(); var m2 = comp.GlobalNamespace.GetMember("C.M2"); @@ -395,7 +401,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() yield return 4; } }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }, options: TestOptions.DebugDll); + var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugDll); comp.VerifyDiagnostics(); CompileAndVerify(comp, symbolValidator: module => { @@ -417,7 +423,7 @@ public static async System.Collections.Generic.IAsyncEnumerator M() yield return 4; } }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }); + var comp = CreateCompilationWithAsyncIterator(source); comp.VerifyDiagnostics( // (4,74): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, or IAsyncEnumerable // public static async System.Collections.Generic.IAsyncEnumerator M() @@ -775,7 +781,7 @@ async System.Collections.Generic.IAsyncEnumerable M() break; } }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }); + var comp = CreateCompilationWithAsyncIterator(source); comp.VerifyDiagnostics( // (8,9): error CS0139: No enclosing loop out of which to break or continue // break; @@ -800,7 +806,7 @@ async System.Collections.Generic.IAsyncEnumerable M2() return 4; } }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }); + var comp = CreateCompilationWithAsyncIterator(source); comp.VerifyDiagnostics( // (8,9): error CS1622: Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration. // return 1; @@ -831,7 +837,7 @@ async System.Collections.Generic.IAsyncEnumerable M2() return null; } }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }); + var comp = CreateCompilationWithAsyncIterator(source); comp.VerifyDiagnostics( // (8,9): error CS1622: Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration. // return null; @@ -862,7 +868,7 @@ async System.Collections.Generic.IAsyncEnumerable M2(ref string s2) return ref s2; } }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }); + var comp = CreateCompilationWithAsyncIterator(source); comp.VerifyDiagnostics( // (4,73): error CS1988: Async methods cannot have ref, in or out parameters // async System.Collections.Generic.IAsyncEnumerable M(ref string s) @@ -902,7 +908,7 @@ async System.Collections.Generic.IAsyncEnumerable M2() return default; } }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }); + var comp = CreateCompilationWithAsyncIterator(source); comp.VerifyDiagnostics( // (8,9): error CS1622: Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration. // return default; @@ -948,7 +954,7 @@ await using (var enumerator = M().GetAsyncEnumerator()) } } }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }, options: TestOptions.DebugExe); + var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "0 1 2 3 4 5"); } @@ -980,7 +986,7 @@ await foreach (var i in M()) }"; foreach (var options in new[] { TestOptions.DebugExe, TestOptions.ReleaseExe }) { - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }, options: options); + var comp = CreateCompilationWithAsyncIterator(source, options: options); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: "0 1 2 3 4 5"); @@ -1377,7 +1383,7 @@ await foreach (var i in M(3)) Write(""5""); } }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }, options: TestOptions.DebugExe); + var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "0 1 2 3 4 5"); } @@ -1410,7 +1416,7 @@ await foreach (var i in C.M(3)) Write(""5""); } }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }, options: TestOptions.DebugExe); + var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "0 1 2 3 4 5"); } @@ -1442,7 +1448,7 @@ await foreach (var i in M(10)) Write(""End""); } }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }, options: TestOptions.DebugExe); + var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "Start p:10 p:11 Value p:12 End"); } @@ -1475,7 +1481,7 @@ await foreach (var i in new C().M()) Write(""End""); } }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }, options: TestOptions.DebugExe); + var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); var v = CompileAndVerify(comp, expectedOutput: "Start f:10 f:11 Value f:12 End"); } @@ -1493,7 +1499,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() return null; } }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }); + var comp = CreateCompilationWithAsyncIterator(source); comp.VerifyDiagnostics( // (8,9): error CS1622: Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration. // return null; @@ -1526,7 +1532,7 @@ await foreach (var i in M()) Write(""Done""); } }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }, options: TestOptions.DebugExe); + var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "0 1 2 3 4 Done"); } @@ -1557,7 +1563,7 @@ await foreach (var i in M()) Write(""Done""); } }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }, options: TestOptions.DebugExe); + var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "0 1 2 3 4 5 Done"); } @@ -1586,7 +1592,7 @@ await foreach (var i in M()) Write(""Done""); } }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }, options: TestOptions.DebugExe); + var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "0 1 2 3 Done"); } @@ -1615,7 +1621,7 @@ await foreach (var i in M()) Write(""Done""); } }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }, options: TestOptions.DebugExe); + var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "0 1 2 Done"); } @@ -1649,7 +1655,7 @@ await foreach (var i in M()) Write(""Done""); } }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }, options: TestOptions.DebugExe); + var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "0 1 2 3 Done"); } @@ -1693,7 +1699,7 @@ await foreach (var i in M()) Write(""Done""); }} }}"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }, options: TestOptions.DebugExe); + var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: expectation); } @@ -1721,7 +1727,7 @@ async System.Collections.Generic.IAsyncEnumerable local() }} }} }}"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }, options: TestOptions.DebugExe); + var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: expectation); } @@ -1794,7 +1800,7 @@ await foreach (var i in M()) Write(""Done""); } }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }, options: TestOptions.DebugExe); + var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: "0 1 2 3 4 Done"); } @@ -1810,7 +1816,7 @@ async System.Collections.Generic.IAsyncEnumerable M() await System.Threading.Tasks.Task.CompletedTask; } }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }); + var comp = CreateCompilationWithAsyncIterator(source); comp.VerifyDiagnostics( // (4,60): error CS0161: 'C.M()': not all code paths return a value // async System.Collections.Generic.IAsyncEnumerable M() @@ -1836,7 +1842,7 @@ await foreach (var i in M()) } } }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }, options: TestOptions.DebugExe); + var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics( // (4,67): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. // static async System.Collections.Generic.IAsyncEnumerable M() @@ -1864,7 +1870,7 @@ await foreach (var i in M()) System.Console.Write(""none""); } }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }, options: TestOptions.DebugExe); + var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics( // (4,67): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. // static async System.Collections.Generic.IAsyncEnumerable M() @@ -1883,7 +1889,7 @@ async System.Collections.Generic.IAsyncEnumerable M() { } }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }); + var comp = CreateCompilationWithAsyncIterator(source); comp.VerifyDiagnostics( // (4,60): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. // async System.Collections.Generic.IAsyncEnumerable M() @@ -1906,7 +1912,7 @@ async System.Collections.Generic.IAsyncEnumerable M() yield return; } }"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }); + var comp = CreateCompilationWithAsyncIterator(source); comp.VerifyDiagnostics( // (7,15): error CS1627: Expression expected after yield return // yield return; @@ -1920,243 +1926,10 @@ async System.Collections.Generic.IAsyncEnumerable M() ); } - private static readonly string s_common = @" -namespace System.Collections.Generic -{ - public interface IAsyncEnumerable - { - IAsyncEnumerator GetAsyncEnumerator(); - } - - public interface IAsyncEnumerator : System.IAsyncDisposable - { - System.Threading.Tasks.ValueTask MoveNextAsync(); - T Current { get; } - } -} -namespace System -{ - public interface IAsyncDisposable - { - System.Threading.Tasks.ValueTask DisposeAsync(); - } -} - -namespace System.Runtime.CompilerServices -{ - public interface IStrongBox - { - ref T Value { get; } - } -} - -namespace System.Threading.Tasks -{ - using System.Runtime.CompilerServices; - using System.Runtime.ExceptionServices; - using System.Threading.Tasks.Sources; - - public struct ManualResetValueTaskSourceLogic - { - private static readonly Action s_sentinel = new Action(s => throw new InvalidOperationException()); - - private readonly IStrongBox> _parent; - private Action _continuation; - private object _continuationState; - private object _capturedContext; - private ExecutionContext _executionContext; - private bool _completed; - private TResult _result; - private ExceptionDispatchInfo _error; - private short _version; - - public ManualResetValueTaskSourceLogic(IStrongBox> parent) - { - _parent = parent ?? throw new ArgumentNullException(nameof(parent)); - _continuation = null; - _continuationState = null; - _capturedContext = null; - _executionContext = null; - _completed = false; - _result = default; - _error = null; - _version = 0; - } - - public short Version => _version; - - private void ValidateToken(short token) - { - if (token != _version) - { - throw new InvalidOperationException(); - } - } - - public ValueTaskSourceStatus GetStatus(short token) - { - ValidateToken(token); - - return - !_completed ? ValueTaskSourceStatus.Pending : - _error == null ? ValueTaskSourceStatus.Succeeded : - _error.SourceException is OperationCanceledException ? ValueTaskSourceStatus.Canceled : - ValueTaskSourceStatus.Faulted; - } - - public TResult GetResult(short token) - { - ValidateToken(token); - - if (!_completed) - { - throw new InvalidOperationException(); - } - - _error?.Throw(); - return _result; - } - - public void Reset() - { - _version++; - - _completed = false; - _continuation = null; - _continuationState = null; - _result = default; - _error = null; - _executionContext = null; - _capturedContext = null; - } - - public void OnCompleted(Action continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) - { - if (continuation == null) - { - throw new ArgumentNullException(nameof(continuation)); - } - ValidateToken(token); - - if ((flags & ValueTaskSourceOnCompletedFlags.FlowExecutionContext) != 0) - { - _executionContext = ExecutionContext.Capture(); - } - - if ((flags & ValueTaskSourceOnCompletedFlags.UseSchedulingContext) != 0) - { - SynchronizationContext sc = SynchronizationContext.Current; - if (sc != null && sc.GetType() != typeof(SynchronizationContext)) - { - _capturedContext = sc; - } - else - { - TaskScheduler ts = TaskScheduler.Current; - if (ts != TaskScheduler.Default) - { - _capturedContext = ts; - } - } - } - - _continuationState = state; - if (Interlocked.CompareExchange(ref _continuation, continuation, null) != null) - { - _executionContext = null; - - object cc = _capturedContext; - _capturedContext = null; - - switch (cc) - { - case null: - Task.Factory.StartNew(continuation, state, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); - break; - - case SynchronizationContext sc: - sc.Post(s => - { - var tuple = (Tuple, object>)s; - tuple.Item1(tuple.Item2); - }, Tuple.Create(continuation, state)); - break; - - case TaskScheduler ts: - Task.Factory.StartNew(continuation, state, CancellationToken.None, TaskCreationOptions.DenyChildAttach, ts); - break; - } - } - } - - public void SetResult(TResult result) - { - _result = result; - SignalCompletion(); - } - - public void SetException(Exception error) - { - _error = ExceptionDispatchInfo.Capture(error); - SignalCompletion(); - } - - private void SignalCompletion() - { - if (_completed) - { - throw new InvalidOperationException(); - } - _completed = true; - - if (Interlocked.CompareExchange(ref _continuation, s_sentinel, null) != null) - { - if (_executionContext != null) - { - ExecutionContext.Run( - _executionContext, - s => ((IStrongBox>)s).Value.InvokeContinuation(), - _parent ?? throw new InvalidOperationException()); - } - else - { - InvokeContinuation(); - } - } - } - - private void InvokeContinuation() - { - object cc = _capturedContext; - _capturedContext = null; - - switch (cc) - { - case null: - _continuation(_continuationState); - break; - - case SynchronizationContext sc: - sc.Post(s => - { - ref ManualResetValueTaskSourceLogic logicRef = ref ((IStrongBox>)s).Value; - logicRef._continuation(logicRef._continuationState); - }, _parent ?? throw new InvalidOperationException()); - break; - - case TaskScheduler ts: - Task.Factory.StartNew(_continuation, _continuationState, CancellationToken.None, TaskCreationOptions.DenyChildAttach, ts); - break; - } - } - } -} -"; - [Fact] public void TestWellKnownMembers() { - var comp = CreateCompilation(s_common, references: new[] { TestReferences.NetStandard20.TasksExtensionsRef }, targetFramework: Roslyn.Test.Utilities.TargetFramework.NetStandard20); + var comp = CreateCompilation(AsyncStreamsTypes, references: new[] { TestReferences.NetStandard20.TasksExtensionsRef }, targetFramework: Roslyn.Test.Utilities.TargetFramework.NetStandard20); comp.VerifyDiagnostics(); verifyType(WellKnownType.System_Runtime_CompilerServices_IStrongBox_T, diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index 7a356df6548b6..66458e392a63c 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -14,6 +14,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics { + [CompilerTrait(CompilerFeature.NullableReferenceTypes)] public class NullableReferenceTypesTests : CSharpTestBase { [Fact] @@ -10300,6 +10301,29 @@ class CL1 ); } + [Fact] + public void ReturningValues_BadValue() + { + CSharpCompilation c = CreateCompilation(new[] { @" +class C +{ + string M() + { + return bad; + } +} +" }, options: WithNonNullTypesTrue()); + + c.VerifyDiagnostics( + // (6,16): error CS0103: The name 'bad' does not exist in the current context + // return bad; + Diagnostic(ErrorCode.ERR_NameNotInContext, "bad").WithArguments("bad").WithLocation(6, 16), + // (6,16): warning CS8619: Nullability of reference types in value of type '?' doesn't match target type 'string'. + // return bad; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "bad").WithArguments("?", "string").WithLocation(6, 16) + ); + } + [Fact] public void IdentityConversion_Return_01() { @@ -22634,6 +22658,8 @@ public void UnboundLambda_02() static void F(object? x) { var z = y => y ?? x.ToString(); + System.Func z2 = y => y ?? x.ToString(); + System.Func z3 = y => null; } }"; var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); @@ -22643,7 +22669,13 @@ static void F(object? x) Diagnostic(ErrorCode.ERR_ImplicitlyTypedVariableAssignedBadValue, "z = y => y ?? x.ToString()").WithArguments("lambda expression").WithLocation(5, 13), // (5,27): warning CS8602: Possible dereference of a null reference. // var z = y => y ?? x.ToString(); - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(5, 27)); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(5, 27), + // (6,53): warning CS8602: Possible dereference of a null reference. + // System.Func z2 = y => y ?? x.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(6, 53), + // (7,48): warning CS8603: Possible null reference return. + // System.Func z3 = y => null; + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(7, 48)); } /// @@ -28249,6 +28281,421 @@ class CL1 {} ); } + [Fact] + public void ReturningValues_IEnumerableT() + { + var source = @" +public class C +{ + System.Collections.Generic.IEnumerable M() + { + return null; // 1 + } + public System.Collections.Generic.IEnumerable? M2() + { + return null; + } + System.Collections.Generic.IEnumerable M3() => null; // 2 + System.Collections.Generic.IEnumerable? M4() => null; +}"; + + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (6,16): warning CS8603: Possible null reference return. + // return null; // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(6, 16), + // (12,60): warning CS8603: Possible null reference return. + // System.Collections.Generic.IEnumerable M3() => null; // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(12, 60) + ); + + var source2 = @" +class D +{ + void M(C c) + { + c.M2() /*T:System.Collections.Generic.IEnumerable?*/ ; + } +} +"; + var comp2 = CreateCompilation(source2, references: new[] { comp.EmitToImageReference() }); + comp2.VerifyTypes(); + } + + [Fact] + public void Yield_IEnumerableT() + { + var source = @" +public class C +{ + public System.Collections.Generic.IEnumerable M() + { + yield return null; // 1 + yield return """"; + yield return null; // 2 + yield break; + } + public System.Collections.Generic.IEnumerable M2() + { + yield return null; + } +}"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (6,22): warning CS8603: Possible null reference return. + // yield return null; // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(6, 22), + // (8,22): warning CS8603: Possible null reference return. + // yield return null; // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(8, 22) + ); + + var source2 = @" +class D +{ + void M(C c) + { + c.M() /*T:System.Collections.Generic.IEnumerable!*/ ; + c.M2() /*T:System.Collections.Generic.IEnumerable!*/ ; + } +} +"; + var comp2 = CreateCompilation(source2, references: new[] { comp.EmitToImageReference() }); + comp2.VerifyTypes(); + } + + [Fact] + public void Yield_IEnumerableT_LocalFunction() + { + var source = @" +class C +{ + void Method() + { + _ = M(); + _ = M2(); + + System.Collections.Generic.IEnumerable M() + { + yield return null; // 1 + yield return """"; + yield return null; // 2 + yield break; + } + System.Collections.Generic.IEnumerable M2() + { + yield return null; + } + } +}"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (11,26): warning CS8603: Possible null reference return. + // yield return null; // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(11, 26), + // (13,26): warning CS8603: Possible null reference return. + // yield return null; // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(13, 26) + ); + } + + [Fact] + public void Yield_IEnumerableT_GenericT() + { + var source = @" +class C +{ + System.Collections.Generic.IEnumerable M() + { + yield return default; // 1 + } + System.Collections.Generic.IEnumerable M1() where T : class + { + yield return default; // 2 + } + System.Collections.Generic.IEnumerable M2() where T : class? + { + yield return default; // 3 + } + System.Collections.Generic.IEnumerable M3() where T : class + { + yield return default; + } +}"; + CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()).VerifyDiagnostics( + // (6,22): warning CS8603: Possible null reference return. + // yield return default; // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "default").WithLocation(6, 22), + // (10,22): warning CS8603: Possible null reference return. + // yield return default; // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "default").WithLocation(10, 22), + // (14,22): warning CS8603: Possible null reference return. + // yield return default; // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "default").WithLocation(14, 22) + ); + } + + [Fact] + public void Yield_IEnumerableT_ErrorValue() + { + var source = @" +class C +{ + System.Collections.Generic.IEnumerable M() + { + yield return bad; + } +}"; + CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()).VerifyDiagnostics( + // (6,22): error CS0103: The name 'bad' does not exist in the current context + // yield return bad; + Diagnostic(ErrorCode.ERR_NameNotInContext, "bad").WithArguments("bad").WithLocation(6, 22), + // (6,22): warning CS8619: Nullability of reference types in value of type '?' doesn't match target type 'string'. + // yield return bad; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "bad").WithArguments("?", "string").WithLocation(6, 22) + ); + } + + [Fact] + public void Yield_IEnumerableT_ErrorValue2() + { + var source = @" +static class C +{ + static System.Collections.Generic.IEnumerable M(object? x) + { + yield return (C)x; + } + static System.Collections.Generic.IEnumerable M(object? y) + { + yield return (C?)y; + } +}"; + CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()).VerifyDiagnostics( + // (6,22): error CS0716: Cannot convert to static type 'C' + // yield return (C)x; + Diagnostic(ErrorCode.ERR_ConvertToStaticClass, "(C)x").WithArguments("C").WithLocation(6, 22), + // (6,22): warning CS8600: Converting null literal or possible null value to non-nullable type. + // yield return (C)x; + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(C)x").WithLocation(6, 22), + // (6,22): warning CS8603: Possible null reference return. + // yield return (C)x; + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "(C)x").WithLocation(6, 22), + // (8,60): error CS0111: Type 'C' already defines a member called 'M' with the same parameter types + // static System.Collections.Generic.IEnumerable M(object? y) + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M").WithArguments("M", "C").WithLocation(8, 60), + // (10,22): error CS0716: Cannot convert to static type 'C' + // yield return (C?)y; + Diagnostic(ErrorCode.ERR_ConvertToStaticClass, "(C?)y").WithArguments("C").WithLocation(10, 22) + ); + } + + [Fact] + public void Yield_IEnumerableT_NoValue() + { + var source = @" +class C +{ + System.Collections.Generic.IEnumerable M() + { + yield return; + } +}"; + CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()).VerifyDiagnostics( + // (6,9): warning CS8619: Nullability of reference types in value of type '?' doesn't match target type 'string'. + // yield return; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "yield return;").WithArguments("?", "string").WithLocation(6, 9), + // (6,15): error CS1627: Expression expected after yield return + // yield return; + Diagnostic(ErrorCode.ERR_EmptyYield, "return").WithLocation(6, 15) + ); + } + + [Fact] + public void Yield_IEnumeratorT() + { + var source = @" +class C +{ + System.Collections.Generic.IEnumerator M() + { + yield return null; // 1 + yield return """"; + } + System.Collections.Generic.IEnumerator M2() + { + yield return null; + } +}"; + CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()).VerifyDiagnostics( + // (6,22): warning CS8603: Possible null reference return. + // yield return null; // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(6, 22) + ); + } + + [Fact] + public void Yield_IEnumeratorT_LocalFunction() + { + var source = @" +class C +{ + void Method() + { + _ = M(); + _ = M2(); + + System.Collections.Generic.IEnumerator M() + { + yield return null; // 1 + yield return """"; + } + System.Collections.Generic.IEnumerator M2() + { + yield return null; + } + } +}"; + CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()).VerifyDiagnostics( + // (11,26): warning CS8603: Possible null reference return. + // yield return null; // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(11, 26) + ); + } + + [Fact] + public void Yield_IEnumerable() + { + var source = @" +class C +{ + System.Collections.IEnumerable M() + { + yield return null; + } +}"; + CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()).VerifyDiagnostics(); + } + + [Fact] + public void Yield_IEnumerator() + { + var source = @" +class C +{ + System.Collections.IEnumerator M() + { + yield return null; + yield return null; + } +}"; + CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()).VerifyDiagnostics(); + } + + [Fact, CompilerTrait(CompilerFeature.AsyncStreams)] + public void Yield_IAsyncEnumerable() + { + var source = @" +using System.Collections.Generic; +using System.Threading.Tasks; +class C +{ + public static async IAsyncEnumerable M() + { + yield return null; // 1 + yield return null; // 2 + await Task.Delay(1); + yield break; + } + public static async IAsyncEnumerable M2() + { + yield return null; + yield return null; + await Task.Delay(1); + } + void Method() + { + _ = local(); + _ = local2(); + + async IAsyncEnumerable local() + { + yield return null; // 3 + await Task.Delay(1); + yield break; + } + async IAsyncEnumerable local2() + { + yield return null; + await Task.Delay(1); + } + } +}"; + CreateCompilationWithTasksExtensions(new[] { source, AsyncStreamsTypes }, options: WithNonNullTypesTrue()).VerifyDiagnostics( + // (8,22): warning CS8603: Possible null reference return. + // yield return null; // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(8, 22), + // (9,22): warning CS8603: Possible null reference return. + // yield return null; // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(9, 22), + // (26,26): warning CS8603: Possible null reference return. + // yield return null; // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(26, 26) + ); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/31057"), CompilerTrait(CompilerFeature.AsyncStreams)] + public void Yield_IAsyncEnumerator() + { + var source = @" +using System.Collections.Generic; +using System.Threading.Tasks; +class C +{ + async IAsyncEnumerator M() + { + yield return null; // 1 + yield return null; // 2 + await Task.Delay(1); + yield break; + } + async IAsyncEnumerator M2() + { + yield return null; + yield return null; + await Task.Delay(1); + } + void Method() + { + _ = local(); + _ = local2(); + + async IAsyncEnumerator local() + { + yield return null; // 3 + await Task.Delay(1); + } + async IAsyncEnumerator local2() + { + yield return null; + await Task.Delay(1); + yield break; + } + } +}"; + CreateCompilationWithTasksExtensions(new[] { source, AsyncStreamsTypes }, options: WithNonNullTypesTrue()).VerifyDiagnostics( + // (8,22): warning CS8603: Possible null reference return. + // yield return null; // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(8, 22), + // (9,22): warning CS8603: Possible null reference return. + // yield return null; // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(9, 22), + // (26,26): warning CS8603: Possible null reference return. + // yield return null; // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(26, 26) + ); + } + [Fact] public void Await_01() { diff --git a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs index 40958cc511e81..ceb31b6b644af 100644 --- a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs @@ -108,6 +108,241 @@ public class AssertsFalseAttribute : Attribute public AssertsFalseAttribute () { } } } +"; + + protected const string AsyncStreamsTypes = @" +namespace System.Collections.Generic +{ + public interface IAsyncEnumerable + { + IAsyncEnumerator GetAsyncEnumerator(); + } + + public interface IAsyncEnumerator : System.IAsyncDisposable + { + System.Threading.Tasks.ValueTask MoveNextAsync(); + T Current { get; } + } +} +namespace System +{ + public interface IAsyncDisposable + { + System.Threading.Tasks.ValueTask DisposeAsync(); + } +} + +#nullable disable + +namespace System.Runtime.CompilerServices +{ + public interface IStrongBox + { + ref T Value { get; } + } +} + +namespace System.Threading.Tasks +{ + using System.Runtime.CompilerServices; + using System.Runtime.ExceptionServices; + using System.Threading.Tasks.Sources; + + public struct ManualResetValueTaskSourceLogic + { + private static readonly Action s_sentinel = new Action(s => throw new InvalidOperationException()); + + private readonly IStrongBox> _parent; + private Action _continuation; + private object _continuationState; + private object _capturedContext; + private ExecutionContext _executionContext; + private bool _completed; + private TResult _result; + private ExceptionDispatchInfo _error; + private short _version; + + public ManualResetValueTaskSourceLogic(IStrongBox> parent) + { + _parent = parent ?? throw new ArgumentNullException(nameof(parent)); + _continuation = null; + _continuationState = null; + _capturedContext = null; + _executionContext = null; + _completed = false; + _result = default; + _error = null; + _version = 0; + } + + public short Version => _version; + + private void ValidateToken(short token) + { + if (token != _version) + { + throw new InvalidOperationException(); + } + } + + public ValueTaskSourceStatus GetStatus(short token) + { + ValidateToken(token); + + return + !_completed ? ValueTaskSourceStatus.Pending : + _error == null ? ValueTaskSourceStatus.Succeeded : + _error.SourceException is OperationCanceledException ? ValueTaskSourceStatus.Canceled : + ValueTaskSourceStatus.Faulted; + } + + public TResult GetResult(short token) + { + ValidateToken(token); + + if (!_completed) + { + throw new InvalidOperationException(); + } + + _error?.Throw(); + return _result; + } + + public void Reset() + { + _version++; + + _completed = false; + _continuation = null; + _continuationState = null; + _result = default; + _error = null; + _executionContext = null; + _capturedContext = null; + } + + public void OnCompleted(Action continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) + { + if (continuation == null) + { + throw new ArgumentNullException(nameof(continuation)); + } + ValidateToken(token); + + if ((flags & ValueTaskSourceOnCompletedFlags.FlowExecutionContext) != 0) + { + _executionContext = ExecutionContext.Capture(); + } + + if ((flags & ValueTaskSourceOnCompletedFlags.UseSchedulingContext) != 0) + { + SynchronizationContext sc = SynchronizationContext.Current; + if (sc != null && sc.GetType() != typeof(SynchronizationContext)) + { + _capturedContext = sc; + } + else + { + TaskScheduler ts = TaskScheduler.Current; + if (ts != TaskScheduler.Default) + { + _capturedContext = ts; + } + } + } + + _continuationState = state; + if (Interlocked.CompareExchange(ref _continuation, continuation, null) != null) + { + _executionContext = null; + + object cc = _capturedContext; + _capturedContext = null; + + switch (cc) + { + case null: + Task.Factory.StartNew(continuation, state, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); + break; + + case SynchronizationContext sc: + sc.Post(s => + { + var tuple = (Tuple, object>)s; + tuple.Item1(tuple.Item2); + }, Tuple.Create(continuation, state)); + break; + + case TaskScheduler ts: + Task.Factory.StartNew(continuation, state, CancellationToken.None, TaskCreationOptions.DenyChildAttach, ts); + break; + } + } + } + + public void SetResult(TResult result) + { + _result = result; + SignalCompletion(); + } + + public void SetException(Exception error) + { + _error = ExceptionDispatchInfo.Capture(error); + SignalCompletion(); + } + + private void SignalCompletion() + { + if (_completed) + { + throw new InvalidOperationException(); + } + _completed = true; + + if (Interlocked.CompareExchange(ref _continuation, s_sentinel, null) != null) + { + if (_executionContext != null) + { + ExecutionContext.Run( + _executionContext, + s => ((IStrongBox>)s).Value.InvokeContinuation(), + _parent ?? throw new InvalidOperationException()); + } + else + { + InvokeContinuation(); + } + } + } + + private void InvokeContinuation() + { + object cc = _capturedContext; + _capturedContext = null; + + switch (cc) + { + case null: + _continuation(_continuationState); + break; + + case SynchronizationContext sc: + sc.Post(s => + { + ref ManualResetValueTaskSourceLogic logicRef = ref ((IStrongBox>)s).Value; + logicRef._continuation(logicRef._continuationState); + }, _parent ?? throw new InvalidOperationException()); + break; + + case TaskScheduler ts: + Task.Factory.StartNew(_continuation, _continuationState, CancellationToken.None, TaskCreationOptions.DenyChildAttach, ts); + break; + } + } + } +} "; protected static CSharpCompilationOptions WithNonNullTypesTrue(CSharpCompilationOptions options = null) diff --git a/src/Test/Utilities/Portable/Traits/CompilerFeature.cs b/src/Test/Utilities/Portable/Traits/CompilerFeature.cs index 25c13df1edac2..2615efc44acaf 100644 --- a/src/Test/Utilities/Portable/Traits/CompilerFeature.cs +++ b/src/Test/Utilities/Portable/Traits/CompilerFeature.cs @@ -33,5 +33,6 @@ public enum CompilerFeature StackAllocInitializer, NullCoalescingAssignment, AsyncStreams, + NullableReferenceTypes, } } From 69e057136e7546253ad532b395f6c455d65706ad Mon Sep 17 00:00:00 2001 From: Neal Gafter Date: Tue, 13 Nov 2018 12:16:37 -0800 Subject: [PATCH 08/10] Track nullable state in try statements (#31082) Fixes #30561 --- .../Portable/FlowAnalysis/AbstractFlowPass.cs | 106 ++- .../Portable/FlowAnalysis/DataFlowPass.cs | 83 +-- .../Portable/FlowAnalysis/NullableWalker.cs | 16 +- .../FlowAnalysis/PreciseAbstractFlowPass.cs | 5 - .../Semantics/NullableReferenceTypesTests.cs | 664 ++++++++++++++++++ 5 files changed, 776 insertions(+), 98 deletions(-) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index ca1d5883039a1..a61011ee3209e 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.CSharp internal abstract partial class AbstractFlowPass : PreciseAbstractFlowPass where TLocalState : PreciseAbstractFlowPass.AbstractLocalState { - protected readonly bool trackUnassignments; // for the data flows out walker, we track unassignments as well as assignments + private readonly bool _trackUnassignments; // for the data flows out walker, we track unassignments as well as assignments protected AbstractFlowPass( CSharpCompilation compilation, @@ -26,7 +26,7 @@ internal abstract partial class AbstractFlowPass : PreciseAbstractF bool trackUnassignments = false) : base(compilation, member, node) { - this.trackUnassignments = trackUnassignments; + this._trackUnassignments = trackUnassignments; } protected AbstractFlowPass( @@ -39,11 +39,25 @@ internal abstract partial class AbstractFlowPass : PreciseAbstractF bool trackUnassignments = false) : base(compilation, member, node, firstInRegion, lastInRegion, trackRegions) { - this.trackUnassignments = trackUnassignments; + this._trackUnassignments = trackUnassignments; } protected abstract void UnionWith(ref TLocalState self, ref TLocalState other); + /// + /// Nontrivial implementation is required for DataFlowsOutWalker or any flow analysis pass that "tracks + /// unassignments" like the nullable walker. The result should be a state, for each variable, that is + /// the strongest result possible (i.e. definitely assigned for the data flow passes, or not null for + /// the nullable analysis). Slightly more formally, this should be a reachable state that won't affect + /// another reachable state when this is intersected with the other state. + /// + protected virtual TLocalState AllBitsSet() + { + return default(TLocalState); + } + + #region TryStatements + public override BoundNode VisitTryStatement(BoundTryStatement node) { var oldPending = SavePending(); // we do not allow branches into a try statement @@ -52,13 +66,13 @@ public override BoundNode VisitTryStatement(BoundTryStatement node) // use this state to resolve all the branches introduced and internal to try/catch var pendingBeforeTry = SavePending(); - VisitTryBlock(node.TryBlock, node, ref initialState); + VisitTryBlockWithUnassignments(node.TryBlock, node, ref initialState); var finallyState = initialState.Clone(); var endState = this.State; foreach (var catchBlock in node.CatchBlocks) { SetState(initialState.Clone()); - VisitCatchBlock(catchBlock, ref finallyState); + VisitCatchBlockWithUnassignments(catchBlock, ref finallyState); IntersectWith(ref endState, ref this.State); } @@ -84,20 +98,20 @@ public override BoundNode VisitTryStatement(BoundTryStatement node) // we will need pending branches as they were before finally later var tryAndCatchPending = SavePending(); var unsetInFinally = AllBitsSet(); - VisitFinallyBlock(node.FinallyBlockOpt, ref unsetInFinally); + VisitFinallyBlockWithUnassignments(node.FinallyBlockOpt, ref unsetInFinally); foreach (var pend in tryAndCatchPending.PendingBranches) { if (pend.Branch == null) continue; // a tracked exception if (pend.Branch.Kind != BoundKind.YieldReturnStatement) { UnionWith(ref pend.State, ref this.State); - if (trackUnassignments) IntersectWith(ref pend.State, ref unsetInFinally); + if (_trackUnassignments) IntersectWith(ref pend.State, ref unsetInFinally); } } RestorePending(tryAndCatchPending); UnionWith(ref endState, ref this.State); - if (trackUnassignments) IntersectWith(ref endState, ref unsetInFinally); + if (_trackUnassignments) IntersectWith(ref endState, ref unsetInFinally); } SetState(endState); @@ -105,6 +119,80 @@ public override BoundNode VisitTryStatement(BoundTryStatement node) return null; } + protected Optional _tryState; + + private void VisitTryBlockWithUnassignments(BoundStatement tryBlock, BoundTryStatement node, ref TLocalState tryState) + { + if (_trackUnassignments) + { + Optional oldTryState = _tryState; + _tryState = AllBitsSet(); + VisitTryBlock(tryBlock, node, ref tryState); + var tempTryStateValue = _tryState.Value; + IntersectWith(ref tryState, ref tempTryStateValue); + if (oldTryState.HasValue) + { + var oldTryStateValue = oldTryState.Value; + IntersectWith(ref oldTryStateValue, ref tempTryStateValue); + oldTryState = oldTryStateValue; + } + + _tryState = oldTryState; + } + else + { + VisitTryBlock(tryBlock, node, ref tryState); + } + } + + private void VisitCatchBlockWithUnassignments(BoundCatchBlock catchBlock, ref TLocalState finallyState) + { + if (_trackUnassignments) + { + Optional oldTryState = _tryState; + _tryState = AllBitsSet(); + VisitCatchBlock(catchBlock, ref finallyState); + var tempTryStateValue = _tryState.Value; + IntersectWith(ref finallyState, ref tempTryStateValue); + if (oldTryState.HasValue) + { + var oldTryStateValue = oldTryState.Value; + IntersectWith(ref oldTryStateValue, ref tempTryStateValue); + oldTryState = oldTryStateValue; + } + + _tryState = oldTryState; + } + else + { + VisitCatchBlock(catchBlock, ref finallyState); + } + } + + private void VisitFinallyBlockWithUnassignments(BoundStatement finallyBlock, ref TLocalState unsetInFinally) + { + if (_trackUnassignments) + { + Optional oldTryState = _tryState; + _tryState = AllBitsSet(); + VisitFinallyBlock(finallyBlock, ref unsetInFinally); + var tempTryStateValue = _tryState.Value; + IntersectWith(ref unsetInFinally, ref tempTryStateValue); + if (oldTryState.HasValue) + { + var oldTryStateValue = oldTryState.Value; + IntersectWith(ref oldTryStateValue, ref tempTryStateValue); + oldTryState = oldTryStateValue; + } + + _tryState = oldTryState; + } + else + { + VisitFinallyBlock(finallyBlock, ref unsetInFinally); + } + } + protected virtual void VisitTryBlock(BoundStatement tryBlock, BoundTryStatement node, ref TLocalState tryState) { VisitStatement(tryBlock); @@ -130,5 +218,7 @@ protected virtual void VisitFinallyBlock(BoundStatement finallyBlock, ref TLocal { VisitStatement(finallyBlock); // this should generate no pending branches } + + #endregion TryStatements } } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs index bca81cb3ac975..411e0eaa11690 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs @@ -28,12 +28,6 @@ namespace Microsoft.CodeAnalysis.CSharp { -#if REFERENCE_STATE - using OptionalState = Optional; -#else - using OptionalState = Nullable; -#endif - /// /// Implement C# data flow analysis (definite assignment). /// @@ -1332,7 +1326,7 @@ protected override LocalState ReachableState() protected override LocalState AllBitsSet() { var result = new LocalState(BitVector.AllSet(nextVariableSlot)); - result.Assigned[0] = false; + result.Assigned[0] = false; // make the state reachable return result; } @@ -1952,56 +1946,7 @@ public override BoundNode VisitBaseReference(BoundBaseReference node) return null; } -#region TryStatements - private OptionalState _tryState; - - protected override void VisitTryBlock(BoundStatement tryBlock, BoundTryStatement node, ref LocalState tryState) - { - if (trackUnassignments) - { - OptionalState oldTryState = _tryState; - _tryState = AllBitsSet(); - base.VisitTryBlock(tryBlock, node, ref tryState); - var tts = _tryState.Value; - IntersectWith(ref tryState, ref tts); - if (oldTryState.HasValue) - { - var ots = oldTryState.Value; - IntersectWith(ref ots, ref tts); - oldTryState = ots; - } - _tryState = oldTryState; - } - else - { - base.VisitTryBlock(tryBlock, node, ref tryState); - } - } - protected override void VisitCatchBlock(BoundCatchBlock catchBlock, ref LocalState finallyState) - { - if (trackUnassignments) - { - OptionalState oldTryState = _tryState; - _tryState = AllBitsSet(); - VisitCatchBlockInternal(catchBlock, ref finallyState); - var tts = _tryState.Value; - IntersectWith(ref finallyState, ref tts); - if (oldTryState.HasValue) - { - var ots = oldTryState.Value; - IntersectWith(ref ots, ref tts); - oldTryState = ots; - } - _tryState = oldTryState; - } - else - { - VisitCatchBlockInternal(catchBlock, ref finallyState); - } - } - - private void VisitCatchBlockInternal(BoundCatchBlock catchBlock, ref LocalState finallyState) { DeclareVariables(catchBlock.Locals); @@ -2019,32 +1964,6 @@ private void VisitCatchBlockInternal(BoundCatchBlock catchBlock, ref LocalState } } - protected override void VisitFinallyBlock(BoundStatement finallyBlock, ref LocalState unsetInFinally) - { - if (trackUnassignments) - { - OptionalState oldTryState = _tryState; - _tryState = AllBitsSet(); - base.VisitFinallyBlock(finallyBlock, ref unsetInFinally); - var tts = _tryState.Value; - IntersectWith(ref unsetInFinally, ref tts); - if (oldTryState.HasValue) - { - var ots = oldTryState.Value; - IntersectWith(ref ots, ref tts); - oldTryState = ots; - } - - _tryState = oldTryState; - } - else - { - base.VisitFinallyBlock(finallyBlock, ref unsetInFinally); - } - } - -#endregion TryStatements - public override BoundNode VisitFieldAccess(BoundFieldAccess node) { var result = base.VisitFieldAccess(node); diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 3e87194309e36..02288a4eb4fb9 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -136,7 +136,7 @@ protected override void Free() ArrayBuilder<(RefKind, TypeSymbolWithAnnotations)> returnTypes, VariableState initialState, Action callbackOpt) - : base(compilation, method, node, new EmptyStructTypeCache(compilation, dev12CompilerCompatibility: false), trackUnassignments: false) + : base(compilation, method, node, new EmptyStructTypeCache(compilation, dev12CompilerCompatibility: false), trackUnassignments: true) { _sourceAssembly = (method is null) ? null : (SourceAssemblySymbol)method.ContainingAssembly; _callbackOpt = callbackOpt; @@ -629,11 +629,18 @@ private void TrackNullableStateForAssignment(BoundExpression value, TypeSymbolWi if (targetSlot >= this.State.Capacity) Normalize(ref this.State); // https://github.com/dotnet/roslyn/issues/29968 Remove isByRefTarget check? - this.State[targetSlot] = isByRefTarget ? + var newState = isByRefTarget ? // Since reference can point to the heap, we cannot assume the value is not null after this assignment, // regardless of what value is being assigned. (targetType.IsNullable == true) ? targetType.NullableAnnotation : NullableAnnotation.Unknown : valueType.NullableAnnotation; + this.State[targetSlot] = newState; + if (newState.IsAnyNullable() && _tryState.HasValue) + { + var state = _tryState.Value; + state[targetSlot] = NullableAnnotation.NullableBasedOnAnalysis; + _tryState = state; + } // https://github.com/dotnet/roslyn/issues/29968 Might this clear state that // should be copied in InheritNullableStateOfTrackableType? @@ -826,7 +833,10 @@ protected override LocalState UnreachableState() protected override LocalState AllBitsSet() { - return new LocalState(reachable: true, new ArrayBuilder(nextVariableSlot)); + // Create a reachable state in which all variables are known to be non-null. + var builder = new ArrayBuilder(nextVariableSlot); + builder.AddMany(NullableAnnotation.NotNullableBasedOnAnalysis, nextVariableSlot); + return new LocalState(reachable: true, builder); } private void EnterParameters() diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs index bed20ffa04b78..69b31aa6760c6 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs @@ -1572,11 +1572,6 @@ public override BoundNode VisitTryStatement(BoundTryStatement node) return null; } - protected virtual LocalState AllBitsSet() // required for DataFlowsOutWalker - { - return default(LocalState); - } - public sealed override BoundNode VisitReturnStatement(BoundReturnStatement node) { var result = VisitReturnStatementNoAdjust(node); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index 66458e392a63c..30c5ac490f3e8 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -56479,5 +56479,669 @@ static void M(object x) // c2.F().ToString(); Diagnostic(ErrorCode.ERR_ObjectProhibited, "c2.F").WithArguments("C.F()").WithLocation(14, 9)); } + + [Fact, WorkItem(30561, "https://github.com/dotnet/roslyn/issues/30561")] + public void SetNullableStateInFinally_01() + { + var source = +@"public static class Program +{ + public static void Main() + { + string? s = string.Empty; + try + { + } + finally + { + s = null; + } + + _ = s.Length; // warning + } +} +"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (14,13): warning CS8602: Possible dereference of a null reference. + // _ = s.Length; // warning + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(14, 13) + ); + } + + [Fact, WorkItem(30561, "https://github.com/dotnet/roslyn/issues/30561")] + public void SetNullableStateInTry_02() + { + var source = +@"public static class Program +{ + public static int Main() + { + string? s = string.Empty; + try + { + s = null; + MayThrow(); + s = string.Empty; + } + catch (System.Exception) + { + } + + return s.Length; // warning: possibly null + } + static void MayThrow() + { + throw null; + } +} +"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (16,16): warning CS8602: Possible dereference of a null reference. + // return s.Length; // warning: possibly null + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(16, 16) + ); + } + + [Fact, WorkItem(30561, "https://github.com/dotnet/roslyn/issues/30561")] + public void SetNullableStateInTry_03() + { + var source = +@"public static class Program +{ + public static int Main() + { + string? s = string.Empty; + try + { + s = null; + MayThrow(); + s = string.Empty; + } + catch (System.Exception) + { + return s.Length; // warning: possibly null + } + + return s.Length; + } + static void MayThrow() + { + throw null; + } +} +"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (14,20): warning CS8602: Possible dereference of a null reference. + // return s.Length; // warning: possibly null + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(14, 20) + ); + } + + [Fact, WorkItem(30561, "https://github.com/dotnet/roslyn/issues/30561")] + public void SetNullableStateInTry_04() + { + var source = +@"public static class Program +{ + public static int Main() + { + string? s = string.Empty; + try + { + s = null; + MayThrow(); + s = string.Empty; + } + catch (System.Exception) + { + _ = s.Length; // warning 1 + } + finally + { + _ = s.Length; // warning 2 + } + + return s.Length; // ok (previously dereferenced) + } + static void MayThrow() + { + throw null; + } +} +"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (14,17): warning CS8602: Possible dereference of a null reference. + // _ = s.Length; // warning 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(14, 17), + // (18,17): warning CS8602: Possible dereference of a null reference. + // _ = s.Length; // warning 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(18, 17) + ); + } + + [Fact, WorkItem(30561, "https://github.com/dotnet/roslyn/issues/30561")] + public void SetNullableStateInTry_05() + { + var source = +@"public static class Program +{ + public static int Main() + { + string? s = null; + try + { + MayThrow(); + s = string.Empty; + _ = s.Length; // ok + } + catch (System.Exception) + { + _ = s.Length; // warning 1 + } + finally + { + _ = s.Length; // warning 2 + } + + return s.Length; // ok (previously dereferenced) + } + static void MayThrow() + { + throw null; + } +} +"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (14,17): warning CS8602: Possible dereference of a null reference. + // _ = s.Length; // warning 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(14, 17), + // (18,17): warning CS8602: Possible dereference of a null reference. + // _ = s.Length; // warning 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(18, 17) + ); + } + + [Fact, WorkItem(30561, "https://github.com/dotnet/roslyn/issues/30561")] + public void SetNullableStateInTry_06() + { + var source = +@"public static class Program +{ + public static int Main() + { + string? s = null; + try + { + MayThrow(); + s = string.Empty; + _ = s.Length; // ok + } + catch (System.NullReferenceException) + { + _ = s.Length; // warning 1 + } + catch (System.Exception) + { + _ = s.Length; // warning 2 + } + finally + { + _ = s.Length; // warning 3 + } + + return s.Length; // ok (previously dereferenced) + } + static void MayThrow() + { + throw null; + } +} +"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (14,17): warning CS8602: Possible dereference of a null reference. + // _ = s.Length; // warning 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(14, 17), + // (18,17): warning CS8602: Possible dereference of a null reference. + // _ = s.Length; // warning 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(18, 17), + // (22,17): warning CS8602: Possible dereference of a null reference. + // _ = s.Length; // warning 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(22, 17) + ); + } + + [Fact, WorkItem(30561, "https://github.com/dotnet/roslyn/issues/30561")] + public void SetNullableStateInTry_07() + { + var source = +@"public static class Program +{ + public static int Main() + { + string? s = null; + try + { + MayThrow(); + s = string.Empty; + _ = s.Length; // ok + } + catch (System.NullReferenceException) + { + _ = s.Length; // warning 1 + } + catch (System.Exception) + { + _ = s.Length; // warning 2 + } + + return s.Length; // ok + } + static void MayThrow() + { + throw null; + } +} +"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (14,17): warning CS8602: Possible dereference of a null reference. + // _ = s.Length; // warning 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(14, 17), + // (18,17): warning CS8602: Possible dereference of a null reference. + // _ = s.Length; // warning 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(18, 17) + ); + } + + [Fact, WorkItem(30561, "https://github.com/dotnet/roslyn/issues/30561")] + public void SetNullableStateBeforeTry_08() + { + var source = +@"public static class Program +{ + public static int Main() + { + string? s = null; + try + { + MayThrow(); + _ = s.Length; // warning 1 + } + catch (System.NullReferenceException) + { + _ = s.Length; // warning 2 + } + catch (System.Exception) + { + _ = s.Length; // warning 3 + } + + return s.Length; // ok (previously dereferenced) + } + static void MayThrow() + { + throw null; + } +} +"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (9,17): warning CS8602: Possible dereference of a null reference. + // _ = s.Length; // warning 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(9, 17), + // (13,17): warning CS8602: Possible dereference of a null reference. + // _ = s.Length; // warning 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(13, 17), + // (17,17): warning CS8602: Possible dereference of a null reference. + // _ = s.Length; // warning 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(17, 17) + ); + } + + [Fact, WorkItem(30561, "https://github.com/dotnet/roslyn/issues/30561")] + public void SetNullableStateInTry_09() + { + var source = +@"public static class Program +{ + public static int Main() + { + string? s = string.Empty; + try + { + s = null; + MayThrow(); + s = string.Empty; + } + finally + { + _ = s.Length; // warning + } + + return s.Length; // ok + } + static void MayThrow() + { + throw null; + } +} +"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (14,17): warning CS8602: Possible dereference of a null reference. + // _ = s.Length; // warning + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(14, 17) + ); + } + + [Fact, WorkItem(30561, "https://github.com/dotnet/roslyn/issues/30561")] + public void SetNullableStateInCatch_10() + { + var source = +@"public static class Program +{ + public static int Main() + { + string? s = string.Empty; + try + { + MayThrow(); + } + catch (System.Exception) + { + s = null; + MayThrow(); + s = string.Empty; + } + finally + { + _ = s.Length; // warning + } + + return s.Length; // ok + } + static void MayThrow() + { + throw null; + } +} +"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (18,17): warning CS8602: Possible dereference of a null reference. + // _ = s.Length; // warning + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(18, 17) + ); + } + + [Fact, WorkItem(30561, "https://github.com/dotnet/roslyn/issues/30561")] + public void SetNullableStateInNestedTry_01() + { + var source = +@"public static class Program +{ + public static void Main() + { + { + string? s = string.Empty; + try + { + try + { + s = null; + } + catch (System.Exception) + { + } + finally + { + } + } + catch (System.Exception) + { + } + finally + { + } + + _ = s.Length; // warning 1a + } + + { + string? s = string.Empty; + try + { + try + { + } + catch (System.Exception) + { + s = null; + } + finally + { + } + } + catch (System.Exception) + { + } + finally + { + } + + _ = s.Length; // warning 1b + } + + { + string? s = string.Empty; + try + { + try + { + } + catch (System.Exception) + { + } + finally + { + s = null; + } + } + catch (System.Exception) + { + } + finally + { + } + + _ = s.Length; // warning 1c + } + + { + string? s = string.Empty; + try + { + } + catch (System.Exception) + { + try + { + s = null; + } + catch (System.Exception) + { + } + finally + { + } + } + finally + { + _ = s.Length; // warning 2a + } + } + + { + string? s = string.Empty; + try + { + } + catch (System.Exception) + { + try + { + } + catch (System.Exception) + { + s = null; + } + finally + { + } + } + finally + { + _ = s.Length; // warning 2b + } + } + + { + string? s = string.Empty; + try + { + } + catch (System.Exception) + { + try + { + } + catch (System.Exception) + { + } + finally + { + s = null; + } + } + finally + { + _ = s.Length; // warning 2c + } + } + + { + string? s = string.Empty; + try + { + } + catch (System.Exception) + { + } + finally + { + try + { + s = null; + } + catch (System.Exception) + { + } + finally + { + } + } + + _ = s.Length; // warning 3a + } + + { + string? s = string.Empty; + try + { + } + catch (System.Exception) + { + } + finally + { + try + { + } + catch (System.Exception) + { + s = null; + } + finally + { + } + } + + _ = s.Length; // warning 3b + } + + { + string? s = string.Empty; + try + { + } + catch (System.Exception) + { + } + finally + { + try + { + } + catch (System.Exception) + { + } + finally + { + s = null; + } + } + + _ = s.Length; // warning 3c + } + } +} +"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (27,17): warning CS8602: Possible dereference of a null reference. + // _ = s.Length; // warning 1a + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(27, 17), + // (52,17): warning CS8602: Possible dereference of a null reference. + // _ = s.Length; // warning 1b + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(52, 17), + // (77,17): warning CS8602: Possible dereference of a null reference. + // _ = s.Length; // warning 1c + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(77, 17), + // (100,21): warning CS8602: Possible dereference of a null reference. + // _ = s.Length; // warning 2a + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(100, 21), + // (124,21): warning CS8602: Possible dereference of a null reference. + // _ = s.Length; // warning 2b + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(124, 21), + // (148,21): warning CS8602: Possible dereference of a null reference. + // _ = s.Length; // warning 2c + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(148, 21), + // (174,17): warning CS8602: Possible dereference of a null reference. + // _ = s.Length; // warning 3a + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(174, 17), + // (199,17): warning CS8602: Possible dereference of a null reference. + // _ = s.Length; // warning 3b + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(199, 17), + // (224,17): warning CS8602: Possible dereference of a null reference. + // _ = s.Length; // warning 3c + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(224, 17) + ); + } } } From 3edd05e1ba1171bdde6f12a35ed69a392858b6c4 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Tue, 13 Nov 2018 14:54:25 -0800 Subject: [PATCH 09/10] Avoid redundant warnings from explicit cast (#31138) --- .../Portable/FlowAnalysis/NullableWalker.cs | 16 ++- .../Semantics/NullableReferenceTypesTests.cs | 105 ++++++++---------- 2 files changed, 62 insertions(+), 59 deletions(-) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 02288a4eb4fb9..c8f03316ac349 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -406,7 +406,6 @@ private new int GetOrCreateSlot(Symbol symbol, int containingSlot = 0) return base.GetOrCreateSlot(symbol, containingSlot); } - // https://github.com/dotnet/roslyn/issues/29952 Remove use of MakeSlot. protected override int MakeSlot(BoundExpression node) { switch (node.Kind) @@ -3300,6 +3299,21 @@ private bool HasTopLevelNullabilityConversion(TypeSymbolWithAnnotations source, goto case ConversionKind.Identity; case ConversionKind.Identity: + // If the operand is an explicit conversion, and this identity conversion + // is converting to the same type including nullability, skip the conversion + // to avoid reporting redundant warnings. Also check useLegacyWarnings + // since that value was used when reporting warnings for the explicit cast. + if (useLegacyWarnings && operandOpt?.Kind == BoundKind.Conversion) + { + var operandConversion = (BoundConversion)operandOpt; + var explicitType = operandConversion.ConversionGroupOpt.ExplicitType; + if (!explicitType.IsNull && explicitType.Equals(targetTypeWithNullability, TypeCompareKind.IgnoreInsignificantNullableModifiersDifference)) + { + return operandType; + } + } + goto case ConversionKind.ImplicitReference; + case ConversionKind.ImplicitReference: case ConversionKind.ExplicitReference: if (operandType.IsNull && operandOpt.IsLiteralNullOrDefault()) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index 30c5ac490f3e8..73b27736ed5da 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -26157,9 +26157,6 @@ class CL4 : CL3 {} // (70,19): warning CS8600: Converting null literal or possible null value to non-nullable type. // CL4 u12 = (CL4)x12; Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(CL4)x12").WithLocation(70, 19), - // (70,19): warning CS8600: Converting null literal or possible null value to non-nullable type. - // CL4 u12 = (CL4)x12; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(CL4)x12").WithLocation(70, 19), // (76,22): hidden CS8607: Expression is probably never null. // object v13 = u13 ?? new object(); Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "u13").WithLocation(76, 22), @@ -36037,9 +36034,6 @@ static void F(S s) // (18,16): warning CS8600: Converting null literal or possible null value to non-nullable type. // B b2 = (B)s; Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(B)s").WithLocation(18, 16), - // (18,16): warning CS8600: Converting null literal or possible null value to non-nullable type. - // B b2 = (B)s; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(B)s").WithLocation(18, 16), // (19,9): warning CS8602: Possible dereference of a null reference. // b2.F(); Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "b2").WithLocation(19, 9), @@ -47740,9 +47734,6 @@ class A // (29,16): warning CS8600: Converting null literal or possible null value to non-nullable type. // T3 t = (T3)NullableObject(); // warn: T3 may be non-null Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T3)NullableObject()").WithLocation(29, 16), - // (29,16): warning CS8600: Converting null literal or possible null value to non-nullable type. - // T3 t = (T3)NullableObject(); // warn: T3 may be non-null - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T3)NullableObject()").WithLocation(29, 16), // (35,23): warning CS8603: Possible null reference return. // static T4 F1() => default; // warn: return type T4 may be non-null Diagnostic(ErrorCode.WRN_NullReferenceReturn, "default").WithLocation(35, 23), @@ -47758,9 +47749,6 @@ class A // (39,17): warning CS8600: Converting null literal or possible null value to non-nullable type. // T4 t4 = (T4)NullableObject(); // warn: T4 may be non-null Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T4)NullableObject()").WithLocation(39, 17), - // (39,17): warning CS8600: Converting null literal or possible null value to non-nullable type. - // T4 t4 = (T4)NullableObject(); // warn: T4 may be non-null - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T4)NullableObject()").WithLocation(39, 17), // (46,23): warning CS8603: Possible null reference return. // static T5 F2() => default(T5); // warn: return type T5 may be non-null Diagnostic(ErrorCode.WRN_NullReferenceReturn, "default(T5)").WithLocation(46, 23), @@ -47779,18 +47767,9 @@ class A // (49,17): warning CS8600: Converting null literal or possible null value to non-nullable type. // T5 t5 = (T5)NullableObject(); // warn: T5 may be non-null Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T5)NullableObject()").WithLocation(49, 17), - // (49,17): warning CS8600: Converting null literal or possible null value to non-nullable type. - // T5 t5 = (T5)NullableObject(); // warn: T5 may be non-null - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T5)NullableObject()").WithLocation(49, 17), // (8,17): warning CS8600: Converting null literal or possible null value to non-nullable type. // T1 t1 = (T1)NullableObject(); // warn: T1 may be non-null Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T1)NullableObject()").WithLocation(8, 17), - // (8,17): warning CS8600: Converting null literal or possible null value to non-nullable type. - // T1 t1 = (T1)NullableObject(); // warn: T1 may be non-null - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T1)NullableObject()").WithLocation(8, 17), - // (18,17): warning CS8600: Converting null literal or possible null value to non-nullable type. - // T2 t2 = (T2)NullableObject(); // warn: T2 may be non-null - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T2)NullableObject()").WithLocation(18, 17), // (18,17): warning CS8600: Converting null literal or possible null value to non-nullable type. // T2 t2 = (T2)NullableObject(); // warn: T2 may be non-null Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T2)NullableObject()").WithLocation(18, 17) @@ -51611,6 +51590,31 @@ class A ); } + [WorkItem(23270, "https://github.com/dotnet/roslyn/issues/23270")] + [Fact] + public void NotNullAfterDereference_00() + { + var source = +@"class Program +{ + static void M(object? obj) + { + obj.F(); + obj.ToString(); // 1 + obj.ToString(); + } +} +static class E +{ + internal static void F(this object? obj) { } +}"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (6,9): warning CS8602: Possible dereference of a null reference. + // obj.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "obj").WithLocation(6, 9)); + } + [WorkItem(23270, "https://github.com/dotnet/roslyn/issues/23270")] [Fact] public void NotNullAfterDereference_01() @@ -52907,7 +52911,6 @@ void M(object? x, object y) } } "; - // https://github.com/dotnet/roslyn/issues/30938 - duplicate warnings CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( // (7,13): error CS0266: Cannot implicitly convert type 'object' to 'T'. An explicit conversion exists (are you missing a cast?) // z = x; @@ -52920,9 +52923,6 @@ void M(object? x, object y) Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "y").WithArguments("object", "T").WithLocation(8, 13), // (9,13): warning CS8600: Converting null literal or possible null value to non-nullable type. // z = (T)x; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T)x").WithLocation(9, 13), - // (9,13): warning CS8600: Converting null literal or possible null value to non-nullable type. - // z = (T)x; Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T)x").WithLocation(9, 13) ); } @@ -52951,9 +52951,6 @@ void M(dynamic? x, dynamic y) Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "x").WithLocation(7, 13), // (9,13): warning CS8600: Converting null literal or possible null value to non-nullable type. // z = (T)x; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T)x").WithLocation(9, 13), - // (9,13): warning CS8600: Converting null literal or possible null value to non-nullable type. - // z = (T)x; Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T)x").WithLocation(9, 13) ); } @@ -52989,9 +52986,6 @@ interface I1{} Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "y").WithArguments("object", "T").WithLocation(8, 13), // (9,13): warning CS8600: Converting null literal or possible null value to non-nullable type. // z = (T)x; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T)x").WithLocation(9, 13), - // (9,13): warning CS8600: Converting null literal or possible null value to non-nullable type. - // z = (T)x; Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T)x").WithLocation(9, 13) ); } @@ -53021,9 +53015,6 @@ interface I1{} Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "x").WithLocation(7, 13), // (9,13): warning CS8600: Converting null literal or possible null value to non-nullable type. // z = (T)x; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T)x").WithLocation(9, 13), - // (9,13): warning CS8600: Converting null literal or possible null value to non-nullable type. - // z = (T)x; Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T)x").WithLocation(9, 13) ); } @@ -53059,9 +53050,6 @@ interface I1{} Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "y").WithArguments("object", "T").WithLocation(8, 13), // (9,13): warning CS8600: Converting null literal or possible null value to non-nullable type. // z = (T)x; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T)x").WithLocation(9, 13), - // (9,13): warning CS8600: Converting null literal or possible null value to non-nullable type. - // z = (T)x; Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T)x").WithLocation(9, 13) ); } @@ -53091,9 +53079,6 @@ interface I1{} Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "x").WithLocation(7, 13), // (9,13): warning CS8600: Converting null literal or possible null value to non-nullable type. // z = (T)x; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T)x").WithLocation(9, 13), - // (9,13): warning CS8600: Converting null literal or possible null value to non-nullable type. - // z = (T)x; Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T)x").WithLocation(9, 13) ); } @@ -53122,9 +53107,6 @@ class Outer Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "x").WithLocation(7, 13), // (8,13): warning CS8600: Converting null literal or possible null value to non-nullable type. // y = (U)x; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(U)x").WithLocation(8, 13), - // (8,13): warning CS8600: Converting null literal or possible null value to non-nullable type. - // y = (U)x; Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(U)x").WithLocation(8, 13) ); } @@ -53155,9 +53137,6 @@ interface I1 {} Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "x").WithLocation(7, 13), // (8,13): warning CS8600: Converting null literal or possible null value to non-nullable type. // y = (U)x; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(U)x").WithLocation(8, 13), - // (8,13): warning CS8600: Converting null literal or possible null value to non-nullable type. - // y = (U)x; Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(U)x").WithLocation(8, 13) ); } @@ -53188,9 +53167,6 @@ interface I1 {} Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "x").WithLocation(7, 13), // (8,13): warning CS8600: Converting null literal or possible null value to non-nullable type. // y = (U)x; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(U)x").WithLocation(8, 13), - // (8,13): warning CS8600: Converting null literal or possible null value to non-nullable type. - // y = (U)x; Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(U)x").WithLocation(8, 13) ); } @@ -53265,9 +53241,6 @@ void M(dynamic? x) Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "x").WithLocation(8, 13), // (9,13): warning CS8600: Converting null literal or possible null value to non-nullable type. // y = (T)x; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T)x").WithLocation(9, 13), - // (9,13): warning CS8600: Converting null literal or possible null value to non-nullable type. - // y = (T)x; Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T)x").WithLocation(9, 13) ); } @@ -53312,9 +53285,6 @@ class Outer Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "y").WithArguments("Outer", "T").WithLocation(8, 13), // (9,13): warning CS8600: Converting null literal or possible null value to non-nullable type. // z = (T)x; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T)x").WithLocation(9, 13), - // (9,13): warning CS8600: Converting null literal or possible null value to non-nullable type. - // z = (T)x; Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T)x").WithLocation(9, 13) ); } @@ -53387,9 +53357,6 @@ class Outer Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "y").WithArguments("Outer", "T").WithLocation(8, 13), // (9,13): warning CS8600: Converting null literal or possible null value to non-nullable type. // z = (T)x; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T)x").WithLocation(9, 13), - // (9,13): warning CS8600: Converting null literal or possible null value to non-nullable type. - // z = (T)x; Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(T)x").WithLocation(9, 13) ); } @@ -57143,5 +57110,27 @@ public static void Main() Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(224, 17) ); } + + [WorkItem(30938, "https://github.com/dotnet/roslyn/issues/30938")] + [Fact] + public void ExplicitCastAndInferredTargetType() + { + var source = +@"class Program +{ + static void F(object? x) + { + if (x == null) return; + var y = x; + x = null; + y = (object)x; + } +}"; + var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( + // (8,13): warning CS8600: Converting null literal or possible null value to non-nullable type. + // y = (object)x; + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(object)x").WithLocation(8, 13)); + } } } From 077eba0d578c432cf1da5ed1e6c8d36fe0cae3fd Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Tue, 13 Nov 2018 15:58:48 -0800 Subject: [PATCH 10/10] Added resource strings missing from #30914 (#31147) --- src/EditorFeatures/Core/EditorFeaturesResources.resx | 6 ++++++ .../Core/xlf/EditorFeaturesResources.cs.xlf | 10 ++++++++++ .../Core/xlf/EditorFeaturesResources.de.xlf | 10 ++++++++++ .../Core/xlf/EditorFeaturesResources.es.xlf | 10 ++++++++++ .../Core/xlf/EditorFeaturesResources.fr.xlf | 10 ++++++++++ .../Core/xlf/EditorFeaturesResources.it.xlf | 10 ++++++++++ .../Core/xlf/EditorFeaturesResources.ja.xlf | 10 ++++++++++ .../Core/xlf/EditorFeaturesResources.ko.xlf | 10 ++++++++++ .../Core/xlf/EditorFeaturesResources.pl.xlf | 10 ++++++++++ .../Core/xlf/EditorFeaturesResources.pt-BR.xlf | 10 ++++++++++ .../Core/xlf/EditorFeaturesResources.ru.xlf | 10 ++++++++++ .../Core/xlf/EditorFeaturesResources.tr.xlf | 10 ++++++++++ .../Core/xlf/EditorFeaturesResources.zh-Hans.xlf | 10 ++++++++++ .../Core/xlf/EditorFeaturesResources.zh-Hant.xlf | 10 ++++++++++ 14 files changed, 136 insertions(+) diff --git a/src/EditorFeatures/Core/EditorFeaturesResources.resx b/src/EditorFeatures/Core/EditorFeaturesResources.resx index 8b7267d585be3..501d61a32ec89 100644 --- a/src/EditorFeatures/Core/EditorFeaturesResources.resx +++ b/src/EditorFeatures/Core/EditorFeaturesResources.resx @@ -830,4 +830,10 @@ Do you want to proceed? Paste Tracking + + Invalid assembly name + + + Invalid characters in assembly name + \ No newline at end of file diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.cs.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.cs.xlf index 5d9ecba980b4a..920ab87860221 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.cs.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.cs.xlf @@ -42,6 +42,16 @@ Operace Formátovat dokument provedla další čištění. + + Invalid assembly name + Invalid assembly name + + + + Invalid characters in assembly name + Invalid characters in assembly name + + Paste Tracking Paste Tracking diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.de.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.de.xlf index ffe259c06077f..476fab24ca55c 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.de.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.de.xlf @@ -42,6 +42,16 @@ Durch "Dokument formatieren" wurde eine zusätzliche Bereinigung durchgeführt. + + Invalid assembly name + Invalid assembly name + + + + Invalid characters in assembly name + Invalid characters in assembly name + + Paste Tracking Paste Tracking diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.es.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.es.xlf index 9589c9e44a7b1..3823ea980b69f 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.es.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.es.xlf @@ -42,6 +42,16 @@ Dar formato al documento realiza una limpieza adicional + + Invalid assembly name + Invalid assembly name + + + + Invalid characters in assembly name + Invalid characters in assembly name + + Paste Tracking Paste Tracking diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.fr.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.fr.xlf index a8c746edba537..ef2f38f86b731 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.fr.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.fr.xlf @@ -42,6 +42,16 @@ Nettoyage supplémentaire effectué avec Mettre le document en forme + + Invalid assembly name + Invalid assembly name + + + + Invalid characters in assembly name + Invalid characters in assembly name + + Paste Tracking Paste Tracking diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.it.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.it.xlf index fdbf458dc2cbe..cb04f3bb1f353 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.it.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.it.xlf @@ -42,6 +42,16 @@ Formatta documento ha eseguito una pulizia aggiuntiva + + Invalid assembly name + Invalid assembly name + + + + Invalid characters in assembly name + Invalid characters in assembly name + + Paste Tracking Paste Tracking diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ja.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ja.xlf index 925222d0e5fde..e6784f38ff098 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ja.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ja.xlf @@ -42,6 +42,16 @@ ドキュメントのフォーマットで追加のクリーンアップが実行されました + + Invalid assembly name + Invalid assembly name + + + + Invalid characters in assembly name + Invalid characters in assembly name + + Paste Tracking Paste Tracking diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ko.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ko.xlf index a55802bef3319..1146565f7a08f 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ko.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ko.xlf @@ -42,6 +42,16 @@ 문서 서식에서 추가 정리를 수행함 + + Invalid assembly name + Invalid assembly name + + + + Invalid characters in assembly name + Invalid characters in assembly name + + Paste Tracking Paste Tracking diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pl.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pl.xlf index c8ce783287946..a52cd65fb1d9d 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pl.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pl.xlf @@ -42,6 +42,16 @@ Funkcja formatowania dokumentu wykonała dodatkowe czyszczenie + + Invalid assembly name + Invalid assembly name + + + + Invalid characters in assembly name + Invalid characters in assembly name + + Paste Tracking Paste Tracking diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pt-BR.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pt-BR.xlf index 592d192676f23..081a99d1b577f 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pt-BR.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pt-BR.xlf @@ -42,6 +42,16 @@ A formatação de documento realizou uma limpeza adicional + + Invalid assembly name + Invalid assembly name + + + + Invalid characters in assembly name + Invalid characters in assembly name + + Paste Tracking Paste Tracking diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ru.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ru.xlf index 703a12ca39b4f..cbc049502a2f3 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ru.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ru.xlf @@ -42,6 +42,16 @@ При форматировании документа выполнена дополнительная очистка + + Invalid assembly name + Invalid assembly name + + + + Invalid characters in assembly name + Invalid characters in assembly name + + Paste Tracking Paste Tracking diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.tr.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.tr.xlf index 8e13c06361ba1..6299989f70132 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.tr.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.tr.xlf @@ -42,6 +42,16 @@ Biçim Belgesi ek temizleme gerçekleştirdi + + Invalid assembly name + Invalid assembly name + + + + Invalid characters in assembly name + Invalid characters in assembly name + + Paste Tracking Paste Tracking diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hans.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hans.xlf index 33d78fe0928a2..7bbe6acda37e1 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hans.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hans.xlf @@ -42,6 +42,16 @@ 设置执行了其他清理的文档的格式 + + Invalid assembly name + Invalid assembly name + + + + Invalid characters in assembly name + Invalid characters in assembly name + + Paste Tracking Paste Tracking diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hant.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hant.xlf index 4638b8cb615ce..d3717fb592f0c 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hant.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hant.xlf @@ -42,6 +42,16 @@ 格式化文件執行了額外的清除 + + Invalid assembly name + Invalid assembly name + + + + Invalid characters in assembly name + Invalid characters in assembly name + + Paste Tracking Paste Tracking