diff --git a/NuGet.Config b/NuGet.Config index 7ad028f274052..f3a854ff13377 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -18,6 +18,7 @@ + diff --git a/Roslyn.sln b/Roslyn.sln index c93853a9295be..57c8d98637fbb 100644 --- a/Roslyn.sln +++ b/Roslyn.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.25920.0 +VisualStudioVersion = 15.0.26228.4 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeAnalysisTest", "src\Compilers\Core\CodeAnalysisTest\CodeAnalysisTest.csproj", "{A4C99B85-765C-4C65-9C2A-BB609AAB09E6}" EndProject @@ -235,8 +235,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResultProvider.Portable", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "vbc", "src\Compilers\VisualBasic\vbc\vbc.csproj", "{E58EE9D7-1239-4961-A0C1-F9EC3952C4C1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pdb2Xml", "src\Tools\Source\Pdb2Xml\Pdb2Xml.csproj", "{CF450DCE-D12B-4A11-8D2D-A7A125372C48}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Setup", "Setup", "{19148439-436F-4CDA-B493-70AF4FFC13E9}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Features", "Features", "{2491A9B9-C0A8-49EE-9077-A32DE76E1E94}" @@ -792,10 +790,6 @@ Global {E58EE9D7-1239-4961-A0C1-F9EC3952C4C1}.Debug|Any CPU.Build.0 = Debug|Any CPU {E58EE9D7-1239-4961-A0C1-F9EC3952C4C1}.Release|Any CPU.ActiveCfg = Release|Any CPU {E58EE9D7-1239-4961-A0C1-F9EC3952C4C1}.Release|Any CPU.Build.0 = Release|Any CPU - {CF450DCE-D12B-4A11-8D2D-A7A125372C48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CF450DCE-D12B-4A11-8D2D-A7A125372C48}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CF450DCE-D12B-4A11-8D2D-A7A125372C48}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CF450DCE-D12B-4A11-8D2D-A7A125372C48}.Release|Any CPU.Build.0 = Release|Any CPU {E3CD2895-76A8-4D11-A316-EA67CB5EA42C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E3CD2895-76A8-4D11-A316-EA67CB5EA42C}.Debug|Any CPU.Build.0 = Debug|Any CPU {E3CD2895-76A8-4D11-A316-EA67CB5EA42C}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -1096,7 +1090,6 @@ Global {BEDC5A4A-809E-4017-9CFD-6C8D4E1847F0} = {998CAFE8-06E4-4683-A151-0F6AA4BFF6C6} {FA0E905D-EC46-466D-B7B2-3B5557F9428C} = {998CAFE8-06E4-4683-A151-0F6AA4BFF6C6} {E58EE9D7-1239-4961-A0C1-F9EC3952C4C1} = {C65C6143-BED3-46E6-869E-9F0BE6E84C37} - {CF450DCE-D12B-4A11-8D2D-A7A125372C48} = {64BDF58B-41BA-A19E-0D34-B5FA598403B6} {19148439-436F-4CDA-B493-70AF4FFC13E9} = {999FBDA2-33DA-4F74-B957-03AC72CCE5EC} {2491A9B9-C0A8-49EE-9077-A32DE76E1E94} = {999FBDA2-33DA-4F74-B957-03AC72CCE5EC} {5CA5F70E-0FDB-467B-B22C-3CD5994F0087} = {999FBDA2-33DA-4F74-B957-03AC72CCE5EC} diff --git a/build/Targets/Dependencies.props b/build/Targets/Dependencies.props index 279cf4abe3ccb..1957b6c346794 100644 --- a/build/Targets/Dependencies.props +++ b/build/Targets/Dependencies.props @@ -21,6 +21,7 @@ 0.8.31-beta 1.0.35 1.1.0 + 1.0.0-beta1-61515-01 1.5.0 1.2.0 1.1.0 diff --git a/build/config/SignToolData.json b/build/config/SignToolData.json index 7f9c78ee5048b..3b03bfcfc19b8 100644 --- a/build/config/SignToolData.json +++ b/build/config/SignToolData.json @@ -55,7 +55,6 @@ "Exes\\vbccore\\vbc.exe", "Exes\\vbi\\vbi.exe", "Exes\\vbicore\\vbi.exe", - "Exes\\Pdb2Xml\\Pdb2Xml.exe", "Vsix\\CompilerExtension\\Roslyn.Compilers.Extension.dll", "Vsix\\Templates\\Roslyn.Templates.dll", "Vsix\\VisualStudioDiagnosticsWindow\\Roslyn.VisualStudio.DiagnosticsWindow.dll", diff --git a/build/scripts/cibuild.ps1 b/build/scripts/cibuild.ps1 index c5991a7823ebd..eaf0af76bdf0c 100644 --- a/build/scripts/cibuild.ps1 +++ b/build/scripts/cibuild.ps1 @@ -104,15 +104,16 @@ try { Run-MSBuild Roslyn.sln /p:Configuration=$buildConfiguration /p:DeployExtension=false # Check if we have credentials to upload to benchview - if ((Test-Path env:\GIT_BRANCH) -and (Test-Path BV_UPLOAD_SAS_TOKEN)) { - $extraArgs="--report-benchview --branch $(env:GIT_BRANCH)" + $extraArgs = "" + if ((Test-Path env:\GIT_BRANCH) -and (Test-Path env:\BV_UPLOAD_SAS_TOKEN)) { + $extraArgs = "--report-benchview --branch $($env:GIT_BRANCH)" # Check if we are in a PR or this is a rolling submission if (Test-Path env:\ghprbPullTitle) { - $extraArgs='$($extraArgs) --benchview-submission-name "[$($env:ghprbPullAuthorLogin)] PR $($env:ghprbPullId): $($env:ghprbPullTitle)" --benchview-submission-type private' + $extraArgs = '$($extraArgs) --benchview-submission-name "[$($env:ghprbPullAuthorLogin)] PR $($env:ghprbPullId): $($env:ghprbPullTitle)" --benchview-submission-type private' } else { - $extraArgs='$(4extraArgs) --benchview-submission-type rolling' + $extraArgs = '$(4extraArgs) --benchview-submission-type rolling' } Create-Directory ".\Binaries\$buildConfiguration\tools\" @@ -121,7 +122,10 @@ try { } Terminate-BuildProcesses - Exec { & ".\Binaries\$buildConfiguration\Exes\Perf.Runner\Roslyn.Test.Performance.Runner.exe" --search-directory=".\\Binaries\\$buildConfiguration\\Dlls\\" --no-trace-upload $extraArgs } + & ".\Binaries\$buildConfiguration\Exes\Perf.Runner\Roslyn.Test.Performance.Runner.exe" --search-directory=".\\Binaries\\$buildConfiguration\\Dlls\\" --no-trace-upload $extraArgs + if (-not $?) { + throw "Perf run failed" + } exit 0 } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index b8de77a07ac40..15bd156c13e0f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -2079,7 +2079,7 @@ internal static void CheckFeatureAvailability(SyntaxNode syntax, MessageID featu LanguageVersion requiredVersion = feature.RequiredVersion(); if (requiredVersion > availableVersion) { - diagnostics.Add(availableVersion.GetErrorCode(), location, feature.Localize(), requiredVersion.Localize()); + diagnostics.Add(availableVersion.GetErrorCode(), location, feature.Localize(), new CSharpRequiredLanguageVersion(requiredVersion)); } } } diff --git a/src/Compilers/CSharp/Portable/CSharpParseOptions.cs b/src/Compilers/CSharp/Portable/CSharpParseOptions.cs index 39d6699788197..f27c57ed273a5 100644 --- a/src/Compilers/CSharp/Portable/CSharpParseOptions.cs +++ b/src/Compilers/CSharp/Portable/CSharpParseOptions.cs @@ -50,32 +50,10 @@ public CSharpParseOptions( IEnumerable preprocessorSymbols = null) : this(languageVersion, documentationMode, - kind, - preprocessorSymbols.ToImmutableArrayOrEmpty(), + kind, + preprocessorSymbols.ToImmutableArrayOrEmpty(), ImmutableDictionary.Empty) { - // We test the mapped value, LanguageVersion, rather than the parameter, languageVersion, - // which has not had "Latest" mapped to the latest version yet. - if (!LanguageVersion.IsValid()) - { - throw new ArgumentOutOfRangeException(nameof(languageVersion)); - } - - if (!kind.IsValid()) - { - throw new ArgumentOutOfRangeException(nameof(kind)); - } - - if (preprocessorSymbols != null) - { - foreach (var preprocessorSymbol in preprocessorSymbols) - { - if (!SyntaxFacts.IsValidIdentifier(preprocessorSymbol)) - { - throw new ArgumentException($"{nameof(preprocessorSymbols)} contains a symbol that is not a valid identifier", nameof(preprocessorSymbols)); - } - } - } } internal CSharpParseOptions( @@ -83,15 +61,13 @@ internal CSharpParseOptions( DocumentationMode documentationMode, SourceCodeKind kind, IEnumerable preprocessorSymbols, - ImmutableDictionary features) - : this(languageVersion, documentationMode, kind, preprocessorSymbols) + IReadOnlyDictionary features) + : base(kind, documentationMode) { - if (features == null) - { - throw new ArgumentNullException(nameof(features)); - } - - _features = features; + this.SpecifiedLanguageVersion = languageVersion; + this.LanguageVersion = languageVersion.MapSpecifiedToEffectiveVersion(); + this.PreprocessorSymbols = preprocessorSymbols.ToImmutableArrayOrEmpty(); + _features = features?.ToImmutableDictionary() ?? ImmutableDictionary.Empty; } private CSharpParseOptions(CSharpParseOptions other) : this( @@ -99,41 +75,21 @@ private CSharpParseOptions(CSharpParseOptions other) : this( documentationMode: other.DocumentationMode, kind: other.Kind, preprocessorSymbols: other.PreprocessorSymbols, - features: other.Features.ToImmutableDictionary()) - { - } - - // No validation - private CSharpParseOptions( - LanguageVersion languageVersion, - DocumentationMode documentationMode, - SourceCodeKind kind, - ImmutableArray preprocessorSymbols, - ImmutableDictionary features) - : base(kind, documentationMode) + features: other.Features) { - Debug.Assert(!preprocessorSymbols.IsDefault); - this.SpecifiedLanguageVersion = languageVersion; - this.LanguageVersion = languageVersion.MapSpecifiedToEffectiveVersion(); - this.PreprocessorSymbols = preprocessorSymbols; - _features = features; } - + public override string Language => LanguageNames.CSharp; public new CSharpParseOptions WithKind(SourceCodeKind kind) { - if (kind == this.Kind) + if (kind == this.SpecifiedKind) { return this; } - if (!kind.IsValid()) - { - throw new ArgumentOutOfRangeException(nameof(kind)); - } - - return new CSharpParseOptions(this) { Kind = kind }; + var effectiveKind = kind.MapSpecifiedToEffectiveKind(); + return new CSharpParseOptions(this) { SpecifiedKind = kind, Kind = effectiveKind }; } public CSharpParseOptions WithLanguageVersion(LanguageVersion version) @@ -144,11 +100,6 @@ public CSharpParseOptions WithLanguageVersion(LanguageVersion version) } var effectiveLanguageVersion = version.MapSpecifiedToEffectiveVersion(); - if (!effectiveLanguageVersion.IsValid()) - { - throw new ArgumentOutOfRangeException(nameof(version)); - } - return new CSharpParseOptions(this) { SpecifiedLanguageVersion = version, LanguageVersion = effectiveLanguageVersion }; } @@ -184,11 +135,6 @@ public CSharpParseOptions WithPreprocessorSymbols(ImmutableArray symbols return this; } - if (!documentationMode.IsValid()) - { - throw new ArgumentOutOfRangeException(nameof(documentationMode)); - } - return new CSharpParseOptions(this) { DocumentationMode = documentationMode }; } @@ -212,12 +158,11 @@ protected override ParseOptions CommonWithFeatures(IEnumerable public new CSharpParseOptions WithFeatures(IEnumerable> features) { - if (features == null) - { - throw new ArgumentNullException(nameof(features)); - } + ImmutableDictionary dictionary = + features?.ToImmutableDictionary(StringComparer.OrdinalIgnoreCase) + ?? ImmutableDictionary.Empty; - return new CSharpParseOptions(this) { _features = features.ToImmutableDictionary(StringComparer.OrdinalIgnoreCase) }; + return new CSharpParseOptions(this) { _features = dictionary }; } public override IReadOnlyDictionary Features @@ -228,6 +173,32 @@ public override IReadOnlyDictionary Features } } + internal override void ValidateOptions(ArrayBuilder builder) + { + ValidateOptions(builder, MessageProvider.Instance); + + // Validate LanguageVersion not SpecifiedLanguageVersion, after Latest/Default has been converted: + if (!LanguageVersion.IsValid()) + { + builder.Add(Diagnostic.Create(MessageProvider.Instance, (int)ErrorCode.ERR_BadLanguageVersion, LanguageVersion.ToString())); + } + + if (!PreprocessorSymbols.IsDefaultOrEmpty) + { + foreach (var symbol in PreprocessorSymbols) + { + if (symbol == null) + { + builder.Add(Diagnostic.Create(MessageProvider.Instance, (int)ErrorCode.ERR_InvalidPreprocessingSymbol, "null")); + } + else if (!SyntaxFacts.IsValidIdentifier(symbol)) + { + builder.Add(Diagnostic.Create(MessageProvider.Instance, (int)ErrorCode.ERR_InvalidPreprocessingSymbol, symbol)); + } + } + } + } + internal bool IsFeatureEnabled(MessageID feature) { string featureFlag = feature.RequiredFeature(); diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index 05f0ae7abe233..335f581c6d10f 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -1232,7 +1232,7 @@ internal static string ERR_BadCoClassSig { } /// - /// Looks up a localized string similar to Invalid option '{0}' for /langversion; must be ISO-1, ISO-2, Default or an integer in range 1 to 7.. + /// Looks up a localized string similar to Invalid option '{0}' for /langversion; must be ISO-1, ISO-2, Default, Latest or a valid version in range 1 to 7.1.. /// internal static string ERR_BadCompatMode { get { @@ -1330,6 +1330,15 @@ internal static string ERR_BadDirectivePlacement { } } + /// + /// Looks up a localized string similar to Provided documentation mode is unsupported or invalid: '{0}'.. + /// + internal static string ERR_BadDocumentationMode { + get { + return ResourceManager.GetString("ERR_BadDocumentationMode", resourceCulture); + } + } + /// /// Looks up a localized string similar to '{0}': user-defined conversions to or from the dynamic type are not allowed. /// @@ -1618,6 +1627,15 @@ internal static string ERR_BadIteratorReturnRef { } } + /// + /// Looks up a localized string similar to Provided language version is unsupported or invalid: '{0}'.. + /// + internal static string ERR_BadLanguageVersion { + get { + return ResourceManager.GetString("ERR_BadLanguageVersion", resourceCulture); + } + } + /// /// Looks up a localized string similar to The modifier '{0}' is not valid for this item. /// @@ -1861,6 +1879,15 @@ internal static string ERR_BadSKunknown { } } + /// + /// Looks up a localized string similar to Provided source code kind is unsupported or invalid: '{0}'. + /// + internal static string ERR_BadSourceCodeKind { + get { + return ResourceManager.GetString("ERR_BadSourceCodeKind", resourceCulture); + } + } + /// /// Looks up a localized string similar to Parameters or locals of type '{0}' cannot be declared in async methods or lambda expressions.. /// @@ -4426,6 +4453,15 @@ internal static string ERR_FeatureNotAvailableInVersion7 { } } + /// + /// Looks up a localized string similar to Feature '{0}' is not available in C# 7.1. Please use language version {1} or greater.. + /// + internal static string ERR_FeatureNotAvailableInVersion7_1 { + get { + return ResourceManager.GetString("ERR_FeatureNotAvailableInVersion7_1", resourceCulture); + } + } + /// /// Looks up a localized string similar to An expression tree may not contain '{0}'. /// @@ -5641,6 +5677,15 @@ internal static string ERR_InvalidPathMap { } } + /// + /// Looks up a localized string similar to Invalid name for a preprocessing symbol; '{0}' is not a valid identifier. + /// + internal static string ERR_InvalidPreprocessingSymbol { + get { + return ResourceManager.GetString("ERR_InvalidPreprocessingSymbol", resourceCulture); + } + } + /// /// Looks up a localized string similar to Invalid preprocessor expression. /// @@ -12105,7 +12150,7 @@ internal static string WRN_DefaultValueForUnconsumedLocation_Title { } /// - /// Looks up a localized string similar to Invalid value for '/define'; '{0}' is not a valid identifier. + /// Looks up a localized string similar to Invalid name for a preprocessing symbol; '{0}' is not a valid identifier. /// internal static string WRN_DefineIdentifierRequired { get { @@ -12114,7 +12159,7 @@ internal static string WRN_DefineIdentifierRequired { } /// - /// Looks up a localized string similar to Invalid value for '/define'; not a valid identifier. + /// Looks up a localized string similar to Invalid name for a preprocessing symbol; not a valid identifier. /// internal static string WRN_DefineIdentifierRequired_Title { get { diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index f5ced08a92dff..280e2b2cc9155 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -2690,7 +2690,7 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep This warning occurs if the assembly attributes AssemblyKeyFileAttribute or AssemblyKeyNameAttribute found in source conflict with the /keyfile or /keycontainer command line option or key file name or key container specified in the Project Properties. - Invalid option '{0}' for /langversion; must be ISO-1, ISO-2, Default or an integer in range 1 to 7. + Invalid option '{0}' for /langversion; must be ISO-1, ISO-2, Default, Latest or a valid version in range 1 to 7.1. Cannot create delegate with '{0}' because it or a method it overrides has a Conditional attribute @@ -3321,10 +3321,10 @@ Give the compiler some way to differentiate the methods. For example, you can gi 'id#' syntax is no longer supported. Use '$id' instead. - Invalid value for '/define'; '{0}' is not a valid identifier + Invalid name for a preprocessing symbol; '{0}' is not a valid identifier - Invalid value for '/define'; not a valid identifier + Invalid name for a preprocessing symbol; not a valid identifier Cannot create short filename '{0}' when a long filename with the same short filename already exists @@ -5020,4 +5020,19 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ It is not legal to use the type 'dynamic' in a pattern. + + Provided documentation mode is unsupported or invalid: '{0}'. + + + Provided source code kind is unsupported or invalid: '{0}' + + + Provided language version is unsupported or invalid: '{0}'. + + + Invalid name for a preprocessing symbol; '{0}' is not a valid identifier + + + Feature '{0}' is not available in C# 7.1. Please use language version {1} or greater. + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs b/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs index f83230991b3f1..89926ce2c350d 100644 --- a/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs +++ b/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs @@ -805,7 +805,7 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar { AddDiagnostic(diagnostics, ErrorCode.ERR_SwitchNeedsString, MessageID.IDS_Text.Localize(), "/langversion:"); } - else if (!TryParseLanguageVersion(value, out languageVersion)) + else if (!value.TryParse(out languageVersion)) { AddDiagnostic(diagnostics, ErrorCode.ERR_BadCompatMode, value); } @@ -1217,12 +1217,10 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar languageVersion: languageVersion, preprocessorSymbols: defines.ToImmutableAndFree(), documentationMode: parseDocumentationComments ? DocumentationMode.Diagnose : DocumentationMode.None, - kind: SourceCodeKind.Regular, + kind: IsScriptRunner ? SourceCodeKind.Script : SourceCodeKind.Regular, features: parsedFeatures ); - var scriptParseOptions = parseOptions.WithKind(SourceCodeKind.Script); - // We want to report diagnostics with source suppression in the error log file. // However, these diagnostics won't be reported on the command line. var reportSuppressedDiagnostics = errorLogPath != null; @@ -1271,6 +1269,7 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar // add option incompatibility errors if any diagnostics.AddRange(options.Errors); + diagnostics.AddRange(parseOptions.Errors); return new CSharpCommandLineArguments { @@ -1307,7 +1306,7 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar DisplayVersion = displayVersion, ManifestResources = managedResources.AsImmutable(), CompilationOptions = options, - ParseOptions = IsScriptRunner ? scriptParseOptions : parseOptions, + ParseOptions = parseOptions, EmitOptions = emitOptions, ScriptArguments = scriptArgs.AsImmutableOrEmpty(), TouchedFilesPath = touchedFilesPath, @@ -1470,7 +1469,7 @@ private ImmutableArray BuildSearchPaths(string sdkDirectoryOpt, List ParseConditionalCompilationSymbols(string value, out IEnumerable diagnostics) { - Diagnostic myDiagnostic = null; + DiagnosticBag outputDiagnostics = DiagnosticBag.GetInstance(); value = value.TrimEnd(null); // Allow a trailing semicolon or comma in the options @@ -1490,15 +1489,13 @@ public static IEnumerable ParseConditionalCompilationSymbols(string valu { defines.Add(trimmedId); } - else if (myDiagnostic == null) + else { - myDiagnostic = Diagnostic.Create(CSharp.MessageProvider.Instance, (int)ErrorCode.WRN_DefineIdentifierRequired, trimmedId); + outputDiagnostics.Add(Diagnostic.Create(CSharp.MessageProvider.Instance, (int)ErrorCode.WRN_DefineIdentifierRequired, trimmedId)); } } - diagnostics = myDiagnostic == null ? SpecializedCollections.EmptyEnumerable() - : SpecializedCollections.SingletonEnumerable(myDiagnostic); - + diagnostics = outputDiagnostics.ToReadOnlyAndFree(); return defines.AsEnumerable(); } @@ -1761,56 +1758,6 @@ internal static ResourceDescription ParseResourceDescription( return new ResourceDescription(resourceName, fileName, dataProvider, isPublic, embedded, checkArgs: false); } - private static bool TryParseLanguageVersion(string str, out LanguageVersion version) - { - if (str == null) - { - version = LanguageVersion.Default; - return true; - } - - switch (str.ToLowerInvariant()) - { - case "iso-1": - version = LanguageVersion.CSharp1; - return true; - - case "iso-2": - version = LanguageVersion.CSharp2; - return true; - - case "7": - version = LanguageVersion.CSharp7; - return true; - - case "default": - version = LanguageVersion.Default; - return true; - - case "latest": - version = LanguageVersion.Latest; - return true; - - default: - // We are likely to introduce minor version numbers after C# 7, thus breaking the - // one-to-one correspondence between the integers and the corresponding - // LanguageVersion enum values. But for compatibility we continue to accept any - // integral value parsed by int.TryParse for its corresponding LanguageVersion enum - // value for language version C# 6 and earlier (e.g. leading zeros are allowed) - int versionNumber; - if (int.TryParse(str, NumberStyles.None, CultureInfo.InvariantCulture, out versionNumber) && - versionNumber <= 6 && - ((LanguageVersion)versionNumber).IsValid()) - { - version = (LanguageVersion)versionNumber; - return true; - } - - version = LanguageVersion.Default; - return false; - } - } - private static IEnumerable ParseWarnings(string value) { value = value.Unquote(); diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index f1c43315e2751..a163e9ecc6b49 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -1940,6 +1940,20 @@ internal ImmutableArray GetDiagnostics(CompilationStage stage, bool builder.AddRange(syntaxTree.GetDiagnostics(cancellationToken)); } } + + var parseOptionsReported = new HashSet(); + foreach (var syntaxTree in syntaxTrees) + { + cancellationToken.ThrowIfCancellationRequested(); + if (!syntaxTree.Options.Errors.IsDefaultOrEmpty && parseOptionsReported.Add(syntaxTree.Options)) + { + var location = syntaxTree.GetLocation(TextSpan.FromBounds(0, 0)); + foreach (var error in syntaxTree.Options.Errors) + { + builder.Add(error.WithLocation(location)); + } + } + } } if (stage == CompilationStage.Declare || stage > CompilationStage.Declare && includeEarlierStages) diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 3036a614c69cd..29a51eff04724 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1439,7 +1439,13 @@ internal enum ErrorCode ERR_DelegateRefMismatch = 8189, #endregion stragglers for C# 7 - // Available = 8190-8195 + #region diagnostics for parse options + ERR_BadSourceCodeKind = 8190, + ERR_BadDocumentationMode = 8191, + ERR_BadLanguageVersion = 8192, + #endregion + + // Available = 8193-8195 #region diagnostics for out var ERR_ImplicitlyTypedOutVariableUsedInTheSameArgumentList = 8196, @@ -1461,5 +1467,7 @@ internal enum ErrorCode #endregion more stragglers for C# 7 ERR_Merge_conflict_marker_encountered = 8300, + ERR_InvalidPreprocessingSymbol = 8301, + ERR_FeatureNotAvailableInVersion7_1 = 8302, } } \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/Errors/MessageProvider.cs b/src/Compilers/CSharp/Portable/Errors/MessageProvider.cs index b7dc830404811..88f4689b56e38 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageProvider.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageProvider.cs @@ -146,6 +146,10 @@ public override ReportDiagnostic GetDiagnosticReport(DiagnosticInfo diagnosticIn public override int ERR_CantReadRulesetFile => (int)ErrorCode.ERR_CantReadRulesetFile; public override int ERR_CompileCancelled => (int)ErrorCode.ERR_CompileCancelled; + // parse options: + public override int ERR_BadSourceCodeKind => (int)ErrorCode.ERR_BadSourceCodeKind; + public override int ERR_BadDocumentationMode => (int)ErrorCode.ERR_BadDocumentationMode; + // compilation options: public override int ERR_BadCompilationOptionValue => (int)ErrorCode.ERR_BadCompilationOptionValue; public override int ERR_MutuallyExclusiveOptions => (int)ErrorCode.ERR_MutuallyExclusiveOptions; diff --git a/src/Compilers/CSharp/Portable/LanguageVersion.cs b/src/Compilers/CSharp/Portable/LanguageVersion.cs index fa89df7158d9f..ee433a587f845 100644 --- a/src/Compilers/CSharp/Portable/LanguageVersion.cs +++ b/src/Compilers/CSharp/Portable/LanguageVersion.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.Globalization; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp @@ -68,34 +69,35 @@ public enum LanguageVersion /// CSharp7 = 7, + /// + /// C# language version 7.1 + /// + CSharp7_1 = 701, + /// /// The latest version of the language supported. /// Latest = int.MaxValue, } - internal static partial class LanguageVersionExtensions + internal static class LanguageVersionExtensionsInternal { - internal static LanguageVersion MapSpecifiedToEffectiveVersion(this LanguageVersion version) + internal static bool IsValid(this LanguageVersion value) { - switch (version) + switch (value) { - case LanguageVersion.Latest: - case LanguageVersion.Default: - return LanguageVersion.CSharp7; - default: - return version; + case LanguageVersion.CSharp1: + case LanguageVersion.CSharp2: + case LanguageVersion.CSharp3: + case LanguageVersion.CSharp4: + case LanguageVersion.CSharp5: + case LanguageVersion.CSharp6: + case LanguageVersion.CSharp7: + case LanguageVersion.CSharp7_1: + return true; } - } - internal static bool IsValid(this LanguageVersion value) - { - return value >= LanguageVersion.CSharp1 && value <= LanguageVersion.CSharp7; - } - - internal static object Localize(this LanguageVersion value) - { - return (int)value; + return false; } internal static ErrorCode GetErrorCode(this LanguageVersion version) @@ -116,9 +118,123 @@ internal static ErrorCode GetErrorCode(this LanguageVersion version) return ErrorCode.ERR_FeatureNotAvailableInVersion6; case LanguageVersion.CSharp7: return ErrorCode.ERR_FeatureNotAvailableInVersion7; + case LanguageVersion.CSharp7_1: + return ErrorCode.ERR_FeatureNotAvailableInVersion7_1; default: throw ExceptionUtilities.UnexpectedValue(version); } } } -} + + internal class CSharpRequiredLanguageVersion : RequiredLanguageVersion + { + internal LanguageVersion Version { get; } + + internal CSharpRequiredLanguageVersion(LanguageVersion version) + { + Version = version; + } + + public override string ToString() => Version.ToDisplayString(); + } + + public static class LanguageVersionFacts + { + /// + /// Displays the version number in the format expected on the command-line (/langver flag). + /// For instance, "6", "7", "7.1", "latest". + /// + public static string ToDisplayString(this LanguageVersion version) + { + switch (version) + { + case LanguageVersion.CSharp1: + return "1"; + case LanguageVersion.CSharp2: + return "2"; + case LanguageVersion.CSharp3: + return "3"; + case LanguageVersion.CSharp4: + return "4"; + case LanguageVersion.CSharp5: + return "5"; + case LanguageVersion.CSharp6: + return "6"; + case LanguageVersion.CSharp7: + return "7"; + case LanguageVersion.CSharp7_1: + return "7.1"; + case LanguageVersion.Default: + return "default"; + case LanguageVersion.Latest: + return "latest"; + default: + throw ExceptionUtilities.UnexpectedValue(version); + } + } + + /// + /// Parse a LanguageVersion from a string input, as the command-line compiler does. + /// + public static bool TryParse(this string version, out LanguageVersion result) + { + if (version == null) + { + result = LanguageVersion.Default; + return true; + } + + switch (version.ToLowerInvariant()) + { + case "iso-1": + result = LanguageVersion.CSharp1; + return true; + + case "iso-2": + result = LanguageVersion.CSharp2; + return true; + + case "default": + result = LanguageVersion.Default; + return true; + + case "latest": + result = LanguageVersion.Latest; + return true; + + case "7.1": + result = LanguageVersion.CSharp7_1; + return true; + + default: + if (int.TryParse(version, NumberStyles.None, CultureInfo.InvariantCulture, out int versionNumber) && versionNumber <= 7) + { + result = (LanguageVersion)versionNumber; + if (result.IsValid()) + { + return true; + } + } + + result = LanguageVersion.Default; + return false; + } + } + + /// + /// Map a language version (such as Default, Latest, or CSharpN) to a specific version (CSharpM). + /// + public static LanguageVersion MapSpecifiedToEffectiveVersion(this LanguageVersion version) + { + switch (version) + { + case LanguageVersion.Latest: + return LanguageVersion.CSharp7_1; + case LanguageVersion.Default: + return LanguageVersion.CSharp7; + default: + return version; + } + } + } +} \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 5a323e0f3ff96..5decfac093449 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -6488,7 +6488,7 @@ private TypeSyntax ParsePointerTypeMods(TypeSyntax type) public StatementSyntax ParseStatement() { return ParseWithStackGuard( - ParseStatementCore, + () => ParseStatementCore() ?? ParseExpressionStatement(), () => SyntaxFactory.EmptyStatement(SyntaxFactory.MissingToken(SyntaxKind.SemicolonToken))); } @@ -7354,15 +7354,35 @@ private StatementSyntax ParseEmbeddedStatement(bool complexCheck) statement = SyntaxFactory.EmptyStatement(EatToken(SyntaxKind.SemicolonToken)); } - // An "embedded" statement is simply a statement that is not a labelled - // statement or a declaration statement. switch (statement.Kind) { + // An "embedded" statement is simply a statement that is not a labelled + // statement or a declaration statement. case SyntaxKind.LabeledStatement: case SyntaxKind.LocalDeclarationStatement: case SyntaxKind.LocalFunctionStatement: statement = this.AddError(statement, ErrorCode.ERR_BadEmbeddedStmt); break; + // In scripts, stand-alone expression statements may not be followed by semicolons. + // ParseExpressionStatement hides the error. + // However, embedded expression statements are required to be followed by semicolon. + case SyntaxKind.ExpressionStatement: + if (IsScript) + { + var expressionStatementSyntax = (ExpressionStatementSyntax)statement; + var semicolonToken = expressionStatementSyntax.SemicolonToken; + + // Do not add a new error if the same error was already added. + if (semicolonToken.IsMissing + && (!semicolonToken.ContainsDiagnostics + || !semicolonToken.GetDiagnostics() + .Contains(diagnosticInfo => (ErrorCode)diagnosticInfo.Code == ErrorCode.ERR_SemicolonExpected))) + { + semicolonToken = this.AddError(semicolonToken, ErrorCode.ERR_SemicolonExpected); + statement = expressionStatementSyntax.Update(expressionStatementSyntax.Expression, semicolonToken); + } + } + break; } return statement; @@ -9393,7 +9413,7 @@ private ExpressionSyntax ParsePostFixExpression(ExpressionSyntax expr) // there is a new declaration on the next line. if (this.CurrentToken.TrailingTrivia.Any((int)SyntaxKind.EndOfLineTrivia) && this.PeekToken(1).Kind == SyntaxKind.IdentifierToken && - this.PeekToken(2).Kind == SyntaxKind.IdentifierToken) + this.PeekToken(2).ContextualKind == SyntaxKind.IdentifierToken) { expr = _syntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, expr, this.EatToken(), diff --git a/src/Compilers/CSharp/Portable/Parser/Lexer.cs b/src/Compilers/CSharp/Portable/Parser/Lexer.cs index 3b37c3087b6f4..d5e1b6ce1ac11 100644 --- a/src/Compilers/CSharp/Portable/Parser/Lexer.cs +++ b/src/Compilers/CSharp/Portable/Parser/Lexer.cs @@ -916,7 +916,8 @@ private void CheckFeatureAvailability(MessageID feature) var requiredVersion = feature.RequiredVersion(); if (availableVersion >= requiredVersion) return; var featureName = feature.Localize(); - this.AddError(availableVersion.GetErrorCode(), featureName, requiredVersion.Localize()); + + this.AddError(availableVersion.GetErrorCode(), featureName, new CSharpRequiredLanguageVersion(requiredVersion)); } private bool ScanInteger() diff --git a/src/Compilers/CSharp/Portable/Parser/SyntaxParser.cs b/src/Compilers/CSharp/Portable/Parser/SyntaxParser.cs index f55e18caa6aea..7b50d32ebef8c 100644 --- a/src/Compilers/CSharp/Portable/Parser/SyntaxParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/SyntaxParser.cs @@ -1074,11 +1074,12 @@ protected TNode CheckFeatureAvailability(TNode node, MessageID feature, b if (forceWarning) { - SyntaxDiagnosticInfo rawInfo = new SyntaxDiagnosticInfo(availableVersion.GetErrorCode(), featureName, requiredVersion.Localize()); + SyntaxDiagnosticInfo rawInfo = new SyntaxDiagnosticInfo(availableVersion.GetErrorCode(), featureName, + new CSharpRequiredLanguageVersion(requiredVersion)); return this.AddError(node, ErrorCode.WRN_ErrorOverride, rawInfo, rawInfo.Code); } - return this.AddError(node, availableVersion.GetErrorCode(), featureName, requiredVersion.Localize()); + return this.AddError(node, availableVersion.GetErrorCode(), featureName, new CSharpRequiredLanguageVersion(requiredVersion)); } } diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index 852cc70b12dd2..05206cd42bdcd 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -13,8 +13,10 @@ Microsoft.CodeAnalysis.CSharp.Conversion.IsThrow.get -> bool Microsoft.CodeAnalysis.CSharp.Conversion.IsTupleConversion.get -> bool Microsoft.CodeAnalysis.CSharp.Conversion.IsTupleLiteralConversion.get -> bool Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp7 = 7 -> Microsoft.CodeAnalysis.CSharp.LanguageVersion +Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp7_1 = 701 -> Microsoft.CodeAnalysis.CSharp.LanguageVersion Microsoft.CodeAnalysis.CSharp.LanguageVersion.Default = 0 -> Microsoft.CodeAnalysis.CSharp.LanguageVersion Microsoft.CodeAnalysis.CSharp.LanguageVersion.Latest = 2147483647 -> Microsoft.CodeAnalysis.CSharp.LanguageVersion +Microsoft.CodeAnalysis.CSharp.LanguageVersionFacts Microsoft.CodeAnalysis.CSharp.Syntax.AccessorDeclarationSyntax.ExpressionBody.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax Microsoft.CodeAnalysis.CSharp.Syntax.AccessorDeclarationSyntax.Update(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.SyntaxToken keyword, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.AccessorDeclarationSyntax Microsoft.CodeAnalysis.CSharp.Syntax.AccessorDeclarationSyntax.WithExpressionBody(Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody) -> Microsoft.CodeAnalysis.CSharp.Syntax.AccessorDeclarationSyntax @@ -260,6 +262,9 @@ static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetDeclaredSymbol(this Mic static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetDeclaredSymbol(this Microsoft.CodeAnalysis.SemanticModel semanticModel, Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax declarationSyntax, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.ISymbol static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetDeclaredSymbol(this Microsoft.CodeAnalysis.SemanticModel semanticModel, Microsoft.CodeAnalysis.CSharp.Syntax.TupleExpressionSyntax declaratorSyntax, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.INamedTypeSymbol static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetForEachStatementInfo(this Microsoft.CodeAnalysis.SemanticModel semanticModel, Microsoft.CodeAnalysis.CSharp.Syntax.CommonForEachStatementSyntax forEachStatement) -> Microsoft.CodeAnalysis.CSharp.ForEachStatementInfo +static Microsoft.CodeAnalysis.CSharp.LanguageVersionFacts.MapSpecifiedToEffectiveVersion(this Microsoft.CodeAnalysis.CSharp.LanguageVersion version) -> Microsoft.CodeAnalysis.CSharp.LanguageVersion +static Microsoft.CodeAnalysis.CSharp.LanguageVersionFacts.ToDisplayString(this Microsoft.CodeAnalysis.CSharp.LanguageVersion version) -> string +static Microsoft.CodeAnalysis.CSharp.LanguageVersionFacts.TryParse(this string version, out Microsoft.CodeAnalysis.CSharp.LanguageVersion result) -> bool static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.AccessorDeclaration(Microsoft.CodeAnalysis.CSharp.SyntaxKind kind) -> Microsoft.CodeAnalysis.CSharp.Syntax.AccessorDeclarationSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.AccessorDeclaration(Microsoft.CodeAnalysis.CSharp.SyntaxKind kind, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body) -> Microsoft.CodeAnalysis.CSharp.Syntax.AccessorDeclarationSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.AccessorDeclaration(Microsoft.CodeAnalysis.CSharp.SyntaxKind kind, Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody) -> Microsoft.CodeAnalysis.CSharp.Syntax.AccessorDeclarationSyntax diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index 930f4710de55c..6775f3a8d74fa 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -1191,7 +1191,8 @@ public void ArgumentParsing() [Fact] public void LangVersion() { - LanguageVersion defaultVersion = LanguageVersion.Default.MapSpecifiedToEffectiveVersion(); + LanguageVersion defaultEffectiveVersion = LanguageVersion.Default.MapSpecifiedToEffectiveVersion(); + LanguageVersion latestEffectiveVersion = LanguageVersion.Latest.MapSpecifiedToEffectiveVersion(); var parsedArgs = DefaultParse(new[] { "/langversion:1", "a.cs" }, _baseDirectory); parsedArgs.Errors.Verify(); @@ -1233,15 +1234,19 @@ public void LangVersion() parsedArgs.Errors.Verify(); Assert.Equal(LanguageVersion.CSharp7, parsedArgs.ParseOptions.LanguageVersion); + parsedArgs = DefaultParse(new[] { "/langversion:7.1", "a.cs" }, _baseDirectory); + parsedArgs.Errors.Verify(); + Assert.Equal(LanguageVersion.CSharp7_1, parsedArgs.ParseOptions.LanguageVersion); + parsedArgs = DefaultParse(new[] { "/langversion:default", "a.cs" }, _baseDirectory); parsedArgs.Errors.Verify(); Assert.Equal(LanguageVersion.Default, parsedArgs.ParseOptions.SpecifiedLanguageVersion); - Assert.Equal(defaultVersion, parsedArgs.ParseOptions.LanguageVersion); + Assert.Equal(defaultEffectiveVersion, parsedArgs.ParseOptions.LanguageVersion); parsedArgs = DefaultParse(new[] { "/langversion:latest", "a.cs" }, _baseDirectory); parsedArgs.Errors.Verify(); Assert.Equal(LanguageVersion.Latest, parsedArgs.ParseOptions.SpecifiedLanguageVersion); - Assert.Equal(defaultVersion, parsedArgs.ParseOptions.LanguageVersion); + Assert.Equal(latestEffectiveVersion, parsedArgs.ParseOptions.LanguageVersion); parsedArgs = DefaultParse(new[] { "/langversion:iso-1", "a.cs" }, _baseDirectory); parsedArgs.Errors.Verify(); @@ -1251,14 +1256,11 @@ public void LangVersion() parsedArgs.Errors.Verify(); Assert.Equal(LanguageVersion.CSharp2, parsedArgs.ParseOptions.LanguageVersion); - parsedArgs = DefaultParse(new[] { "/langversion:default", "a.cs" }, _baseDirectory); - parsedArgs.Errors.Verify(); - Assert.Equal(defaultVersion, parsedArgs.ParseOptions.LanguageVersion); - // default value parsedArgs = DefaultParse(new[] { "a.cs" }, _baseDirectory); parsedArgs.Errors.Verify(); - Assert.Equal(defaultVersion, parsedArgs.ParseOptions.LanguageVersion); + Assert.Equal(LanguageVersion.Default, parsedArgs.ParseOptions.SpecifiedLanguageVersion); + Assert.Equal(defaultEffectiveVersion, parsedArgs.ParseOptions.LanguageVersion); // override value with iso-1 parsedArgs = DefaultParse(new[] { "/langversion:6", "/langversion:iso-1", "a.cs" }, _baseDirectory); @@ -1272,13 +1274,13 @@ public void LangVersion() // override value with default parsedArgs = DefaultParse(new[] { "/langversion:6", "/langversion:default", "a.cs" }, _baseDirectory); - Assert.Equal(defaultVersion, parsedArgs.ParseOptions.LanguageVersion); + Assert.Equal(defaultEffectiveVersion, parsedArgs.ParseOptions.LanguageVersion); parsedArgs.Errors.Verify(); // override value with default parsedArgs = DefaultParse(new[] { "/langversion:7", "/langversion:default", "a.cs" }, _baseDirectory); parsedArgs.Errors.Verify(); - Assert.Equal(defaultVersion, parsedArgs.ParseOptions.LanguageVersion); + Assert.Equal(defaultEffectiveVersion, parsedArgs.ParseOptions.LanguageVersion); // override value with numeric parsedArgs = DefaultParse(new[] { "/langversion:iso-2", "/langversion:6", "a.cs" }, _baseDirectory); @@ -1288,50 +1290,106 @@ public void LangVersion() // errors parsedArgs = DefaultParse(new[] { "/langversion:iso-3", "a.cs" }, _baseDirectory); parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadCompatMode).WithArguments("iso-3")); - Assert.Equal(defaultVersion, parsedArgs.ParseOptions.LanguageVersion); + Assert.Equal(defaultEffectiveVersion, parsedArgs.ParseOptions.LanguageVersion); parsedArgs = DefaultParse(new[] { "/langversion:iso1", "a.cs" }, _baseDirectory); parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_BadCompatMode).WithArguments("iso1")); - Assert.Equal(defaultVersion, parsedArgs.ParseOptions.LanguageVersion); + Assert.Equal(defaultEffectiveVersion, parsedArgs.ParseOptions.LanguageVersion); parsedArgs = DefaultParse(new[] { "/langversion:0", "/langversion:7", "a.cs" }, _baseDirectory); parsedArgs.Errors.Verify( Diagnostic(ErrorCode.ERR_BadCompatMode).WithArguments("0")); - Assert.Equal(defaultVersion, parsedArgs.ParseOptions.LanguageVersion); + Assert.Equal(defaultEffectiveVersion, parsedArgs.ParseOptions.LanguageVersion); parsedArgs = DefaultParse(new[] { "/langversion:0", "/langversion:8", "a.cs" }, _baseDirectory); parsedArgs.Errors.Verify( Diagnostic(ErrorCode.ERR_BadCompatMode).WithArguments("0"), Diagnostic(ErrorCode.ERR_BadCompatMode).WithArguments("8")); - Assert.Equal(defaultVersion, parsedArgs.ParseOptions.LanguageVersion); + Assert.Equal(defaultEffectiveVersion, parsedArgs.ParseOptions.LanguageVersion); parsedArgs = DefaultParse(new[] { "/langversion:0", "/langversion:1000", "a.cs" }, _baseDirectory); parsedArgs.Errors.Verify( Diagnostic(ErrorCode.ERR_BadCompatMode).WithArguments("0"), Diagnostic(ErrorCode.ERR_BadCompatMode).WithArguments("1000")); - Assert.Equal(defaultVersion, parsedArgs.ParseOptions.LanguageVersion); + Assert.Equal(defaultEffectiveVersion, parsedArgs.ParseOptions.LanguageVersion); parsedArgs = DefaultParse(new[] { "/langversion", "a.cs" }, _baseDirectory); parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("", "/langversion:")); - Assert.Equal(defaultVersion, parsedArgs.ParseOptions.LanguageVersion); + Assert.Equal(defaultEffectiveVersion, parsedArgs.ParseOptions.LanguageVersion); parsedArgs = DefaultParse(new[] { "/LANGversion:", "a.cs" }, _baseDirectory); parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("", "/langversion:")); - Assert.Equal(defaultVersion, parsedArgs.ParseOptions.LanguageVersion); + Assert.Equal(defaultEffectiveVersion, parsedArgs.ParseOptions.LanguageVersion); parsedArgs = DefaultParse(new[] { "/langversion: ", "a.cs" }, _baseDirectory); parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("", "/langversion:")); - Assert.Equal(defaultVersion, parsedArgs.ParseOptions.LanguageVersion); + Assert.Equal(defaultEffectiveVersion, parsedArgs.ParseOptions.LanguageVersion); } [Fact] - public void RememberToUpdateDiagnosticsWhenUpdatingLangVersion() + public void LanguageVersionAdded_Canary() { - // When new language versions are added, this test will fail. Remember to update the diagnostics message (ERR_BadCompatMode). - Assert.Equal(LanguageVersion.CSharp7, LanguageVersion.Latest.MapSpecifiedToEffectiveVersion()); + // When a new version is added, this test will break. This list must be checked: + // - update the command-line error for bad /langver flag () + // - update the "UpgradeProject" codefixer + // - update the IDE drop-down for selecting Language Version + // - don't fix the canary test until you update all the tests that include it + Assert.Equal(LanguageVersion.CSharp7_1, LanguageVersion.Latest.MapSpecifiedToEffectiveVersion()); Assert.Equal(LanguageVersion.CSharp7, LanguageVersion.Default.MapSpecifiedToEffectiveVersion()); } + [Fact] + public void LanguageVersion_DisplayString() + { + AssertEx.SetEqual(new[] { "default", "1", "2", "3", "4", "5", "6", "7", "7.1", "latest" }, + Enum.GetValues(typeof(LanguageVersion)).Cast().Select(v => v.ToDisplayString())); + // For minor versions, the format should be "x.y", such as "7.1" + } + + [Fact] + public void LanguageVersion_MapSpecifiedToEffectiveVersion() + { + Assert.Equal(LanguageVersion.CSharp1, LanguageVersion.CSharp1.MapSpecifiedToEffectiveVersion()); + Assert.Equal(LanguageVersion.CSharp2, LanguageVersion.CSharp2.MapSpecifiedToEffectiveVersion()); + Assert.Equal(LanguageVersion.CSharp3, LanguageVersion.CSharp3.MapSpecifiedToEffectiveVersion()); + Assert.Equal(LanguageVersion.CSharp4, LanguageVersion.CSharp4.MapSpecifiedToEffectiveVersion()); + Assert.Equal(LanguageVersion.CSharp5, LanguageVersion.CSharp5.MapSpecifiedToEffectiveVersion()); + Assert.Equal(LanguageVersion.CSharp6, LanguageVersion.CSharp6.MapSpecifiedToEffectiveVersion()); + Assert.Equal(LanguageVersion.CSharp7, LanguageVersion.CSharp7.MapSpecifiedToEffectiveVersion()); + Assert.Equal(LanguageVersion.CSharp7, LanguageVersion.Default.MapSpecifiedToEffectiveVersion()); + Assert.Equal(LanguageVersion.CSharp7_1, LanguageVersion.Latest.MapSpecifiedToEffectiveVersion()); + + // The canary check is a reminder that this test needs to be updated when a language version is added + LanguageVersionAdded_Canary(); + } + + [Theory, + InlineData("iso-1", true, LanguageVersion.CSharp1), + InlineData("ISO-1", true, LanguageVersion.CSharp1), + InlineData("iso-2", true, LanguageVersion.CSharp2), + InlineData("1", true, LanguageVersion.CSharp1), + InlineData("2", true, LanguageVersion.CSharp2), + InlineData("3", true, LanguageVersion.CSharp3), + InlineData("4", true, LanguageVersion.CSharp4), + InlineData("5", true, LanguageVersion.CSharp5), + InlineData("05", true, LanguageVersion.CSharp5), + InlineData("6", true, LanguageVersion.CSharp6), + InlineData("7", true, LanguageVersion.CSharp7), + InlineData("07", true, LanguageVersion.CSharp7), + InlineData("7.1", true, LanguageVersion.CSharp7_1), + InlineData("default", true, LanguageVersion.Default), + InlineData("latest", true, LanguageVersion.Latest), + InlineData(null, true, LanguageVersion.Default), + InlineData("bad", false, LanguageVersion.Default)] + public void LanguageVersion_TryParseDisplayString(string input, bool success, LanguageVersion expected) + { + Assert.Equal(success, input.TryParse(out var version)); + Assert.Equal(expected, version); + + // The canary check is a reminder that this test needs to be updated when a language version is added + LanguageVersionAdded_Canary(); + } + [Fact] [WorkItem(546961, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546961")] public void Define() @@ -1384,7 +1442,7 @@ public void Define() var nonCompliant = "def1;;def2;"; parsed = CSharpCommandLineParser.ParseConditionalCompilationSymbols(nonCompliant, out diagnostics); diagnostics.Verify( - // warning CS2029: Invalid value for '/define'; '' is not a valid identifier + // warning CS2029: Invalid name for a preprocessing symbol; '' is not a valid identifier Diagnostic(ErrorCode.WRN_DefineIdentifierRequired).WithArguments("")); Assert.Equal(new[] { "def1", "def2" }, parsed); @@ -6363,7 +6421,7 @@ public void InvalidDefineSwitch() outWriter = new StringWriter(CultureInfo.InvariantCulture); exitCode = new MockCSharpCompiler(null, _baseDirectory, new[] { "/nologo", "/preferreduilang:en", "/t:library", src.ToString(), @"/define:""""" }).Run(outWriter); Assert.Equal(0, exitCode); - Assert.Equal("warning CS2029: Invalid value for '/define'; '' is not a valid identifier", outWriter.ToString().Trim()); + Assert.Equal("warning CS2029: Invalid name for a preprocessing symbol; '' is not a valid identifier", outWriter.ToString().Trim()); outWriter = new StringWriter(CultureInfo.InvariantCulture); exitCode = new MockCSharpCompiler(null, _baseDirectory, new[] { "/nologo", "/preferreduilang:en", "/t:library", src.ToString(), "/define: " }).Run(outWriter); @@ -6378,29 +6436,32 @@ public void InvalidDefineSwitch() outWriter = new StringWriter(CultureInfo.InvariantCulture); exitCode = new MockCSharpCompiler(null, _baseDirectory, new[] { "/nologo", "/preferreduilang:en", "/t:library", src.ToString(), "/define:,,," }).Run(outWriter); Assert.Equal(0, exitCode); - Assert.Equal("warning CS2029: Invalid value for '/define'; '' is not a valid identifier", outWriter.ToString().Trim()); + Assert.Equal("warning CS2029: Invalid name for a preprocessing symbol; '' is not a valid identifier", outWriter.ToString().Trim()); outWriter = new StringWriter(CultureInfo.InvariantCulture); exitCode = new MockCSharpCompiler(null, _baseDirectory, new[] { "/nologo", "/preferreduilang:en", "/t:library", src.ToString(), "/define:,blah,Blah" }).Run(outWriter); Assert.Equal(0, exitCode); - Assert.Equal("warning CS2029: Invalid value for '/define'; '' is not a valid identifier", outWriter.ToString().Trim()); + Assert.Equal("warning CS2029: Invalid name for a preprocessing symbol; '' is not a valid identifier", outWriter.ToString().Trim()); outWriter = new StringWriter(CultureInfo.InvariantCulture); exitCode = new MockCSharpCompiler(null, _baseDirectory, new[] { "/nologo", "/preferreduilang:en", "/t:library", src.ToString(), "/define:a;;b@" }).Run(outWriter); Assert.Equal(0, exitCode); - Assert.Equal("warning CS2029: Invalid value for '/define'; '' is not a valid identifier", outWriter.ToString().Trim()); + var errorLines = outWriter.ToString().Trim().Split(new string[] { Environment.NewLine }, StringSplitOptions.None); + Assert.Equal("warning CS2029: Invalid name for a preprocessing symbol; '' is not a valid identifier", errorLines[0]); + Assert.Equal("warning CS2029: Invalid name for a preprocessing symbol; 'b@' is not a valid identifier", errorLines[1]); outWriter = new StringWriter(CultureInfo.InvariantCulture); exitCode = new MockCSharpCompiler(null, _baseDirectory, new[] { "/nologo", "/preferreduilang:en", "/t:library", src.ToString(), "/define:a,b@;" }).Run(outWriter); Assert.Equal(0, exitCode); - Assert.Equal("warning CS2029: Invalid value for '/define'; 'b@' is not a valid identifier", outWriter.ToString().Trim()); + Assert.Equal("warning CS2029: Invalid name for a preprocessing symbol; 'b@' is not a valid identifier", outWriter.ToString().Trim()); //Bug 531612 - Native would normally not give the 2nd warning outWriter = new StringWriter(CultureInfo.InvariantCulture); exitCode = new MockCSharpCompiler(null, _baseDirectory, new[] { "/nologo", "/preferreduilang:en", "/t:library", src.ToString(), @"/define:OE_WIN32=-1:LANG_HOST_EN=-1:LANG_OE_EN=-1:LANG_PRJ_EN=-1:HOST_COM20SDKEVERETT=-1:EXEMODE=-1:OE_NT5=-1:Win32=-1", @"/d:TRACE=TRUE,DEBUG=TRUE" }).Run(outWriter); Assert.Equal(0, exitCode); - Assert.Equal(@"warning CS2029: Invalid value for '/define'; 'OE_WIN32=-1:LANG_HOST_EN=-1:LANG_OE_EN=-1:LANG_PRJ_EN=-1:HOST_COM20SDKEVERETT=-1:EXEMODE=-1:OE_NT5=-1:Win32=-1' is not a valid identifier -warning CS2029: Invalid value for '/define'; 'TRACE=TRUE' is not a valid identifier", outWriter.ToString().Trim()); + errorLines = outWriter.ToString().Trim().Split(new string[] { Environment.NewLine }, StringSplitOptions.None); + Assert.Equal(@"warning CS2029: Invalid name for a preprocessing symbol; 'OE_WIN32=-1:LANG_HOST_EN=-1:LANG_OE_EN=-1:LANG_PRJ_EN=-1:HOST_COM20SDKEVERETT=-1:EXEMODE=-1:OE_NT5=-1:Win32=-1' is not a valid identifier", errorLines[0]); + Assert.Equal(@"warning CS2029: Invalid name for a preprocessing symbol; 'TRACE=TRUE' is not a valid identifier", errorLines[1]); CleanupAllGeneratedFiles(src.Path); } @@ -8883,6 +8944,37 @@ public void Version() } } + [Fact] + public void CompilingCodeWithInvalidPreProcessorSymbolsShouldProvideDiagnostics() + { + var parsedArgs = DefaultParse(new[] { "/define:1", "a.cs" }, _baseDirectory); + parsedArgs.Errors.Verify( + // warning CS2029: Invalid name for a preprocessing symbol; '1' is not a valid identifier + Diagnostic(ErrorCode.WRN_DefineIdentifierRequired).WithArguments("1").WithLocation(1, 1)); + } + + [Fact] + public void CompilingCodeWithInvalidLanguageVersionShouldProvideDiagnostics() + { + var parsedArgs = DefaultParse(new[] { "/langversion:1000", "a.cs" }, _baseDirectory); + parsedArgs.Errors.Verify( + // error CS1617: Invalid option '1000' for /langversion; must be ISO-1, ISO-2, Default or an integer in range 1 to 6. + Diagnostic(ErrorCode.ERR_BadCompatMode).WithArguments("1000").WithLocation(1, 1)); + } + + [Fact, WorkItem(16913, "https://github.com/dotnet/roslyn/issues/16913")] + public void CompilingCodeWithMultipleInvalidPreProcessorSymbolsShouldErrorOut() + { + var parsedArgs = DefaultParse(new[] { "/define:valid1,2invalid,valid3", "/define:4,5,valid6", "a.cs" }, _baseDirectory); + parsedArgs.Errors.Verify( + // warning CS2029: Invalid value for '/define'; '2invalid' is not a valid identifier + Diagnostic(ErrorCode.WRN_DefineIdentifierRequired).WithArguments("2invalid"), + // warning CS2029: Invalid value for '/define'; '4' is not a valid identifier + Diagnostic(ErrorCode.WRN_DefineIdentifierRequired).WithArguments("4"), + // warning CS2029: Invalid value for '/define'; '5' is not a valid identifier + Diagnostic(ErrorCode.WRN_DefineIdentifierRequired).WithArguments("5")); + } + public class QuotedArgumentTests { private void VerifyQuotedValid(string name, string value, T expected, Func getValue) diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs index 3b9eeb1c190b0..f7387c4b2a074 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs @@ -9219,6 +9219,10 @@ static void Main() // CS0151ERR_IntegralTypeValueExpected} Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "}").WithArguments("}").WithLocation(8, 36) ); + + Assert.Equal("7", Compilation.GetRequiredLanguageVersion(comp.GetDiagnostics()[0])); + Assert.Null(Compilation.GetRequiredLanguageVersion(comp.GetDiagnostics()[2])); + Assert.Throws(() => Compilation.GetRequiredLanguageVersion(null)); } [Fact] diff --git a/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs b/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs index a0fa5bba7f79f..bd7c85b7f815f 100644 --- a/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs +++ b/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs @@ -14,6 +14,7 @@ using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; +using Microsoft.DiaSymReader.Tools; using Roslyn.Test.PdbUtilities; using Roslyn.Test.Utilities; using Xunit; diff --git a/src/Compilers/CSharp/Test/Semantic/Diagnostics/GetDiagnosticsTests.cs b/src/Compilers/CSharp/Test/Semantic/Diagnostics/GetDiagnosticsTests.cs index 1b8205d5cdb91..c527dffe9b8ec 100644 --- a/src/Compilers/CSharp/Test/Semantic/Diagnostics/GetDiagnosticsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Diagnostics/GetDiagnosticsTests.cs @@ -299,5 +299,100 @@ public void TestEventQueueCompletionForEmptyCompilation() Assert.True(compilation.EventQueue.IsCompleted); } + + [Fact] + public void CompilingCodeWithInvalidPreProcessorSymbolsShouldProvideDiagnostics() + { + var compilation = CreateCompilation(string.Empty, parseOptions: new CSharpParseOptions().WithPreprocessorSymbols(new[] { "1" })); + + compilation.VerifyDiagnostics( + // (1,1): error CS8301: Invalid name for a preprocessing symbol; '1' is not a valid identifier + // + Diagnostic(ErrorCode.ERR_InvalidPreprocessingSymbol, "").WithArguments("1").WithLocation(1, 1)); + } + + [Fact] + public void CompilingCodeWithInvalidSourceCodeKindShouldProvideDiagnostics() + { +#pragma warning disable CS0618 // Type or member is obsolete + var compilation = CreateCompilationWithMscorlib45(string.Empty, parseOptions: new CSharpParseOptions().WithKind(SourceCodeKind.Interactive)); +#pragma warning restore CS0618 // Type or member is obsolete + + compilation.VerifyDiagnostics( + // (1,1): error CS8190: Provided source code kind is unsupported or invalid: 'Interactive' + // + Diagnostic(ErrorCode.ERR_BadSourceCodeKind, "").WithArguments("Interactive").WithLocation(1, 1)); + } + + [Fact] + public void CompilingCodeWithInvalidLanguageVersionShouldProvideDiagnostics() + { + var compilation = CreateCompilation(string.Empty, parseOptions: new CSharpParseOptions().WithLanguageVersion((LanguageVersion)10000)); + compilation.VerifyDiagnostics( + // (1,1): error CS8192: Provided language version is unsupported or invalid: '10000'. + // + Diagnostic(ErrorCode.ERR_BadLanguageVersion, "").WithArguments("10000").WithLocation(1, 1)); + } + + [Fact] + public void CompilingCodeWithInvalidDocumentationModeShouldProvideDiagnostics() + { + var compilation = CreateCompilation(string.Empty, parseOptions: new CSharpParseOptions().WithDocumentationMode(unchecked((DocumentationMode)100))); + compilation.VerifyDiagnostics( + // (1,1): error CS8191: Provided documentation mode is unsupported or invalid: '100'. + // + Diagnostic(ErrorCode.ERR_BadDocumentationMode, "").WithArguments("100").WithLocation(1, 1)); + } + + [Fact] + public void CompilingCodeWithInvalidParseOptionsInMultipleSyntaxTreesShouldReportThemAll() + { + var syntaxTree1 = Parse(string.Empty, options: new CSharpParseOptions().WithPreprocessorSymbols(new[] { "1" })); + var syntaxTree2 = Parse(string.Empty, options: new CSharpParseOptions().WithPreprocessorSymbols(new[] { "2" })); + var syntaxTree3 = Parse(string.Empty, options: new CSharpParseOptions().WithPreprocessorSymbols(new[] { "3" })); + + var compilation = CreateCompilation(new[] { syntaxTree1, syntaxTree2, syntaxTree3 }); + var diagnostics = compilation.GetDiagnostics(); + + diagnostics.Verify( + // (1,1): error CS8301: Invalid name for a preprocessing symbol; '1' is not a valid identifier + // + Diagnostic(ErrorCode.ERR_InvalidPreprocessingSymbol, "").WithArguments("1").WithLocation(1, 1), + // (1,1): error CS8301: Invalid name for a preprocessing symbol; '2' is not a valid identifier + // + Diagnostic(ErrorCode.ERR_InvalidPreprocessingSymbol, "").WithArguments("2").WithLocation(1, 1), + // (1,1): error CS8301: Invalid name for a preprocessing symbol; '3' is not a valid identifier + // + Diagnostic(ErrorCode.ERR_InvalidPreprocessingSymbol, "").WithArguments("3").WithLocation(1, 1)); + + Assert.True(diagnostics[0].Location.SourceTree.Equals(syntaxTree1)); + Assert.True(diagnostics[1].Location.SourceTree.Equals(syntaxTree2)); + Assert.True(diagnostics[2].Location.SourceTree.Equals(syntaxTree3)); + } + + [Fact] + public void CompilingCodeWithSameParseOptionsInMultipleSyntaxTreesShouldReportOnlyNonDuplicates() + { + var parseOptions1 = new CSharpParseOptions().WithPreprocessorSymbols(new[] { "1" }); + var parseOptions2 = new CSharpParseOptions().WithPreprocessorSymbols(new[] { "2" }); + + var syntaxTree1 = Parse(string.Empty, options: parseOptions1); + var syntaxTree2 = Parse(string.Empty, options: parseOptions2); + var syntaxTree3 = Parse(string.Empty, options: parseOptions2); + + var compilation = CreateCompilationWithMscorlib(new[] { syntaxTree1, syntaxTree2, syntaxTree3 }); + var diagnostics = compilation.GetDiagnostics(); + + diagnostics.Verify( + // (1,1): error CS8301: Invalid name for a preprocessing symbol; '1' is not a valid identifier + // + Diagnostic(ErrorCode.ERR_InvalidPreprocessingSymbol, "").WithArguments("1").WithLocation(1, 1), + // (1,1): error CS8301: Invalid name for a preprocessing symbol; '2' is not a valid identifier + // + Diagnostic(ErrorCode.ERR_InvalidPreprocessingSymbol, "").WithArguments("2").WithLocation(1, 1)); + + Assert.True(diagnostics[0].Location.SourceTree.Equals(syntaxTree1)); + Assert.True(diagnostics[1].Location.SourceTree.Equals(syntaxTree2)); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests.cs index 481617dd9330a..2aa11ad5a61de 100644 --- a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests { - public partial class IOperationTests : CompilingTestBase + public partial class IOperationTests : SemanticModelTestBase { [Fact] [WorkItem(382240, "https://devdiv.visualstudio.com/DevDiv/_workitems?id=382240")] diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ISymbolInitializer.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ISymbolInitializer.cs index c24cc6deae1ef..786a849684702 100644 --- a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ISymbolInitializer.cs +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_ISymbolInitializer.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests { - public partial class IOperationTests : CompilingTestBase + public partial class IOperationTests : SemanticModelTestBase { [Fact, WorkItem(17595, "https://github.com/dotnet/roslyn/issues/17595")] public void NoInitializers() diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/ScriptSemanticsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/ScriptSemanticsTests.cs index f8a07cfbc7b57..17afc57dd7443 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/ScriptSemanticsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/ScriptSemanticsTests.cs @@ -1150,15 +1150,6 @@ public void Errors_03() ); } - [Fact] - [WorkItem(10023, "https://github.com/dotnet/roslyn/issues/10023")] - public void NoNeedToTestSourceCodeKindInteractive() - { -#pragma warning disable CS0618 - Assert.Throws(() => new CSharp.CSharpParseOptions(kind: SourceCodeKind.Interactive)); -#pragma warning restore CS0618 - } - private static MemberAccessExpressionSyntax ErrorTestsGetNode(SyntaxTree syntaxTree) { var node1 = (CompilationUnitSyntax)syntaxTree.GetRoot(); diff --git a/src/Compilers/CSharp/Test/Syntax/IncrementalParsing/IncrementalParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/IncrementalParsing/IncrementalParsingTests.cs index 513938589da49..b85f0344d0f3f 100644 --- a/src/Compilers/CSharp/Test/Syntax/IncrementalParsing/IncrementalParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/IncrementalParsing/IncrementalParsingTests.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests { - public class IncrementalParsingTests + public class IncrementalParsingTests : TestBase { private CSharpParseOptions GetOptions(string[] defines) { @@ -2341,6 +2341,93 @@ public void InsertOpenBraceBeforeCodes() CompareIncToFullParseErrors(reparsedTree, parsedTree); } + [WorkItem(6676, "https://github.com/dotnet/roslyn/issues/6676")] + [Fact] + public void InsertExpressionStatementWithoutSemicolonBefore() + { + SourceText oldText = SourceText.From(@"System.Console.WriteLine(true) +"); + var startTree = SyntaxFactory.ParseSyntaxTree(oldText, options: TestOptions.Script); + + startTree.GetDiagnostics().Verify(); + + var newText = oldText.WithChanges(new TextChange(new TextSpan(0, 0), @"System.Console.WriteLine(false) +")); + + AssertEx.AreEqual(@"System.Console.WriteLine(false) +System.Console.WriteLine(true) +", +newText.ToString()); + + var reparsedTree = startTree.WithChangedText(newText); + var parsedTree = SyntaxFactory.ParseSyntaxTree(newText, options: TestOptions.Script); + + parsedTree.GetDiagnostics().Verify( + // (1,32): error CS1002: ; expected + // System.Console.WriteLine(false) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 32)); + + CompareIncToFullParseErrors(reparsedTree, parsedTree); + } + + [WorkItem(6676, "https://github.com/dotnet/roslyn/issues/6676")] + [Fact] + public void InsertExpressionStatementWithoutSemicolonAfter() + { + SourceText oldText = SourceText.From(@"System.Console.WriteLine(true) +"); + var startTree = SyntaxFactory.ParseSyntaxTree(oldText, options: TestOptions.Script); + + startTree.GetDiagnostics().Verify(); + + var newText = oldText.WithInsertAt( + oldText.Length, + @"System.Console.WriteLine(false) +"); + + AssertEx.Equal(@"System.Console.WriteLine(true) +System.Console.WriteLine(false) +", newText.ToString()); + + var reparsedTree = startTree.WithChangedText(newText); + + var parsedTree = SyntaxFactory.ParseSyntaxTree(newText, options: TestOptions.Script); + parsedTree.GetDiagnostics().Verify( + // (1,31): error CS1002: ; expected + // System.Console.WriteLine(true) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 31)); + + CompareIncToFullParseErrors(reparsedTree, parsedTree); + } + + [WorkItem(6676, "https://github.com/dotnet/roslyn/issues/6676")] + [Fact] + public void MakeEmbeddedExpressionStatementWithoutSemicolon() + { + SourceText oldText = SourceText.From(@"System.Console.WriteLine(true) +"); + var startTree = SyntaxFactory.ParseSyntaxTree(oldText, options: TestOptions.Script); + + startTree.GetDiagnostics().Verify(); + + var newText = oldText.WithChanges(new TextChange(new TextSpan(0, 0), @"if (false) +")); + + AssertEx.Equal(@"if (false) +System.Console.WriteLine(true) +", newText.ToString()); + + var reparsedTree = startTree.WithChangedText(newText); + var parsedTree = SyntaxFactory.ParseSyntaxTree(newText, options: TestOptions.Script); + + parsedTree.GetDiagnostics().Verify( + // (2,31): error CS1002: ; expected + // System.Console.WriteLine(true) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(2, 31)); + + CompareIncToFullParseErrors(reparsedTree, parsedTree); + } + [WorkItem(531404, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/531404")] [Fact] public void AppendDisabledText() diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/CSharpParseOptionsTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/CSharpParseOptionsTests.cs index 33d35123ee37a..8d47f1cef4058 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/CSharpParseOptionsTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/CSharpParseOptionsTests.cs @@ -21,6 +21,17 @@ private void TestProperty(Func fac Assert.Same(newOpt2, newOpt1); } + [Fact] + [WorkItem(15358, "https://github.com/dotnet/roslyn/issues/15358")] + public void WithDocumentationModeDoesntChangeFeatures() + { + var kvp = new KeyValuePair("IOperation", "true"); + var po = new CSharpParseOptions().WithFeatures(new[] { kvp }); + Assert.Equal(po.Features.AsSingleton(), kvp); + var po2 = po.WithDocumentationMode(DocumentationMode.Diagnose); + Assert.Equal(po2.Features.AsSingleton(), kvp); + } + [Fact] public void WithXxx() { @@ -29,32 +40,11 @@ public void WithXxx() TestProperty((old, value) => old.WithDocumentationMode(value), opt => opt.DocumentationMode, DocumentationMode.None); TestProperty((old, value) => old.WithPreprocessorSymbols(value), opt => opt.PreprocessorSymbols, ImmutableArray.Create("A", "B", "C")); - Assert.Throws(() => CSharpParseOptions.Default.WithKind((SourceCodeKind)Int32.MaxValue)); - Assert.Throws(() => CSharpParseOptions.Default.WithLanguageVersion((LanguageVersion)1000)); - Assert.Equal(0, CSharpParseOptions.Default.WithPreprocessorSymbols(ImmutableArray.Create("A", "B")).WithPreprocessorSymbols(default(ImmutableArray)).PreprocessorSymbols.Length); Assert.Equal(0, CSharpParseOptions.Default.WithPreprocessorSymbols(ImmutableArray.Create("A", "B")).WithPreprocessorSymbols((IEnumerable)null).PreprocessorSymbols.Length); Assert.Equal(0, CSharpParseOptions.Default.WithPreprocessorSymbols(ImmutableArray.Create("A", "B")).WithPreprocessorSymbols((string[])null).PreprocessorSymbols.Length); } - [Fact] - public void ConstructorValidation() - { - Assert.Throws(() => new CSharpParseOptions(kind: (SourceCodeKind)Int32.MaxValue)); - Assert.Throws(() => new CSharpParseOptions(languageVersion: (LanguageVersion)1000)); - } - - [Fact(), WorkItem(546206, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546206")] - public void InvalidDefineSymbols() - { - // command line gives CS2029: Invalid value for '/define'; 'xxx' is not a valid identifier - Assert.Throws(() => new CSharpParseOptions(preprocessorSymbols: ImmutableArray.Create(""))); - Assert.Throws(() => new CSharpParseOptions(preprocessorSymbols: ImmutableArray.Create(" "))); - Assert.Throws(() => new CSharpParseOptions(preprocessorSymbols: ImmutableArray.Create("Good", "Bad.Symbol"))); - Assert.Throws(() => new CSharpParseOptions(preprocessorSymbols: ImmutableArray.Create("123", "Good"))); - Assert.Throws(() => new CSharpParseOptions(preprocessorSymbols: ImmutableArray.Create("Good", null, @"Bad\Symbol"))); - } - /// /// If this test fails, please update the /// and methods to @@ -73,5 +63,203 @@ public void TestFieldsForEqualsAndGetHashCode() "PreprocessorSymbols", "SpecifiedLanguageVersion"); } + + [Fact] + public void SpecifiedKindIsMappedCorrectly() + { + var options = new CSharpParseOptions(); + Assert.Equal(SourceCodeKind.Regular, options.Kind); + Assert.Equal(SourceCodeKind.Regular, options.SpecifiedKind); + + options.Errors.Verify(); + + options = new CSharpParseOptions(kind: SourceCodeKind.Regular); + Assert.Equal(SourceCodeKind.Regular, options.Kind); + Assert.Equal(SourceCodeKind.Regular, options.SpecifiedKind); + + options.Errors.Verify(); + + options = new CSharpParseOptions(kind: SourceCodeKind.Script); + Assert.Equal(SourceCodeKind.Script, options.Kind); + Assert.Equal(SourceCodeKind.Script, options.SpecifiedKind); + + options.Errors.Verify(); + +#pragma warning disable CS0618 // SourceCodeKind.Interactive is obsolete + options = new CSharpParseOptions(kind: SourceCodeKind.Interactive); + Assert.Equal(SourceCodeKind.Script, options.Kind); + Assert.Equal(SourceCodeKind.Interactive, options.SpecifiedKind); +#pragma warning restore CS0618 // SourceCodeKind.Interactive is obsolete + + options.Errors.Verify( + // error CS8190: Provided source code kind is unsupported or invalid: 'Interactive'. + Diagnostic(ErrorCode.ERR_BadSourceCodeKind).WithArguments("Interactive").WithLocation(1, 1)); + + options = new CSharpParseOptions(kind: (SourceCodeKind)int.MinValue); + Assert.Equal(SourceCodeKind.Regular, options.Kind); + Assert.Equal((SourceCodeKind)int.MinValue, options.SpecifiedKind); + + options.Errors.Verify( + // warning CS8190: Provided source code kind is unsupported or invalid: '-2147483648' + Diagnostic(ErrorCode.ERR_BadSourceCodeKind).WithArguments("-2147483648").WithLocation(1, 1)); + } + + [Fact] + public void TwoOptionsWithDifferentSpecifiedKindShouldNotHaveTheSameHashCodes() + { + var options1 = new CSharpParseOptions(kind: SourceCodeKind.Script); + var options2 = new CSharpParseOptions(kind: SourceCodeKind.Script); + + Assert.Equal(options1.GetHashCode(), options2.GetHashCode()); + + // They both map internally to SourceCodeKind.Script +#pragma warning disable CS0618 // SourceCodeKind.Interactive is obsolete + options1 = new CSharpParseOptions(kind: SourceCodeKind.Script); + options2 = new CSharpParseOptions(kind: SourceCodeKind.Interactive); +#pragma warning restore CS0618 // SourceCodeKind.Interactive is obsolete + + Assert.NotEqual(options1.GetHashCode(), options2.GetHashCode()); + } + + [Fact] + public void TwoOptionsWithDifferentSpecifiedKindShouldNotBeEqual() + { + var options1 = new CSharpParseOptions(kind: SourceCodeKind.Script); + var options2 = new CSharpParseOptions(kind: SourceCodeKind.Script); + + Assert.True(options1.Equals(options2)); + + // They both map internally to SourceCodeKind.Script +#pragma warning disable CS0618 // SourceCodeKind.Interactive is obsolete + options1 = new CSharpParseOptions(kind: SourceCodeKind.Script); + options2 = new CSharpParseOptions(kind: SourceCodeKind.Interactive); +#pragma warning restore CS0618 // SourceCodeKind.Interactive is obsolete + + Assert.False(options1.Equals(options2)); + } + + [Fact] + public void BadSourceCodeKindShouldProduceDiagnostics() + { +#pragma warning disable CS0618 // Type or member is obsolete + var options = new CSharpParseOptions(kind: SourceCodeKind.Interactive); +#pragma warning restore CS0618 // Type or member is obsolete + + options.Errors.Verify( + // error CS8190: Provided source code kind is unsupported or invalid: 'Interactive'. + Diagnostic(ErrorCode.ERR_BadSourceCodeKind).WithArguments("Interactive").WithLocation(1, 1)); + } + + [Fact] + public void BadDocumentationModeShouldProduceDiagnostics() + { + var options = new CSharpParseOptions(documentationMode: unchecked((DocumentationMode)100)); + + options.Errors.Verify( + // error CS8191: Provided documentation mode is unsupported or invalid: '100'. + Diagnostic(ErrorCode.ERR_BadDocumentationMode).WithArguments("100").WithLocation(1, 1)); + } + + [Fact] + public void BadLanguageVersionShouldProduceDiagnostics() + { + var options = new CSharpParseOptions(languageVersion: unchecked((LanguageVersion)10000)); + + options.Errors.Verify( + // error CS8191: Provided language version is unsupported or invalid: '10000'. + Diagnostic(ErrorCode.ERR_BadLanguageVersion).WithArguments("10000").WithLocation(1, 1)); + } + + [Fact] + public void BadPreProcessorSymbolsShouldProduceDiagnostics() + { + var options = new CSharpParseOptions(preprocessorSymbols: new[] { "test", "1" }); + + options.Errors.Verify( + // error CS8301: Invalid name for a preprocessing symbol; '1' is not a valid identifier + Diagnostic(ErrorCode.ERR_InvalidPreprocessingSymbol).WithArguments("1").WithLocation(1, 1)); + } + + [Fact] + public void BadSourceCodeKindShouldProduceDiagnostics_WithVariation() + { +#pragma warning disable CS0618 // Type or member is obsolete + var options = new CSharpParseOptions().WithKind(SourceCodeKind.Interactive); +#pragma warning restore CS0618 // Type or member is obsolete + + options.Errors.Verify( + // error CS8190: Provided source code kind is unsupported or invalid: 'Interactive'. + Diagnostic(ErrorCode.ERR_BadSourceCodeKind).WithArguments("Interactive").WithLocation(1, 1)); + } + + [Fact] + public void BadDocumentationModeShouldProduceDiagnostics_WithVariation() + { + var options = new CSharpParseOptions().WithDocumentationMode(unchecked((DocumentationMode)100)); + + options.Errors.Verify( + // error CS8191: Provided documentation mode is unsupported or invalid: '100'. + Diagnostic(ErrorCode.ERR_BadDocumentationMode).WithArguments("100").WithLocation(1, 1)); + } + + [Fact] + public void BadLanguageVersionShouldProduceDiagnostics_WithVariation() + { + var options = new CSharpParseOptions().WithLanguageVersion(unchecked((LanguageVersion)10000)); + + options.Errors.Verify( + // error CS8191: Provided language version is unsupported or invalid: '10000'. + Diagnostic(ErrorCode.ERR_BadLanguageVersion).WithArguments("10000").WithLocation(1, 1)); + } + + [Fact] + public void BadPreProcessorSymbolsShouldProduceDiagnostics_EmptyString() + { + var options = new CSharpParseOptions().WithPreprocessorSymbols(new[] { "" }); + + options.Errors.Verify( + // error CS8301: Invalid name for a preprocessing symbol; '' is not a valid identifier + Diagnostic(ErrorCode.ERR_InvalidPreprocessingSymbol).WithArguments("").WithLocation(1, 1)); + } + + [Fact] + public void BadPreProcessorSymbolsShouldProduceDiagnostics_WhiteSpacetring() + { + var options = new CSharpParseOptions().WithPreprocessorSymbols(new[] { " " }); + + options.Errors.Verify( + // error CS8301: Invalid name for a preprocessing symbol; ' ' is not a valid identifier + Diagnostic(ErrorCode.ERR_InvalidPreprocessingSymbol).WithArguments(" ").WithLocation(1, 1)); + } + + [Fact] + public void BadPreProcessorSymbolsShouldProduceDiagnostics_SymbolWithDots() + { + var options = new CSharpParseOptions().WithPreprocessorSymbols(new[] { "Good", "Bad.Symbol" }); + + options.Errors.Verify( + // error CS8301: Invalid name for a preprocessing symbol; 'Bad.Symbol' is not a valid identifier + Diagnostic(ErrorCode.ERR_InvalidPreprocessingSymbol).WithArguments("Bad.Symbol").WithLocation(1, 1)); + } + + [Fact] + public void BadPreProcessorSymbolsShouldProduceDiagnostics_SymbolWithSlashes() + { + var options = new CSharpParseOptions().WithPreprocessorSymbols(new[] { "Good", "Bad\\Symbol" }); + + options.Errors.Verify( + // error CS8301: Invalid name for a preprocessing symbol; 'Bad\Symbol' is not a valid identifier + Diagnostic(ErrorCode.ERR_InvalidPreprocessingSymbol).WithArguments("Bad\\Symbol").WithLocation(1, 1)); + } + + [Fact] + public void BadPreProcessorSymbolsShouldProduceDiagnostics_NullSymbol() + { + var options = new CSharpParseOptions().WithPreprocessorSymbols(new[] { "Good", null }); + + options.Errors.Verify( + // error CS8301: Invalid name for a preprocessing symbol; 'null' is not a valid identifier + Diagnostic(ErrorCode.ERR_InvalidPreprocessingSymbol).WithArguments("null").WithLocation(1, 1)); + } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs index 184cbd0c2a940..15abb2c6810b9 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs @@ -3533,5 +3533,127 @@ void M() EOF(); } + [Fact, WorkItem(17683, "https://github.com/dotnet/roslyn/issues/17683")] + public void Bug17683a() + { + var source = +@"from t in e +where +t == Int32. +MinValue +select t"; + UsingExpression(source); + N(SyntaxKind.QueryExpression); + { + N(SyntaxKind.FromClause); + { + N(SyntaxKind.FromKeyword); + N(SyntaxKind.IdentifierToken, "t"); + N(SyntaxKind.InKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + } + N(SyntaxKind.QueryBody); + { + N(SyntaxKind.WhereClause); + { + N(SyntaxKind.WhereKeyword); + N(SyntaxKind.EqualsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.EqualsEqualsToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Int32"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "MinValue"); + } + } + } + } + N(SyntaxKind.SelectClause); + { + N(SyntaxKind.SelectKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "t"); + } + } + } + } + EOF(); + } + + [Fact] + public void Bug17683b() + { + var source = +@"switch (e) +{ + case Int32. + MaxValue when true: + break; +}"; + UsingStatement(source); + 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.ConstantPattern); + { + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Int32"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "MaxValue"); + } + } + } + N(SyntaxKind.WhenClause); + { + N(SyntaxKind.WhenKeyword); + N(SyntaxKind.TrueLiteralExpression); + { + N(SyntaxKind.TrueKeyword); + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } } } \ No newline at end of file diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs index 6e27cef45f88d..61105c0f14c16 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs @@ -6,16 +6,45 @@ using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.CSharp.UnitTests { - public class StatementParsingTests + public class StatementParsingTests : ParsingTests { + public StatementParsingTests(ITestOutputHelper output) : base(output) { } + private StatementSyntax ParseStatement(string text, int offset = 0, ParseOptions options = null) { return SyntaxFactory.ParseStatement(text, offset, options); } + [Fact] + [WorkItem(17458, "https://github.com/dotnet/roslyn/issues/17458")] + public void ParsePrivate() + { + UsingStatement("private", + // (1,1): error CS1073: Unexpected token 'private' + // private + Diagnostic(ErrorCode.ERR_UnexpectedToken, "").WithArguments("private").WithLocation(1, 1), + // (1,1): error CS1525: Invalid expression term 'private' + // private + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "private").WithArguments("private").WithLocation(1, 1), + // (1,1): error CS1002: ; expected + // private + Diagnostic(ErrorCode.ERR_SemicolonExpected, "private").WithLocation(1, 1) + ); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + EOF(); + } + [Fact] public void TestName() { @@ -2661,6 +2690,21 @@ protected override void M() CSharpTestBase.Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(9, 10)); } + [WorkItem(6676, "https://github.com/dotnet/roslyn/issues/6676")] + [Fact] + public void TestRunEmbeddedStatementNotFollowedBySemicolon() + { + var text = @"if (true) +System.Console.WriteLine(true)"; + var statement = this.ParseStatement(text); + + Assert.NotNull(statement); + Assert.Equal(SyntaxKind.IfStatement, statement.Kind()); + Assert.Equal(text, statement.ToString()); + Assert.Equal(1, statement.Errors().Length); + Assert.Equal((int)ErrorCode.ERR_SemicolonExpected, statement.Errors()[0].Code); + } + private sealed class TokenAndTriviaWalker : CSharpSyntaxWalker { public int Tokens; diff --git a/src/Compilers/Core/CodeAnalysisTest/CommonParseOptionsTests.cs b/src/Compilers/Core/CodeAnalysisTest/CommonParseOptionsTests.cs index e1057ad8f3d66..bdbaf7ea375b6 100644 --- a/src/Compilers/Core/CodeAnalysisTest/CommonParseOptionsTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/CommonParseOptionsTests.cs @@ -19,10 +19,12 @@ public void TestFieldsForEqualsAndGetHashCode() ReflectionAssert.AssertPublicAndInternalFieldsAndProperties( typeof(ParseOptions), "DocumentationMode", - "Language", + "Errors", "Features", "Kind", - "PreprocessorSymbolNames"); + "Language", + "PreprocessorSymbolNames", + "SpecifiedKind"); } } } diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs index 4eeb2bfd0298b..c270b6573f891 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -2803,5 +2803,34 @@ public ImmutableArray GetUnreferencedAssemblyIdentities(Diagno } internal abstract bool IsUnreferencedAssemblyIdentityDiagnosticCode(int code); + + /// + /// Returns the required language version found in a , if any is found. + /// Returns null if none is found. + /// + public static string GetRequiredLanguageVersion(Diagnostic diagnostic) + { + if (diagnostic == null) + { + throw new ArgumentNullException(nameof(diagnostic)); + } + + bool found = false; + string foundVersion = null; + if (diagnostic.Arguments != null) + { + foreach (var argument in diagnostic.Arguments) + { + if (argument is RequiredLanguageVersion versionDiagnostic) + { + Debug.Assert(!found); // only one required language version in a given diagnostic + found = true; + foundVersion = versionDiagnostic.ToString(); + } + } + } + + return foundVersion; + } } } \ No newline at end of file diff --git a/src/Compilers/Core/Portable/Compilation/ParseOptions.cs b/src/Compilers/Core/Portable/Compilation/ParseOptions.cs index 0de52ad0c257f..c22c8772414a9 100644 --- a/src/Compilers/Core/Portable/Compilation/ParseOptions.cs +++ b/src/Compilers/Core/Portable/Compilation/ParseOptions.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.ComponentModel; using System.Linq; using Roslyn.Utilities; @@ -13,11 +14,19 @@ namespace Microsoft.CodeAnalysis /// public abstract class ParseOptions { + private readonly Lazy> _lazyErrors; + /// /// Specifies whether to parse as regular code files, script files or interactive code. /// public SourceCodeKind Kind { get; protected set; } + /// + /// Gets the specified source code kind, which is the value that was specified in + /// the call to the constructor, or modified using the method. + /// + public SourceCodeKind SpecifiedKind { get; protected set; } + /// /// Gets a value indicating whether the documentation comments are parsed. /// @@ -26,8 +35,16 @@ public abstract class ParseOptions internal ParseOptions(SourceCodeKind kind, DocumentationMode documentationMode) { - this.Kind = kind; + this.SpecifiedKind = kind; + this.Kind = kind.MapSpecifiedToEffectiveKind(); this.DocumentationMode = documentationMode; + + _lazyErrors = new Lazy>(() => + { + var builder = ArrayBuilder.GetInstance(); + ValidateOptions(builder); + return builder.ToImmutableAndFree(); + }); } /// @@ -35,6 +52,14 @@ internal ParseOptions(SourceCodeKind kind, DocumentationMode documentationMode) /// public abstract string Language { get; } + /// + /// Errors collection related to an incompatible set of parse options + /// + public ImmutableArray Errors + { + get { return _lazyErrors.Value; } + } + /// /// Creates a new options instance with the specified source code kind. /// @@ -43,6 +68,25 @@ public ParseOptions WithKind(SourceCodeKind kind) return CommonWithKind(kind); } + /// + /// Performs validation of options compatibilities and generates diagnostics if needed + /// + internal abstract void ValidateOptions(ArrayBuilder builder); + + internal void ValidateOptions(ArrayBuilder builder, CommonMessageProvider messageProvider) + { + // Validate SpecifiedKind not Kind, to catch deprecated specified kinds: + if (!SpecifiedKind.IsValid()) + { + builder.Add(messageProvider.CreateDiagnostic(messageProvider.ERR_BadSourceCodeKind, Location.None, SpecifiedKind.ToString())); + } + + if (!DocumentationMode.IsValid()) + { + builder.Add(messageProvider.CreateDiagnostic(messageProvider.ERR_BadDocumentationMode, Location.None, DocumentationMode.ToString())); + } + } + // It was supposed to be a protected implementation detail. // The "pattern" we have for these is the public With* method is the only public callable one, // and that forwards to the protected Common* like all the other methods in the class. @@ -92,7 +136,7 @@ protected bool EqualsHelper(ParseOptions other) } return - this.Kind == other.Kind && + this.SpecifiedKind == other.SpecifiedKind && this.DocumentationMode == other.DocumentationMode && this.Features.SequenceEqual(other.Features) && (this.PreprocessorSymbolNames == null ? other.PreprocessorSymbolNames == null : this.PreprocessorSymbolNames.SequenceEqual(other.PreprocessorSymbolNames, StringComparer.Ordinal)); @@ -103,7 +147,7 @@ protected bool EqualsHelper(ParseOptions other) protected int GetHashCodeHelper() { return - Hash.Combine((int)this.Kind, + Hash.Combine((int)this.SpecifiedKind, Hash.Combine((int)this.DocumentationMode, Hash.Combine(HashFeatures(this.Features), Hash.Combine(Hash.CombineValues(this.PreprocessorSymbolNames, StringComparer.Ordinal), 0)))); diff --git a/src/Compilers/Core/Portable/Diagnostic/CommonMessageProvider.cs b/src/Compilers/Core/Portable/Diagnostic/CommonMessageProvider.cs index 8d729577a9634..94182e4719dc8 100644 --- a/src/Compilers/Core/Portable/Diagnostic/CommonMessageProvider.cs +++ b/src/Compilers/Core/Portable/Diagnostic/CommonMessageProvider.cs @@ -159,6 +159,10 @@ public DiagnosticInfo FilterDiagnosticInfo(DiagnosticInfo diagnosticInfo, Compil public abstract int ERR_CantReadRulesetFile { get; } public abstract int ERR_CompileCancelled { get; } + // parse options: + public abstract int ERR_BadSourceCodeKind { get; } + public abstract int ERR_BadDocumentationMode { get; } + // compilation options: public abstract int ERR_BadCompilationOptionValue { get; } public abstract int ERR_MutuallyExclusiveOptions { get; } diff --git a/src/Compilers/Core/Portable/Diagnostic/Diagnostic.cs b/src/Compilers/Core/Portable/Diagnostic/Diagnostic.cs index 63c07987f9a5e..16c5e418ab109 100644 --- a/src/Compilers/Core/Portable/Diagnostic/Diagnostic.cs +++ b/src/Compilers/Core/Portable/Diagnostic/Diagnostic.cs @@ -507,4 +507,13 @@ internal virtual bool IsNotConfigurable() return AnalyzerManager.HasNotConfigurableTag(this.CustomTags); } } + + /// + /// This type is attached to diagnostics for required language version and should only be used + /// on such diagnostics, as they are recognized by . + /// + internal abstract class RequiredLanguageVersion : IMessageSerializable + { + public abstract override string ToString(); + } } diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 04a4963d5f804..319ae4115b891 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -188,6 +188,8 @@ Microsoft.CodeAnalysis.OperationKind.VariableDeclarationStatement = 3 -> Microso Microsoft.CodeAnalysis.OperationKind.WithStatement = 82 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.OperationKind.YieldBreakStatement = 12 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.OperationKind.YieldReturnStatement = 16 -> Microsoft.CodeAnalysis.OperationKind +Microsoft.CodeAnalysis.ParseOptions.Errors.get -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.ParseOptions.SpecifiedKind.get -> Microsoft.CodeAnalysis.SourceCodeKind Microsoft.CodeAnalysis.PortableExecutableReference.GetMetadataId() -> Microsoft.CodeAnalysis.MetadataId Microsoft.CodeAnalysis.SemanticModel.GetOperation(Microsoft.CodeAnalysis.SyntaxNode node, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.IOperation Microsoft.CodeAnalysis.Semantics.ArgumentKind @@ -813,6 +815,7 @@ override Microsoft.CodeAnalysis.Semantics.OperationWalker.VisitWithStatement(Mic override Microsoft.CodeAnalysis.Semantics.OperationWalker.VisitYieldBreakStatement(Microsoft.CodeAnalysis.Semantics.IReturnStatement operation) -> void override Microsoft.CodeAnalysis.SyntaxNode.ToString() -> string static Microsoft.CodeAnalysis.CaseInsensitiveComparison.StartsWith(string value, string possibleStart) -> bool +static Microsoft.CodeAnalysis.Compilation.GetRequiredLanguageVersion(Microsoft.CodeAnalysis.Diagnostic diagnostic) -> string static Microsoft.CodeAnalysis.EmbeddedText.FromBytes(string filePath, System.ArraySegment bytes, Microsoft.CodeAnalysis.Text.SourceHashAlgorithm checksumAlgorithm = Microsoft.CodeAnalysis.Text.SourceHashAlgorithm.Sha1) -> Microsoft.CodeAnalysis.EmbeddedText static Microsoft.CodeAnalysis.EmbeddedText.FromSource(string filePath, Microsoft.CodeAnalysis.Text.SourceText text) -> Microsoft.CodeAnalysis.EmbeddedText static Microsoft.CodeAnalysis.EmbeddedText.FromStream(string filePath, System.IO.Stream stream, Microsoft.CodeAnalysis.Text.SourceHashAlgorithm checksumAlgorithm = Microsoft.CodeAnalysis.Text.SourceHashAlgorithm.Sha1) -> Microsoft.CodeAnalysis.EmbeddedText diff --git a/src/Compilers/Core/Portable/SourceCodeKind.cs b/src/Compilers/Core/Portable/SourceCodeKind.cs index ea36e0ce340c0..a32e5aff28195 100644 --- a/src/Compilers/Core/Portable/SourceCodeKind.cs +++ b/src/Compilers/Core/Portable/SourceCodeKind.cs @@ -32,6 +32,22 @@ public enum SourceCodeKind internal static partial class SourceCodeKindExtensions { + internal static SourceCodeKind MapSpecifiedToEffectiveKind(this SourceCodeKind kind) + { + switch (kind) + { + case SourceCodeKind.Script: +#pragma warning disable CS0618 // SourceCodeKind.Interactive is obsolete + case SourceCodeKind.Interactive: +#pragma warning restore CS0618 // SourceCodeKind.Interactive is obsolete + return SourceCodeKind.Script; + + case SourceCodeKind.Regular: + default: + return SourceCodeKind.Regular; + } + } + internal static bool IsValid(this SourceCodeKind value) { return value >= SourceCodeKind.Regular && value <= SourceCodeKind.Script; diff --git a/src/Compilers/Core/Portable/Syntax/SyntaxNodeExtensions.cs b/src/Compilers/Core/Portable/Syntax/SyntaxNodeExtensions.cs index 6c99fab9767ef..423b6ef022b7a 100644 --- a/src/Compilers/Core/Portable/Syntax/SyntaxNodeExtensions.cs +++ b/src/Compilers/Core/Portable/Syntax/SyntaxNodeExtensions.cs @@ -409,9 +409,7 @@ public static TSyntax WithTrailingTrivia( /// /// Creates a new node from this node with the trailing trivia removed. /// - public static TSyntax WithoutTrailingTrivia( - this TSyntax node - ) where TSyntax : SyntaxNode + public static TSyntax WithoutTrailingTrivia(this TSyntax node) where TSyntax : SyntaxNode { return node.WithTrailingTrivia((IEnumerable)null); } diff --git a/src/Compilers/Test/Utilities/CSharp/SemanticModelTestBase.cs b/src/Compilers/Test/Utilities/CSharp/SemanticModelTestBase.cs index 7994fd0ecdf71..d9c8e73587e5f 100644 --- a/src/Compilers/Test/Utilities/CSharp/SemanticModelTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/SemanticModelTestBase.cs @@ -6,9 +6,8 @@ using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; -using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Test.Utilities; using Xunit; -using Roslyn.Test.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UnitTests { @@ -220,5 +219,41 @@ protected CompilationUtils.SemanticInfoSummary GetSemanticInfoForTest(string tes { return GetSemanticInfoForTest(testSrc); } + + protected string GetOperationTreeForTest(CSharpCompilation compilation) + where TSyntaxNode: SyntaxNode + { + var tree = compilation.SyntaxTrees[0]; + var model = compilation.GetSemanticModel(tree); + SyntaxNode syntaxNode = GetSyntaxNodeOfTypeForBinding(GetSyntaxNodeList(tree)); + if (syntaxNode == null) + { + return null; + } + + var operation = model.GetOperationInternal(syntaxNode); + return operation != null ? OperationTreeVerifier.GetOperationTree(operation) : null; + } + + protected string GetOperationTreeForTest(string testSrc, string expectedOperationTree, CSharpParseOptions parseOptions = null) + where TSyntaxNode : SyntaxNode + { + var compilation = CreateCompilationWithMscorlib(testSrc, new[] { SystemCoreRef }, parseOptions: parseOptions); + return GetOperationTreeForTest(compilation); + } + + protected void VerifyOperationTreeForTest(CSharpCompilation compilation, string expectedOperationTree) + where TSyntaxNode : SyntaxNode + { + var actualOperationTree = GetOperationTreeForTest(compilation); + OperationTreeVerifier.Verify(expectedOperationTree, actualOperationTree); + } + + protected void VerifyOperationTreeForTest(string testSrc, string expectedOperationTree, CSharpParseOptions parseOptions = null) + where TSyntaxNode : SyntaxNode + { + var actualOperationTree = GetOperationTreeForTest(testSrc, expectedOperationTree, parseOptions); + OperationTreeVerifier.Verify(expectedOperationTree, actualOperationTree); + } } } diff --git a/src/Compilers/Test/Utilities/VisualBasic/CompilationTestUtils.vb b/src/Compilers/Test/Utilities/VisualBasic/CompilationTestUtils.vb index 355601740cd89..5bc2250edcab2 100644 --- a/src/Compilers/Test/Utilities/VisualBasic/CompilationTestUtils.vb +++ b/src/Compilers/Test/Utilities/VisualBasic/CompilationTestUtils.vb @@ -100,6 +100,15 @@ Friend Module CompilationUtils Return VisualBasicCompilation.Create(If(assemblyName, GetUniqueName()), sourceTrees, If(references Is Nothing, additionalRefs, additionalRefs.Concat(references)), options) End Function + Public Function CreateCompilationWithMscorlib45(source As String, + Optional references As IEnumerable(Of MetadataReference) = Nothing, + Optional options As VisualBasicCompilationOptions = Nothing, + Optional assemblyName As String = Nothing, + Optional parseOptions As VisualBasicParseOptions = Nothing) As VisualBasicCompilation + Dim additionalRefs = {MscorlibRef_v4_0_30316_17626} + Return VisualBasicCompilation.Create(If(assemblyName, GetUniqueName()), {Parse(source, parseOptions)}, If(references Is Nothing, additionalRefs, additionalRefs.Concat(references)), options) + End Function + Public Function CreateCompilationWithMscorlib45AndVBRuntime(sourceTrees As IEnumerable(Of SyntaxTree), Optional references As IEnumerable(Of MetadataReference) = Nothing, Optional options As VisualBasicCompilationOptions = Nothing) As VisualBasicCompilation @@ -180,6 +189,14 @@ Friend Module CompilationUtils Return CreateCompilationWithReferences(sources, references, options, parseOptions:=parseOptions) End Function + Public Function CreateCompilationWithMscorlibAndVBRuntime(trees As IEnumerable(Of SyntaxTree), + options As VisualBasicCompilationOptions, + Optional assemblyName As String = Nothing) As VisualBasicCompilation + + Dim references = {MscorlibRef, SystemRef, MsvbRef} + Return CreateCompilation(trees, references, options, assemblyName) + End Function + Public Function CreateCompilationWithMscorlibAndVBRuntime(sources As XElement, options As VisualBasicCompilationOptions) As VisualBasicCompilation Return CreateCompilationWithMscorlibAndVBRuntime(sources, Nothing, options, parseOptions:=If(options Is Nothing, Nothing, options.ParseOptions)) diff --git a/src/Compilers/Test/Utilities/VisualBasic/SemanticModelTestBase.vb b/src/Compilers/Test/Utilities/VisualBasic/SemanticModelTestBase.vb index 7df729311b49b..f3ec8a71cb2b7 100644 --- a/src/Compilers/Test/Utilities/VisualBasic/SemanticModelTestBase.vb +++ b/src/Compilers/Test/Utilities/VisualBasic/SemanticModelTestBase.vb @@ -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. +Imports Microsoft.CodeAnalysis.Test.Utilities Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -151,4 +152,32 @@ Public MustInherit Class SemanticModelTestBase : Inherits BasicTestBase ).Where(Function(s) anyArity OrElse DirectCast(s, Symbol).GetArity() = arity.Value).ToList() End Function + Friend Function GetOperationTreeForTest(Of TSyntaxNode As SyntaxNode)(compilation As VisualBasicCompilation, fileName As String, Optional which As Integer = 0) As String + Dim node As SyntaxNode = CompilationUtils.FindBindingText(Of TSyntaxNode)(compilation, fileName, which) + If node Is Nothing Then + Return Nothing + End If + + Dim tree = (From t In compilation.SyntaxTrees Where t.FilePath = fileName).Single() + Dim semanticModel = compilation.GetSemanticModel(tree) + Dim operation = semanticModel.GetOperationInternal(node) + Return If(operation IsNot Nothing, OperationTreeVerifier.GetOperationTree(operation), Nothing) + End Function + + Friend Function GetOperationTreeForTest(Of TSyntaxNode As SyntaxNode)(testSrc As String, Optional parseOptions As VisualBasicParseOptions = Nothing, Optional which As Integer = 0) As String + Dim fileName = "a.vb" + Dim syntaxTree = Parse(testSrc, fileName, parseOptions) + Dim compilation = CreateCompilationWithMscorlib(syntaxTree) + Return GetOperationTreeForTest(Of TSyntaxNode)(compilation, fileName, which) + End Function + + Friend Sub VerifyOperationTreeForTest(Of TSyntaxNode As SyntaxNode)(compilation As VisualBasicCompilation, fileName As String, expectedOperationTree As String, Optional which As Integer = 0) + Dim actualOperationTree = GetOperationTreeForTest(Of TSyntaxNode)(compilation, fileName, which) + OperationTreeVerifier.Verify(expectedOperationTree, actualOperationTree) + End Sub + + Friend Sub VerifyOperationTreeForTest(Of TSyntaxNode As SyntaxNode)(testSrc As String, expectedOperationTree As String, Optional parseOptions As VisualBasicParseOptions = Nothing, Optional which As Integer = 0) + Dim actualOperationTree = GetOperationTreeForTest(Of TSyntaxNode)(testSrc, parseOptions, which) + OperationTreeVerifier.Verify(expectedOperationTree, actualOperationTree) + End Sub End Class diff --git a/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb b/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb index 0ff31b101c10c..4e2c3feffe17a 100644 --- a/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb +++ b/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb @@ -807,26 +807,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic If String.IsNullOrEmpty(value) Then AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, "langversion", ":") Else - Select Case value.ToLowerInvariant() - Case "9", "9.0" - languageVersion = LanguageVersion.VisualBasic9 - Case "10", "10.0" - languageVersion = LanguageVersion.VisualBasic10 - Case "11", "11.0" - languageVersion = LanguageVersion.VisualBasic11 - Case "12", "12.0" - languageVersion = LanguageVersion.VisualBasic12 - Case "14", "14.0" - languageVersion = LanguageVersion.VisualBasic14 - Case "15", "15.0" - languageVersion = LanguageVersion.VisualBasic15 - Case "default" - languageVersion = LanguageVersion.Default - Case "latest" - languageVersion = LanguageVersion.Latest - Case Else - AddDiagnostic(diagnostics, ERRID.ERR_InvalidSwitchValue, "langversion", value) - End Select + If Not value.TryParse(languageVersion) Then + AddDiagnostic(diagnostics, ERRID.ERR_InvalidSwitchValue, "langversion", value) + End If End If Continue For @@ -1307,12 +1290,10 @@ lVbRuntimePlus: Dim parseOptions = New VisualBasicParseOptions( languageVersion:=languageVersion, documentationMode:=If(parseDocumentationComments, DocumentationMode.Diagnose, DocumentationMode.None), - kind:=SourceCodeKind.Regular, + kind:=If(IsScriptRunner, SourceCodeKind.Script, SourceCodeKind.Regular), preprocessorSymbols:=AddPredefinedPreprocessorSymbols(outputKind, defines.AsImmutableOrEmpty()), features:=parsedFeatures) - Dim scriptParseOptions = parseOptions.WithKind(SourceCodeKind.Script) - ' We want to report diagnostics with source suppression in the error log file. ' However, these diagnostics won't be reported on the command line. Dim reportSuppressedDiagnostics = errorLogPath IsNot Nothing @@ -1355,7 +1336,7 @@ lVbRuntimePlus: runtimeMetadataVersion:=Nothing, instrumentationKinds:=instrumentationKinds.ToImmutableAndFree()) - ' add option incompatibility errors if any + ' add option incompatibility errors if any (parse options will be included in options.Errors) diagnostics.AddRange(options.Errors) If documentationPath Is GenerateFileNameForDocComment Then @@ -1398,7 +1379,7 @@ lVbRuntimePlus: .DisplayVersion = displayVersion, .ManifestResources = managedResources.AsImmutable(), .CompilationOptions = options, - .ParseOptions = If(IsScriptRunner, scriptParseOptions, parseOptions), + .ParseOptions = parseOptions, .EmitOptions = emitOptions, .ScriptArguments = scriptArgs.AsImmutableOrEmpty(), .TouchedFilesPath = touchedFilesPath, @@ -1674,7 +1655,7 @@ lVbRuntimePlus: ''' ''' Invalid value provided. Private Shared Function PublicSymbolsToInternalDefines(symbols As IEnumerable(Of KeyValuePair(Of String, Object)), - parameterName As String) As ImmutableDictionary(Of String, InternalSyntax.CConst) + diagnosticBuilder As ArrayBuilder(Of Diagnostic)) As ImmutableDictionary(Of String, InternalSyntax.CConst) Dim result = ImmutableDictionary.CreateBuilder(Of String, InternalSyntax.CConst)(CaseInsensitiveComparison.Comparer) @@ -1683,7 +1664,7 @@ lVbRuntimePlus: Dim constant = InternalSyntax.CConst.TryCreate(symbol.Value) If constant Is Nothing Then - Throw New ArgumentException(String.Format(ErrorFactory.IdToString(ERRID.IDS_InvalidPreprocessorConstantType, Culture), symbol.Key, symbol.Value.GetType()), parameterName) + diagnosticBuilder.Add(Diagnostic.Create(VisualBasic.MessageProvider.Instance, ERRID.ERR_InvalidPreprocessorConstantType, symbol.Key, symbol.Value.GetType())) End If result(symbol.Key) = constant @@ -1727,7 +1708,7 @@ lVbRuntimePlus: Dim diagnosticBuilder = ArrayBuilder(Of Diagnostic).GetInstance() Dim parsedTokensAsString As New StringBuilder - Dim defines As ImmutableDictionary(Of String, InternalSyntax.CConst) = PublicSymbolsToInternalDefines(symbols, "symbols") + Dim defines As ImmutableDictionary(Of String, InternalSyntax.CConst) = PublicSymbolsToInternalDefines(symbols, diagnosticBuilder) ' remove quotes around the whole /define argument (incl. nested) Dim unquotedString As String @@ -1769,7 +1750,7 @@ lVbRuntimePlus: diagnosticBuilder.Add( New DiagnosticWithInfo( - ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1, + ErrorFactory.ErrorInfo(ERRID.ERR_ConditionalCompilationConstantNotValid, ErrorFactory.ErrorInfo(ERRID.ERR_ExpectedEOS), parsedTokensAsString.ToString), Location.None)) @@ -1793,7 +1774,7 @@ lVbRuntimePlus: diagnosticBuilder.Add( New DiagnosticWithInfo( - ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1, + ErrorFactory.ErrorInfo(ERRID.ERR_ConditionalCompilationConstantNotValid, ErrorFactory.ErrorInfo(ERRID.ERR_ExpectedIdentifier), parsedTokensAsString.ToString), Location.None)) @@ -1822,7 +1803,7 @@ lVbRuntimePlus: diagnosticBuilder.Add( New DiagnosticWithInfo( - ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1, + ErrorFactory.ErrorInfo(ERRID.ERR_ConditionalCompilationConstantNotValid, ErrorFactory.ErrorInfo(ERRID.ERR_ExpectedIdentifier), parsedTokensAsString.ToString), Location.None)) @@ -1839,7 +1820,7 @@ lVbRuntimePlus: diagnosticBuilder.Add( New DiagnosticWithInfo( - ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1, + ErrorFactory.ErrorInfo(ERRID.ERR_ConditionalCompilationConstantNotValid, ErrorFactory.ErrorInfo(ERRID.ERR_ExpectedIdentifier), parsedTokensAsString.ToString), Location.None)) @@ -1894,7 +1875,7 @@ lVbRuntimePlus: Dim errorSkipped As Boolean = False For Each diag In expression.VbGreen.GetSyntaxErrors If diag.Code <> ERRID.ERR_ExpectedExpression AndAlso diag.Code <> ERRID.ERR_BadCCExpression Then - diagnosticBuilder.Add(New DiagnosticWithInfo(ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1, diag, parsedTokensAsString.ToString), Location.None)) + diagnosticBuilder.Add(New DiagnosticWithInfo(ErrorFactory.ErrorInfo(ERRID.ERR_ConditionalCompilationConstantNotValid, diag, parsedTokensAsString.ToString), Location.None)) Else errorSkipped = True End If @@ -1903,7 +1884,7 @@ lVbRuntimePlus: If errorSkipped Then diagnosticBuilder.Add( New DiagnosticWithInfo( - ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1, + ErrorFactory.ErrorInfo(ERRID.ERR_ConditionalCompilationConstantNotValid, ErrorFactory.ErrorInfo(If(atTheEndOrSeparator, ERRID.ERR_ExpectedExpression, ERRID.ERR_BadCCExpression)), parsedTokensAsString.ToString), Location.None)) @@ -1924,7 +1905,7 @@ lVbRuntimePlus: diagnosticBuilder.Add( New DiagnosticWithInfo( - ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1, + ErrorFactory.ErrorInfo(ERRID.ERR_ConditionalCompilationConstantNotValid, ErrorFactory.ErrorInfo(err, value.ErrorArgs), parsedTokensAsString.ToString), Location.None)) @@ -1952,7 +1933,7 @@ lVbRuntimePlus: diagnosticBuilder.Add( New DiagnosticWithInfo( - ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1, + ErrorFactory.ErrorInfo(ERRID.ERR_ConditionalCompilationConstantNotValid, ErrorFactory.ErrorInfo(ERRID.ERR_IllegalChar), parsedTokensAsString.ToString), Location.None)) @@ -1962,7 +1943,7 @@ lVbRuntimePlus: diagnosticBuilder.Add( New DiagnosticWithInfo( - ErrorFactory.ErrorInfo(ERRID.ERR_ProjectCCError1, + ErrorFactory.ErrorInfo(ERRID.ERR_ConditionalCompilationConstantNotValid, ErrorFactory.ErrorInfo(ERRID.ERR_ExpectedEOS), parsedTokensAsString.ToString), Location.None)) diff --git a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb index 499cd9b6671f3..40154db79f08f 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb @@ -1929,6 +1929,21 @@ Namespace Microsoft.CodeAnalysis.VisualBasic builder.AddRange(tree.GetDiagnostics(cancellationToken)) Next End If + + Dim parseOptionsReported = New HashSet(Of ParseOptions) + If Options.ParseOptions IsNot Nothing Then + parseOptionsReported.Add(Options.ParseOptions) ' This is reported in Options.Errors at CompilationStage.Declare + End If + + For Each tree In SyntaxTrees + cancellationToken.ThrowIfCancellationRequested() + If Not tree.Options.Errors.IsDefaultOrEmpty AndAlso parseOptionsReported.Add(tree.Options) Then + Dim location = tree.GetLocation(TextSpan.FromBounds(0, 0)) + For Each err In tree.Options.Errors + builder.Add(err.WithLocation(location)) + Next + End If + Next End If ' Add declaration errors diff --git a/src/Compilers/VisualBasic/Portable/Errors/Errors.vb b/src/Compilers/VisualBasic/Portable/Errors/Errors.vb index 1d21dcb6380ec..ac5800186dd39 100644 --- a/src/Compilers/VisualBasic/Portable/Errors/Errors.vb +++ b/src/Compilers/VisualBasic/Portable/Errors/Errors.vb @@ -753,7 +753,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic 'ERR_UnableToCreateMetaDataAPI = 31024 'ERR_UnableToOpenFile1 = 31027 ERR_EventHandlerSignatureIncompatible2 = 31029 - ERR_ProjectCCError1 = 31030 + ERR_ConditionalCompilationConstantNotValid = 31030 'ERR_ProjectCCError0 = 31031 ERR_InterfaceImplementedTwice1 = 31033 ERR_InterfaceNotImplemented1 = 31035 @@ -1726,6 +1726,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ERR_Merge_conflict_marker_encountered = 37284 + ERR_BadSourceCodeKind = 37285 + ERR_BadDocumentationMode = 37286 + ERR_BadLanguageVersion = 37287 + ERR_InvalidPreprocessorConstantType = 37288 + '// WARNINGS BEGIN HERE WRN_UseOfObsoleteSymbol2 = 40000 WRN_InvalidOverrideDueToTupleNames2 = 40001 @@ -1968,7 +1973,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic IDS_LogoLine1 = 56007 IDS_LogoLine2 = 56008 IDS_VBCHelp = 56009 - IDS_InvalidPreprocessorConstantType = 56010 + ' available: 56010 IDS_ToolName = 56011 ' Feature codes diff --git a/src/Compilers/VisualBasic/Portable/Errors/MessageProvider.vb b/src/Compilers/VisualBasic/Portable/Errors/MessageProvider.vb index 8a69e2e5de128..5b8af88023e6c 100644 --- a/src/Compilers/VisualBasic/Portable/Errors/MessageProvider.vb +++ b/src/Compilers/VisualBasic/Portable/Errors/MessageProvider.vb @@ -232,6 +232,20 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property + ' parse options: + + Public Overrides ReadOnly Property ERR_BadSourceCodeKind As Integer + Get + Return ERRID.ERR_BadSourceCodeKind + End Get + End Property + + Public Overrides ReadOnly Property ERR_BadDocumentationMode As Integer + Get + Return ERRID.ERR_BadDocumentationMode + End Get + End Property + ' compilation options: Public Overrides ReadOnly Property ERR_BadCompilationOptionValue As Integer diff --git a/src/Compilers/VisualBasic/Portable/LanguageVersion.vb b/src/Compilers/VisualBasic/Portable/LanguageVersion.vb index 34d1e1b7c3ed1..8fcfc4f1848e7 100644 --- a/src/Compilers/VisualBasic/Portable/LanguageVersion.vb +++ b/src/Compilers/VisualBasic/Portable/LanguageVersion.vb @@ -56,9 +56,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Select End Function + End Module + + Public Module LanguageVersionFacts + ''' + ''' Map a language version (such as Default, Latest, Or VisualBasicN) to a specific version (VisualBasicN). + ''' - Friend Function MapSpecifiedToEffectiveVersion(version As LanguageVersion) As LanguageVersion + Public Function MapSpecifiedToEffectiveVersion(version As LanguageVersion) As LanguageVersion Select Case version Case LanguageVersion.Latest, LanguageVersion.Default Return LanguageVersion.VisualBasic15 @@ -67,5 +73,81 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Select End Function + ''' + ''' Displays the version number in the format understood on the command-line (/langver flag). + ''' For instance, "9", "15", "latest". + ''' + + Public Function ToDisplayString(version As LanguageVersion) As String + Select Case version + Case LanguageVersion.VisualBasic9 + Return "9" + Case LanguageVersion.VisualBasic10 + Return "10" + Case LanguageVersion.VisualBasic11 + Return "11" + Case LanguageVersion.VisualBasic12 + Return "12" + Case LanguageVersion.VisualBasic14 + Return "14" + Case LanguageVersion.VisualBasic15 + Return "15" + Case LanguageVersion.Default + Return "default" + Case LanguageVersion.Latest + Return "latest" + Case Else + Throw ExceptionUtilities.UnexpectedValue(version) + End Select + End Function + + ''' + ''' Parse a LanguageVersion from a string input, as the command-line compiler does. + ''' + + Public Function TryParse(version As String, ByRef result As LanguageVersion) As Boolean + If version Is Nothing Then + result = LanguageVersion.Default + Return False + End If + + Select Case version.ToLowerInvariant() + Case "9", "9.0" + result = LanguageVersion.VisualBasic9 + Case "10", "10.0" + result = LanguageVersion.VisualBasic10 + Case "11", "11.0" + result = LanguageVersion.VisualBasic11 + Case "12", "12.0" + result = LanguageVersion.VisualBasic12 + Case "14", "14.0" + result = LanguageVersion.VisualBasic14 + Case "15", "15.0" + result = LanguageVersion.VisualBasic15 + Case "default" + result = LanguageVersion.Default + Case "latest" + result = LanguageVersion.Latest + Case Else + result = LanguageVersion.Default + Return False + End Select + Return True + End Function + End Module + + Friend Class VisualBasicRequiredLanguageVersion + Inherits RequiredLanguageVersion + + Friend ReadOnly Property Version As LanguageVersion + + Friend Sub New(version As LanguageVersion) + Me.Version = version + End Sub + + Public Overrides Function ToString() As String + Return Version.ToDisplayString() + End Function + End Class End Namespace diff --git a/src/Compilers/VisualBasic/Portable/Parser/Parser.vb b/src/Compilers/VisualBasic/Portable/Parser/Parser.vb index 16f8c1dfdcd55..64f17ec899001 100644 --- a/src/Compilers/VisualBasic/Portable/Parser/Parser.vb +++ b/src/Compilers/VisualBasic/Portable/Parser/Parser.vb @@ -6171,7 +6171,8 @@ checkNullable: If feature = Feature.InterpolatedStrings Then ' Bug: It is too late in the release cycle to update localized strings. As a short term measure we will output ' an unlocalized string and fix this to be localized in the next release. - Return ReportSyntaxError(node, ERRID.ERR_LanguageVersion, languageVersion.GetErrorName(), "interpolated strings") + Dim requiredVersion = New VisualBasicRequiredLanguageVersion(feature.GetLanguageVersion()) + Return ReportSyntaxError(node, ERRID.ERR_LanguageVersion, languageVersion.GetErrorName(), "interpolated strings", requiredVersion) Else Return ReportFeatureUnavailable(feature, node, languageVersion) End If @@ -6179,7 +6180,8 @@ checkNullable: Private Shared Function ReportFeatureUnavailable(Of TNode As VisualBasicSyntaxNode)(feature As Feature, node As TNode, languageVersion As LanguageVersion) As TNode Dim featureName = ErrorFactory.ErrorInfo(feature.GetResourceId()) - Return ReportSyntaxError(node, ERRID.ERR_LanguageVersion, languageVersion.GetErrorName(), featureName) + Dim requiredVersion = New VisualBasicRequiredLanguageVersion(feature.GetLanguageVersion()) + Return ReportSyntaxError(node, ERRID.ERR_LanguageVersion, languageVersion.GetErrorName(), featureName, requiredVersion) End Function Friend Function ReportFeatureUnavailable(Of TNode As VisualBasicSyntaxNode)(feature As Feature, node As TNode) As TNode @@ -6201,7 +6203,8 @@ checkNullable: Friend Shared Function CheckFeatureAvailability(diagnostics As DiagnosticBag, location As Location, languageVersion As LanguageVersion, feature As Feature) As Boolean If Not CheckFeatureAvailability(languageVersion, feature) Then Dim featureName = ErrorFactory.ErrorInfo(feature.GetResourceId()) - diagnostics.Add(ERRID.ERR_LanguageVersion, location, languageVersion.GetErrorName(), featureName) + Dim requiredVersion = New VisualBasicRequiredLanguageVersion(feature.GetLanguageVersion()) + diagnostics.Add(ERRID.ERR_LanguageVersion, location, languageVersion.GetErrorName(), featureName, requiredVersion) Return False End If Return True diff --git a/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt b/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt index 9a65b044b9274..bbcc5881382a8 100644 --- a/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt @@ -6,6 +6,10 @@ Microsoft.CodeAnalysis.VisualBasic.LanguageVersion.Default = 0 -> Microsoft.CodeAnalysis.VisualBasic.LanguageVersion Microsoft.CodeAnalysis.VisualBasic.LanguageVersion.Latest = 2147483647 -> Microsoft.CodeAnalysis.VisualBasic.LanguageVersion Microsoft.CodeAnalysis.VisualBasic.LanguageVersion.VisualBasic15 = 15 -> Microsoft.CodeAnalysis.VisualBasic.LanguageVersion +Microsoft.CodeAnalysis.VisualBasic.LanguageVersionFacts +Microsoft.CodeAnalysis.VisualBasic.LanguageVersionFacts.MapSpecifiedToEffectiveVersion(version As Microsoft.CodeAnalysis.VisualBasic.LanguageVersion) -> Microsoft.CodeAnalysis.VisualBasic.LanguageVersion +Microsoft.CodeAnalysis.VisualBasic.LanguageVersionFacts.ToDisplayString(version As Microsoft.CodeAnalysis.VisualBasic.LanguageVersion) -> String +Microsoft.CodeAnalysis.VisualBasic.LanguageVersionFacts.TryParse(version As String, ByRef result As Microsoft.CodeAnalysis.VisualBasic.LanguageVersion) -> Boolean Microsoft.CodeAnalysis.VisualBasic.Syntax.NamedTupleElementSyntax Microsoft.CodeAnalysis.VisualBasic.Syntax.NamedTupleElementSyntax.AsClause() -> Microsoft.CodeAnalysis.VisualBasic.Syntax.SimpleAsClauseSyntax Microsoft.CodeAnalysis.VisualBasic.Syntax.NamedTupleElementSyntax.Identifier() -> Microsoft.CodeAnalysis.SyntaxToken diff --git a/src/Compilers/VisualBasic/Portable/Scanner/Scanner.vb b/src/Compilers/VisualBasic/Portable/Scanner/Scanner.vb index a12c45769da2f..3b9243bd23337 100644 --- a/src/Compilers/VisualBasic/Portable/Scanner/Scanner.vb +++ b/src/Compilers/VisualBasic/Portable/Scanner/Scanner.vb @@ -2650,7 +2650,11 @@ baddate: If CheckFeatureAvailability(feature) Then Return token End If - Dim errorInfo = ErrorFactory.ErrorInfo(ERRID.ERR_LanguageVersion, _options.LanguageVersion.GetErrorName(), ErrorFactory.ErrorInfo(feature.GetResourceId())) + Dim requiredVersion = New VisualBasicRequiredLanguageVersion(feature.GetLanguageVersion()) + Dim errorInfo = ErrorFactory.ErrorInfo(ERRID.ERR_LanguageVersion, + _options.LanguageVersion.GetErrorName(), + ErrorFactory.ErrorInfo(feature.GetResourceId()), + requiredVersion) Return DirectCast(token.AddError(errorInfo), SyntaxToken) End Function diff --git a/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb b/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb index d0a6f6a7876b2..11ad746856e2e 100644 --- a/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb +++ b/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb @@ -1035,6 +1035,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property + ''' + ''' Looks up a localized string similar to Provided documentation mode is unsupported or invalid: '{0}'.. + ''' + Friend ReadOnly Property ERR_BadDocumentationMode() As String + Get + Return ResourceManager.GetString("ERR_BadDocumentationMode", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to Enum '{0}' must contain at least one member.. ''' @@ -1278,6 +1287,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property + ''' + ''' Looks up a localized string similar to Provided language version is unsupported or invalid: '{0}'.. + ''' + Friend ReadOnly Property ERR_BadLanguageVersion() As String + Get + Return ResourceManager.GetString("ERR_BadLanguageVersion", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to '{0}' is not valid on a local constant declaration.. ''' @@ -1485,6 +1503,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property + ''' + ''' Looks up a localized string similar to Provided source code kind is unsupported or invalid: '{0}'. + ''' + Friend ReadOnly Property ERR_BadSourceCodeKind() As String + Get + Return ResourceManager.GetString("ERR_BadSourceCodeKind", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to '{0}' and '{1}' cannot be combined.. ''' @@ -2341,6 +2368,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property + ''' + ''' Looks up a localized string similar to Conditional compilation constant '{1}' is not valid: {0}. + ''' + Friend ReadOnly Property ERR_ConditionalCompilationConstantNotValid() As String + Get + Return ResourceManager.GetString("ERR_ConditionalCompilationConstantNotValid", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to Type '{0}' must define operator '{1}' to be used in a '{2}' expression.. ''' @@ -6269,6 +6305,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property + ''' + ''' Looks up a localized string similar to Preprocessor constant '{0}' of type '{1}' is not supported, only primitive types are allowed.. + ''' + Friend ReadOnly Property ERR_InvalidPreprocessorConstantType() As String + Get + Return ResourceManager.GetString("ERR_InvalidPreprocessorConstantType", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to Invalid signature public key specified in AssemblySignatureKeyAttribute.. ''' @@ -9252,15 +9297,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property - ''' - ''' Looks up a localized string similar to Project-level conditional compilation constant '{1}' is not valid: {0}. - ''' - Friend ReadOnly Property ERR_ProjectCCError1() As String - Get - Return ResourceManager.GetString("ERR_ProjectCCError1", resourceCulture) - End Get - End Property - ''' ''' Looks up a localized string similar to Property access must assign to the property or use its value.. ''' @@ -12180,15 +12216,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property - ''' - ''' Looks up a localized string similar to Preprocessor constant '{0}' of type '{1}' is not supported, only primitive types are allowed.. - ''' - Friend ReadOnly Property IDS_InvalidPreprocessorConstantType() As String - Get - Return ResourceManager.GetString("IDS_InvalidPreprocessorConstantType", resourceCulture) - End Get - End Property - ''' ''' Looks up a localized string similar to {0} version {1}. ''' diff --git a/src/Compilers/VisualBasic/Portable/VBResources.resx b/src/Compilers/VisualBasic/Portable/VBResources.resx index 766722e4338af..ba8f8bb0fd788 100644 --- a/src/Compilers/VisualBasic/Portable/VBResources.resx +++ b/src/Compilers/VisualBasic/Portable/VBResources.resx @@ -1857,8 +1857,8 @@ Method '{0}' cannot handle event '{1}' because they do not have a compatible signature. - - Project-level conditional compilation constant '{1}' is not valid: {0} + + Conditional compilation constant '{1}' is not valid: {0} Interface '{0}' can be implemented only once by this type. @@ -5263,7 +5263,7 @@ '{0}' is not a valid format specifier - + Preprocessor constant '{0}' of type '{1}' is not supported, only primitive types are allowed. @@ -5467,4 +5467,13 @@ Merge conflict marker encountered + + Provided documentation mode is unsupported or invalid: '{0}'. + + + Provided language version is unsupported or invalid: '{0}'. + + + Provided source code kind is unsupported or invalid: '{0}' + \ No newline at end of file diff --git a/src/Compilers/VisualBasic/Portable/VisualBasicCompilationOptions.vb b/src/Compilers/VisualBasic/Portable/VisualBasicCompilationOptions.vb index 3d58fef3cfd39..0d2f70a5e0b80 100644 --- a/src/Compilers/VisualBasic/Portable/VisualBasicCompilationOptions.vb +++ b/src/Compilers/VisualBasic/Portable/VisualBasicCompilationOptions.vb @@ -913,6 +913,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Friend Overrides Sub ValidateOptions(builder As ArrayBuilder(Of Diagnostic)) ValidateOptions(builder, MessageProvider.Instance) + If ParseOptions IsNot Nothing Then + builder.AddRange(ParseOptions.Errors) + End If + If Me.EmbedVbCoreRuntime AndAlso Me.OutputKind.IsNetModule() Then builder.Add(Diagnostic.Create(MessageProvider.Instance, ERRID.ERR_VBCoreNetModuleConflict)) End If diff --git a/src/Compilers/VisualBasic/Portable/VisualBasicParseOptions.vb b/src/Compilers/VisualBasic/Portable/VisualBasicParseOptions.vb index 4549f790d7f98..8ab58bc088815 100644 --- a/src/Compilers/VisualBasic/Portable/VisualBasicParseOptions.vb +++ b/src/Compilers/VisualBasic/Portable/VisualBasicParseOptions.vb @@ -40,18 +40,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic kind, If(preprocessorSymbols Is Nothing, DefaultPreprocessorSymbols, ImmutableArray.CreateRange(preprocessorSymbols)), ImmutableDictionary(Of String, String).Empty) - - ' We test the mapped value, _languageVersion, rather than the parameter, languageVersion, - ' which has not had "Latest" mapped to the latest version yet. - If Not _languageVersion.IsValid Then - Throw New ArgumentOutOfRangeException(NameOf(languageVersion)) - End If - - If Not kind.IsValid Then - Throw New ArgumentOutOfRangeException(NameOf(kind)) - End If - - ValidatePreprocessorSymbols(preprocessorSymbols, NameOf(preprocessorSymbols)) End Sub Friend Sub New( @@ -61,66 +49,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic preprocessorSymbols As IEnumerable(Of KeyValuePair(Of String, Object)), features As ImmutableDictionary(Of String, String)) - MyClass.New(languageVersion, - documentationMode, - kind, - If(preprocessorSymbols Is Nothing, DefaultPreprocessorSymbols, ImmutableArray.CreateRange(preprocessorSymbols)), - features) - - ' We test the mapped value, _languageVersion, rather than the parameter, languageVersion, - ' which has not had "Latest" mapped to the latest version yet. - If Not _languageVersion.IsValid Then - Throw New ArgumentOutOfRangeException(NameOf(languageVersion)) - End If - - If Not kind.IsValid Then - Throw New ArgumentOutOfRangeException(NameOf(kind)) - End If - - ValidatePreprocessorSymbols(preprocessorSymbols, NameOf(preprocessorSymbols)) - - If features Is Nothing Then - Throw New ArgumentException(NameOf(features)) - End If - End Sub - - Private Shared Sub ValidatePreprocessorSymbols(preprocessorSymbols As IEnumerable(Of KeyValuePair(Of String, Object)), - parameterName As String) - If preprocessorSymbols Is Nothing Then - Return - End If - - For Each symbol In preprocessorSymbols - If Not IsValidIdentifier(symbol.Key) OrElse - SyntaxFacts.GetKeywordKind(symbol.Key) <> SyntaxKind.None Then - - Throw New ArgumentException(parameterName) - End If - - Debug.Assert(SyntaxFactory.ParseTokens(symbol.Key).Select(Function(t) t.Kind).SequenceEqual({SyntaxKind.IdentifierToken, SyntaxKind.EndOfFileToken})) - - Dim constant = InternalSyntax.CConst.TryCreate(symbol.Value) - If constant Is Nothing Then - Throw New ArgumentException(String.Format(VBResources.IDS_InvalidPreprocessorConstantType, symbol.Key, symbol.Value.GetType()), parameterName) - End If - Next - End Sub - - ' Does not perform validation. - Private Sub New( - languageVersion As LanguageVersion, - documentationMode As DocumentationMode, - kind As SourceCodeKind, - preprocessorSymbols As ImmutableArray(Of KeyValuePair(Of String, Object)), - features As ImmutableDictionary(Of String, String)) - MyBase.New(kind, documentationMode) - Debug.Assert(Not preprocessorSymbols.IsDefault) _specifiedLanguageVersion = languageVersion _languageVersion = languageVersion.MapSpecifiedToEffectiveVersion - _preprocessorSymbols = preprocessorSymbols - _features = features + _preprocessorSymbols = preprocessorSymbols.ToImmutableArrayOrEmpty + _features = If(features, ImmutableDictionary(Of String, String).Empty) End Sub Private Sub New(other As VisualBasicParseOptions) @@ -200,10 +134,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End If Dim effectiveVersion = version.MapSpecifiedToEffectiveVersion() - If Not effectiveVersion.IsValid Then - Throw New ArgumentOutOfRangeException(NameOf(version)) - End If - Return New VisualBasicParseOptions(Me) With {._specifiedLanguageVersion = version, ._languageVersion = effectiveVersion} End Function @@ -213,15 +143,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ''' The parser source code kind. ''' A new instance of VisualBasicParseOptions if source code kind is different; otherwise current instance. Public Shadows Function WithKind(kind As SourceCodeKind) As VisualBasicParseOptions - If kind = Me.Kind Then + If kind = Me.SpecifiedKind Then Return Me End If - If Not kind.IsValid Then - Throw New ArgumentOutOfRangeException(NameOf(kind)) - End If - - Return New VisualBasicParseOptions(Me) With {.Kind = kind} + Dim effectiveKind = kind.MapSpecifiedToEffectiveKind + Return New VisualBasicParseOptions(Me) With {.SpecifiedKind = kind, .Kind = effectiveKind} End Function ''' @@ -234,10 +161,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return Me End If - If Not documentationMode.IsValid() Then - Throw New ArgumentOutOfRangeException(NameOf(documentationMode)) - End If - Return New VisualBasicParseOptions(Me) With {.DocumentationMode = documentationMode} End Function @@ -273,8 +196,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return Me End If - ValidatePreprocessorSymbols(symbols, NameOf(symbols)) - Return New VisualBasicParseOptions(Me) With {._preprocessorSymbols = symbols} End Function @@ -306,10 +227,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Public Shadows Function WithFeatures(features As IEnumerable(Of KeyValuePair(Of String, String))) As VisualBasicParseOptions ' there are currently no parse options for experimental features If features Is Nothing Then - Throw New ArgumentException(NameOf(features)) + Return New VisualBasicParseOptions(Me) With {._features = ImmutableDictionary(Of String, String).Empty} + Else + Return New VisualBasicParseOptions(Me) With {._features = features.ToImmutableDictionary(StringComparer.OrdinalIgnoreCase)} End If - - Return New VisualBasicParseOptions(Me) With {._features = features.ToImmutableDictionary(StringComparer.OrdinalIgnoreCase)} End Function Public Overrides ReadOnly Property Features As IReadOnlyDictionary(Of String, String) @@ -318,6 +239,30 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property + + Friend Overrides Sub ValidateOptions(builder As ArrayBuilder(Of Diagnostic)) + ValidateOptions(builder, MessageProvider.Instance) + + ' Validate LanguageVersion Not SpecifiedLanguageVersion, after Latest/Default has been converted + If Not LanguageVersion.IsValid Then + builder.Add(Diagnostic.Create(MessageProvider.Instance, ERRID.ERR_BadLanguageVersion, LanguageVersion.ToString)) + End If + + If Not PreprocessorSymbols.IsDefaultOrEmpty Then + For Each symbol In PreprocessorSymbols + If Not IsValidIdentifier(symbol.Key) OrElse SyntaxFacts.GetKeywordKind(symbol.Key) <> SyntaxKind.None Then + builder.Add(Diagnostic.Create(MessageProvider.Instance, ERRID.ERR_ConditionalCompilationConstantNotValid, VBResources.ERR_ExpectedIdentifier, symbol.Key)) + Else + Debug.Assert(SyntaxFactory.ParseTokens(symbol.Key).Select(Function(t) t.Kind).SequenceEqual({SyntaxKind.IdentifierToken, SyntaxKind.EndOfFileToken})) + End If + + If InternalSyntax.CConst.TryCreate(symbol.Value) Is Nothing Then + builder.Add(Diagnostic.Create(MessageProvider.Instance, ERRID.ERR_InvalidPreprocessorConstantType, symbol.Key, symbol.Value.GetType)) + End If + Next + End If + End Sub + ''' ''' Determines whether the current object is equal to another object of the same type. ''' diff --git a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineArgumentsTests.vb b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineArgumentsTests.vb index 2ea9b15293616..d1cb760943c31 100644 --- a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineArgumentsTests.vb +++ b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineArgumentsTests.vb @@ -45,26 +45,26 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CommandLine.UnitTests dict = VisualBasicCommandLineParser.ParseConditionalCompilationSymbols(text, errors) Assert.Equal(0, dict.Count) Assert.Equal(1, errors.Count) - errors.Verify(Diagnostic(ERRID.ERR_ProjectCCError1).WithArguments("Identifier expected.", "= ^^ ^^ ")) + errors.Verify(Diagnostic(ERRID.ERR_ConditionalCompilationConstantNotValid).WithArguments("Identifier expected.", "= ^^ ^^ ")) Dim previousSymbols As New Dictionary(Of String, Object)() From {{"Foo", 1}, {"Bar", "Foo"}} text = ",,,=,,," dict = VisualBasicCommandLineParser.ParseConditionalCompilationSymbols(text, errors, previousSymbols) Assert.Equal(2, dict.Count) Assert.Equal(1, errors.Count) - errors.Verify(Diagnostic(ERRID.ERR_ProjectCCError1).WithArguments("Identifier expected.", "= ^^ ^^ ")) + errors.Verify(Diagnostic(ERRID.ERR_ConditionalCompilationConstantNotValid).WithArguments("Identifier expected.", "= ^^ ^^ ")) text = "OnlyEqualsNoValue1=, Bar=foo" dict = VisualBasicCommandLineParser.ParseConditionalCompilationSymbols(text, errors) Assert.Equal(0, dict.Count) Assert.Equal(1, errors.Count) - errors.Verify(Diagnostic(ERRID.ERR_ProjectCCError1).WithArguments("Expression expected.", "OnlyEqualsNoValue1= ^^ ^^ ")) + errors.Verify(Diagnostic(ERRID.ERR_ConditionalCompilationConstantNotValid).WithArguments("Expression expected.", "OnlyEqualsNoValue1= ^^ ^^ ")) text = "Bar=foo, OnlyEqualsNoValue1=" dict = VisualBasicCommandLineParser.ParseConditionalCompilationSymbols(text, errors) Assert.Equal(1, dict.Count) Assert.Equal(1, errors.Count) - errors.Verify(Diagnostic(ERRID.ERR_ProjectCCError1).WithArguments("Expression expected.", "OnlyEqualsNoValue1= ^^ ^^ ")) + errors.Verify(Diagnostic(ERRID.ERR_ConditionalCompilationConstantNotValid).WithArguments("Expression expected.", "OnlyEqualsNoValue1= ^^ ^^ ")) text = """""OnlyEqualsNoValue1""""" dict = VisualBasicCommandLineParser.ParseConditionalCompilationSymbols(text, errors) @@ -81,12 +81,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CommandLine.UnitTests text = "then=bar" ' keyword :) dict = VisualBasicCommandLineParser.ParseConditionalCompilationSymbols(text, errors) Assert.Equal(1, errors.Count) - errors.Verify(Diagnostic(ERRID.ERR_ProjectCCError1).WithArguments("Identifier expected.", "then ^^ ^^ =bar")) + errors.Verify(Diagnostic(ERRID.ERR_ConditionalCompilationConstantNotValid).WithArguments("Identifier expected.", "then ^^ ^^ =bar")) text = "bar=then" ' keyword :) dict = VisualBasicCommandLineParser.ParseConditionalCompilationSymbols(text, errors) Assert.Equal(1, errors.Count) - errors.Verify(Diagnostic(ERRID.ERR_ProjectCCError1).WithArguments("Syntax error in conditional compilation expression.", "bar= ^^ ^^ then")) + errors.Verify(Diagnostic(ERRID.ERR_ConditionalCompilationConstantNotValid).WithArguments("Syntax error in conditional compilation expression.", "bar= ^^ ^^ then")) text = "FOO:BAR" dict = VisualBasicCommandLineParser.ParseConditionalCompilationSymbols(text, errors) @@ -107,12 +107,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CommandLine.UnitTests text = "FOO=23::,,:::BAR" dict = VisualBasicCommandLineParser.ParseConditionalCompilationSymbols(text, errors) Assert.Equal(1, errors.Count) - errors.Verify(Diagnostic(ERRID.ERR_ProjectCCError1).WithArguments("Identifier expected.", "FOO=23:: ^^ , ^^ ,:::")) + errors.Verify(Diagnostic(ERRID.ERR_ConditionalCompilationConstantNotValid).WithArguments("Identifier expected.", "FOO=23:: ^^ , ^^ ,:::")) text = "FOO=23,:BAR" dict = VisualBasicCommandLineParser.ParseConditionalCompilationSymbols(text, errors) Assert.Equal(1, errors.Count) - errors.Verify(Diagnostic(ERRID.ERR_ProjectCCError1).WithArguments("Identifier expected.", "FOO=23, ^^ : ^^ BAR")) + errors.Verify(Diagnostic(ERRID.ERR_ConditionalCompilationConstantNotValid).WithArguments("Identifier expected.", "FOO=23, ^^ : ^^ BAR")) text = "FOO::BAR,,BAZ" dict = VisualBasicCommandLineParser.ParseConditionalCompilationSymbols(text, errors) @@ -179,38 +179,38 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CommandLine.UnitTests text = "A=""A"",B=""B"",T=IF(1>0, A, B)+B+""C"",RRR=1+""3""" dict = VisualBasicCommandLineParser.ParseConditionalCompilationSymbols(text, errors) - errors.Verify(Diagnostic(ERRID.ERR_ProjectCCError1).WithArguments("Conversion from 'String' to 'Double' cannot occur in a constant expression.", "RRR=1+""3"" ^^ ^^ ")) + errors.Verify(Diagnostic(ERRID.ERR_ConditionalCompilationConstantNotValid).WithArguments("Conversion from 'String' to 'Double' cannot occur in a constant expression.", "RRR=1+""3"" ^^ ^^ ")) text = "A=""A"",B=""B"",T=IF(1>0, A, B)+B+""C"",X=IF(1,,,,,RRR=1" dict = VisualBasicCommandLineParser.ParseConditionalCompilationSymbols(text, errors) errors.Verify( - Diagnostic(ERRID.ERR_ProjectCCError1).WithArguments("')' expected.", "X=IF(1,,,,,RRR=1 ^^ ^^ "), - Diagnostic(ERRID.ERR_ProjectCCError1).WithArguments("'If' operator requires either two or three operands.", "X=IF(1,,,,,RRR=1 ^^ ^^ "), - Diagnostic(ERRID.ERR_ProjectCCError1).WithArguments("Expression expected.", "X=IF(1,,,,,RRR=1 ^^ ^^ ")) + Diagnostic(ERRID.ERR_ConditionalCompilationConstantNotValid).WithArguments("')' expected.", "X=IF(1,,,,,RRR=1 ^^ ^^ "), + Diagnostic(ERRID.ERR_ConditionalCompilationConstantNotValid).WithArguments("'If' operator requires either two or three operands.", "X=IF(1,,,,,RRR=1 ^^ ^^ "), + Diagnostic(ERRID.ERR_ConditionalCompilationConstantNotValid).WithArguments("Expression expected.", "X=IF(1,,,,,RRR=1 ^^ ^^ ")) text = "A=CHR(128)" dict = VisualBasicCommandLineParser.ParseConditionalCompilationSymbols(text, errors) - errors.Verify(Diagnostic(ERRID.ERR_ProjectCCError1).WithArguments("End of statement expected.", "A=CHR ^^ ^^ (128)")) + errors.Verify(Diagnostic(ERRID.ERR_ConditionalCompilationConstantNotValid).WithArguments("End of statement expected.", "A=CHR ^^ ^^ (128)")) text = "A=ASCW(""G"")" dict = VisualBasicCommandLineParser.ParseConditionalCompilationSymbols(text, errors) - errors.Verify(Diagnostic(ERRID.ERR_ProjectCCError1).WithArguments("End of statement expected.", "A=ASCW ^^ ^^ (""G"")")) + errors.Verify(Diagnostic(ERRID.ERR_ConditionalCompilationConstantNotValid).WithArguments("End of statement expected.", "A=ASCW ^^ ^^ (""G"")")) text = "A=1--1,B=1 1" dict = VisualBasicCommandLineParser.ParseConditionalCompilationSymbols(text, errors) - errors.Verify(Diagnostic(ERRID.ERR_ProjectCCError1).WithArguments("End of statement expected.", "B=1 ^^ ^^ 1")) + errors.Verify(Diagnostic(ERRID.ERR_ConditionalCompilationConstantNotValid).WithArguments("End of statement expected.", "B=1 ^^ ^^ 1")) text = "A=1--1,B=1 C=1" dict = VisualBasicCommandLineParser.ParseConditionalCompilationSymbols(text, errors) - errors.Verify(Diagnostic(ERRID.ERR_ProjectCCError1).WithArguments("End of statement expected.", "B=1 ^^ ^^ C=1")) + errors.Verify(Diagnostic(ERRID.ERR_ConditionalCompilationConstantNotValid).WithArguments("End of statement expected.", "B=1 ^^ ^^ C=1")) text = "A=111111111111111111111111" dict = VisualBasicCommandLineParser.ParseConditionalCompilationSymbols(text, errors) - errors.Verify(Diagnostic(ERRID.ERR_ProjectCCError1).WithArguments("Overflow.", "A=111111111111111111111111 ^^ ^^ ")) + errors.Verify(Diagnostic(ERRID.ERR_ConditionalCompilationConstantNotValid).WithArguments("Overflow.", "A=111111111111111111111111 ^^ ^^ ")) text = "A= 2 + " + vbCrLf + "2" dict = VisualBasicCommandLineParser.ParseConditionalCompilationSymbols(text, errors) - errors.Verify(Diagnostic(ERRID.ERR_ProjectCCError1).WithArguments("Syntax error in conditional compilation expression.", "A= 2 + " + vbCrLf + " ^^ ^^ 2")) + errors.Verify(Diagnostic(ERRID.ERR_ConditionalCompilationConstantNotValid).WithArguments("Syntax error in conditional compilation expression.", "A= 2 + " + vbCrLf + " ^^ ^^ 2")) text = "A= 2 + _" + vbCrLf + "2" dict = VisualBasicCommandLineParser.ParseConditionalCompilationSymbols(text, errors) @@ -280,7 +280,7 @@ blah Dim text = "'Blah'" Dim dict = VisualBasicCommandLineParser.ParseConditionalCompilationSymbols(text, errors) - errors.Verify(Diagnostic(ERRID.ERR_ProjectCCError1).WithArguments("Identifier expected.", " ^^ 'Blah' ^^ ")) + errors.Verify(Diagnostic(ERRID.ERR_ConditionalCompilationConstantNotValid).WithArguments("Identifier expected.", " ^^ 'Blah' ^^ ")) End Sub End Class End Namespace diff --git a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb index 63e30a52155f2..ddf22605b7bee 100644 --- a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb +++ b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb @@ -1606,6 +1606,66 @@ End Module").Path Private Const s_VBC_VER As Double = PredefinedPreprocessorSymbols.CurrentVersionNumber + + Public Sub LanguageVersionAdded_Canary() + ' When a new version is added, this test will break. This list must be checked: + ' - update the "UpgradeProject" codefixer + ' - update the IDE drop-down for selecting Language Version + ' - don't fix the canary test until you update all the tests that include it + Assert.Equal(LanguageVersion.VisualBasic15, LanguageVersion.Latest.MapSpecifiedToEffectiveVersion()) + Assert.Equal(LanguageVersion.VisualBasic15, LanguageVersion.Default.MapSpecifiedToEffectiveVersion()) + End Sub + + + Public Sub LanguageVersion_DisplayString() + AssertEx.SetEqual({"default", "9", "10", "11", "12", "14", "15", "latest"}, + System.Enum.GetValues(GetType(LanguageVersion)).Cast(Of LanguageVersion)().Select(Function(v) v.ToDisplayString())) + ' For minor versions, the format should be "x.y", such as "15.1" + End Sub + + + Public Sub LanguageVersion_MapSpecifiedToEffectiveVersion() + Assert.Equal(LanguageVersion.VisualBasic9, LanguageVersion.VisualBasic9.MapSpecifiedToEffectiveVersion()) + Assert.Equal(LanguageVersion.VisualBasic10, LanguageVersion.VisualBasic10.MapSpecifiedToEffectiveVersion()) + Assert.Equal(LanguageVersion.VisualBasic11, LanguageVersion.VisualBasic11.MapSpecifiedToEffectiveVersion()) + Assert.Equal(LanguageVersion.VisualBasic12, LanguageVersion.VisualBasic12.MapSpecifiedToEffectiveVersion()) + Assert.Equal(LanguageVersion.VisualBasic14, LanguageVersion.VisualBasic14.MapSpecifiedToEffectiveVersion()) + Assert.Equal(LanguageVersion.VisualBasic15, LanguageVersion.VisualBasic15.MapSpecifiedToEffectiveVersion()) + Assert.Equal(LanguageVersion.VisualBasic15, LanguageVersion.Default.MapSpecifiedToEffectiveVersion()) + Assert.Equal(LanguageVersion.VisualBasic15, LanguageVersion.Latest.MapSpecifiedToEffectiveVersion()) + + ' The canary check Is a reminder that this test needs to be updated when a language version Is added + LanguageVersionAdded_Canary() + End Sub + + + Public Sub LanguageVersion_TryParseDisplayString(input As String, success As Boolean, expected As LanguageVersion) + Dim version As LanguageVersion + Assert.Equal(success, input.TryParse(version)) + Assert.Equal(expected, version) + + ' The canary check is a reminder that this test needs to be updated when a language version is added + LanguageVersionAdded_Canary() + End Sub + Public Sub TestDefine() TestDefines({"/D:a=True,b=1", "a.vb"}, @@ -6025,7 +6085,7 @@ Imports System output = New StringWriter() exitCode = vbc.Run(output, Nothing) Assert.Equal(1, exitCode) - Assert.Equal("vbc : error BC31030: Project-level conditional compilation constant '_ ^^ ^^ ' is not valid: Identifier expected.", output.ToString().Trim()) + Assert.Equal("vbc : error BC31030: Conditional compilation constant '_ ^^ ^^ ' is not valid: Identifier expected.", output.ToString().Trim()) vbc = New MockVisualBasicCompiler(Nothing, _baseDirectory, {"-nologo", "/preferreduilang:en", "/t:libraRY", "/define:_a,", source}) output = New StringWriter() @@ -6037,31 +6097,31 @@ Imports System output = New StringWriter() exitCode = vbc.Run(output, Nothing) Assert.Equal(1, exitCode) - Assert.Equal("vbc : error BC31030: Project-level conditional compilation constant '_ ^^ ^^ a' is not valid: Identifier expected.", output.ToString().Trim()) + Assert.Equal("vbc : error BC31030: Conditional compilation constant '_ ^^ ^^ a' is not valid: Identifier expected.", output.ToString().Trim()) vbc = New MockVisualBasicCompiler(Nothing, _baseDirectory, {"-nologo", "/preferreduilang:en", "/t:libraRY", "/define:a,_,b", source}) output = New StringWriter() exitCode = vbc.Run(output, Nothing) Assert.Equal(1, exitCode) - Assert.Equal("vbc : error BC31030: Project-level conditional compilation constant '_ ^^ ^^ ' is not valid: Identifier expected.", output.ToString().Trim()) + Assert.Equal("vbc : error BC31030: Conditional compilation constant '_ ^^ ^^ ' is not valid: Identifier expected.", output.ToString().Trim()) vbc = New MockVisualBasicCompiler(Nothing, _baseDirectory, {"-nologo", "/preferreduilang:en", "/t:libraRY", "/define:_", source}) output = New StringWriter() exitCode = vbc.Run(output, Nothing) Assert.Equal(1, exitCode) - Assert.Equal("vbc : error BC31030: Project-level conditional compilation constant '_ ^^ ^^ ' is not valid: Identifier expected.", output.ToString().Trim()) + Assert.Equal("vbc : error BC31030: Conditional compilation constant '_ ^^ ^^ ' is not valid: Identifier expected.", output.ToString().Trim()) vbc = New MockVisualBasicCompiler(Nothing, _baseDirectory, {"-nologo", "/preferreduilang:en", "/t:libraRY", "/define:_ ", source}) output = New StringWriter() exitCode = vbc.Run(output, Nothing) Assert.Equal(1, exitCode) - Assert.Equal("vbc : error BC31030: Project-level conditional compilation constant '_ ^^ ^^ ' is not valid: Identifier expected.", output.ToString().Trim()) + Assert.Equal("vbc : error BC31030: Conditional compilation constant '_ ^^ ^^ ' is not valid: Identifier expected.", output.ToString().Trim()) vbc = New MockVisualBasicCompiler(Nothing, _baseDirectory, {"-nologo", "/preferreduilang:en", "/t:libraRY", "/define:a,_", source}) output = New StringWriter() exitCode = vbc.Run(output, Nothing) Assert.Equal(1, exitCode) - Assert.Equal("vbc : error BC31030: Project-level conditional compilation constant '_ ^^ ^^ ' is not valid: Identifier expected.", output.ToString().Trim()) + Assert.Equal("vbc : error BC31030: Conditional compilation constant '_ ^^ ^^ ' is not valid: Identifier expected.", output.ToString().Trim()) CleanupAllGeneratedFiles(source) End Sub @@ -6196,13 +6256,13 @@ End Module Dim output As New StringWriter() Dim exitCode = vbc.Run(output, Nothing) Assert.Equal(1, exitCode) - Assert.Equal("vbc : error BC31030: Project-level conditional compilation constant 'I ^^ ^^ ' is not valid: End of statement expected.", output.ToString().Trim()) + Assert.Equal("vbc : error BC31030: Conditional compilation constant 'I ^^ ^^ ' is not valid: End of statement expected.", output.ToString().Trim()) vbc = New MockVisualBasicCompiler(Nothing, _baseDirectory, {"/nologo", "/preferreduilang:en", "/define:I*", source}) output = New StringWriter() exitCode = vbc.Run(output, Nothing) Assert.Equal(1, exitCode) - Assert.Equal("vbc : error BC31030: Project-level conditional compilation constant 'I ^^ ^^ ' is not valid: End of statement expected.", output.ToString().Trim()) + Assert.Equal("vbc : error BC31030: Conditional compilation constant 'I ^^ ^^ ' is not valid: End of statement expected.", output.ToString().Trim()) End Sub @@ -8118,6 +8178,18 @@ End Module Assert.Equal($"error BC2012: can't open '{sourceLinkPath}' for writing: Fake IOException{Environment.NewLine}", outWriter.ToString()) End Sub + + Public Sub CompilingCodeWithInvalidPreProcessorSymbolsShouldProvideDiagnostics() + Dim parsedArgs = DefaultParse({"/define:1", "a.cs"}, _baseDirectory) + parsedArgs.Errors.Verify(Diagnostic(ERRID.ERR_ConditionalCompilationConstantNotValid).WithArguments("Identifier expected.", "1 ^^ ^^ ").WithLocation(1, 1)) + End Sub + + + Public Sub CompilingCodeWithInvalidLanguageVersionShouldProvideDiagnostics() + Dim parsedArgs = DefaultParse({"/langversion:1000", "a.cs"}, _baseDirectory) + parsedArgs.Errors.Verify(Diagnostic(ERRID.ERR_InvalidSwitchValue).WithArguments("langversion", "1000").WithLocation(1, 1)) + End Sub + Private Function MakeTrivialExe(Optional directory As String = Nothing) As String Return Temp.CreateFile(directory:=directory, prefix:="", extension:=".vb").WriteAllText(" Class Program diff --git a/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenTuples.vb b/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenTuples.vb index a94a53ed856d0..e88b9998af2a1 100644 --- a/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenTuples.vb +++ b/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenTuples.vb @@ -13587,6 +13587,7 @@ Imports System Public Class C Shared Sub Main() Dim x As (Integer, Integer) = (1, 1) + Else End Sub End Class @@ -13602,8 +13603,14 @@ BC36716: Visual Basic 14.0 does not support tuples. BC36716: Visual Basic 14.0 does not support tuples. Dim x As (Integer, Integer) = (1, 1) ~~~~~~ +BC30086: 'Else' must be preceded by a matching 'If' or 'ElseIf'. + Else + ~~~~ ) - + Dim x = comp.GetDiagnostics() + Assert.Equal("15", Compilation.GetRequiredLanguageVersion(comp.GetDiagnostics()(0))) + Assert.Null(Compilation.GetRequiredLanguageVersion(comp.GetDiagnostics()(2))) + Assert.Throws(Of ArgumentNullException)(Sub() Compilation.GetRequiredLanguageVersion(Nothing)) End Sub diff --git a/src/Compilers/VisualBasic/Test/Emit/PDB/PDBTests.vb b/src/Compilers/VisualBasic/Test/Emit/PDB/PDBTests.vb index 1da9cb85cdd1e..9798f188c1201 100644 --- a/src/Compilers/VisualBasic/Test/Emit/PDB/PDBTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/PDB/PDBTests.vb @@ -8,6 +8,7 @@ Imports System.Text Imports Microsoft.CodeAnalysis.Test.Utilities Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Symbols +Imports Microsoft.DiaSymReader.Tools Imports Roslyn.Test.PdbUtilities Imports Roslyn.Test.Utilities diff --git a/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/GetDiagnosticsTests.vb b/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/GetDiagnosticsTests.vb index 082ddf9a9482f..c768157c0b588 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/GetDiagnosticsTests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/GetDiagnosticsTests.vb @@ -224,6 +224,189 @@ End Namespace Assert.True(completedCompilationUnits.Contains(tree.FilePath)) End Sub + + Public Sub CompilingCodeWithInvalidPreProcessorSymbolsShouldProvideDiagnostics() + Dim dict = New Dictionary(Of String, Object) + dict.Add("1", Nothing) + Dim compilation = CreateCompilation(String.Empty, parseOptions:=New VisualBasicParseOptions().WithPreprocessorSymbols(dict)) + + CompilationUtils.AssertTheseDiagnostics(compilation, +BC31030: Conditional compilation constant '1' is not valid: Identifier expected. + +~ +) + End Sub + + + Public Sub CompilingCodeWithInvalidSourceCodeKindShouldProvideDiagnostics() +#Disable Warning BC40000 ' Type or member is obsolete + Dim compilation = CreateCompilationWithMscorlib45(String.Empty, parseOptions:=New VisualBasicParseOptions().WithKind(SourceCodeKind.Interactive)) +#Enable Warning BC40000 ' Type or member is obsolete + + CompilationUtils.AssertTheseDiagnostics(compilation, +BC37285: Provided source code kind is unsupported or invalid: 'Interactive' + +~ +) + End Sub + + + Public Sub CompilingCodeWithInvalidLanguageVersionShouldProvideDiagnostics() + Dim compilation = CreateCompilation(String.Empty, parseOptions:=New VisualBasicParseOptions().WithLanguageVersion(DirectCast(10000, LanguageVersion))) + + CompilationUtils.AssertTheseDiagnostics(compilation, +BC37287: Provided language version is unsupported or invalid: '10000'. + +~ +) + End Sub + + + Public Sub CompilingCodeWithInvalidDocumentationModeShouldProvideDiagnostics() + Dim compilation = CreateCompilation(String.Empty, parseOptions:=New VisualBasicParseOptions().WithDocumentationMode(CType(100, DocumentationMode))) + + CompilationUtils.AssertTheseDiagnostics(compilation, +BC37286: Provided documentation mode is unsupported or invalid: '100'. + +~ +) + End Sub + + + Public Sub CompilingCodeWithInvalidParseOptionsInMultipleSyntaxTreesShouldReportThemAll() + Dim dict1 = New Dictionary(Of String, Object) + dict1.Add("1", Nothing) + Dim dict2 = New Dictionary(Of String, Object) + dict2.Add("2", Nothing) + Dim dict3 = New Dictionary(Of String, Object) + dict3.Add("3", Nothing) + + Dim syntaxTree1 = Parse(String.Empty, options:=New VisualBasicParseOptions().WithPreprocessorSymbols(dict1)) + Dim syntaxTree2 = Parse(String.Empty, options:=New VisualBasicParseOptions().WithPreprocessorSymbols(dict2)) + Dim syntaxTree3 = Parse(String.Empty, options:=New VisualBasicParseOptions().WithPreprocessorSymbols(dict3)) + + Dim options = New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary) + Dim compilation = CreateCompilationWithMscorlib({syntaxTree1, syntaxTree2, syntaxTree3}, options:=options) + Dim diagnostics = compilation.GetDiagnostics() + + CompilationUtils.AssertTheseDiagnostics(diagnostics, + +BC31030: Conditional compilation constant '1' is not valid: Identifier expected. + +~ +BC31030: Conditional compilation constant '2' is not valid: Identifier expected. + +~ +BC31030: Conditional compilation constant '3' is not valid: Identifier expected. + +~ +) + + Assert.True(diagnostics(0).Location.SourceTree.Equals(syntaxTree1)) + Assert.True(diagnostics(1).Location.SourceTree.Equals(syntaxTree2)) + Assert.True(diagnostics(2).Location.SourceTree.Equals(syntaxTree3)) + End Sub + + + Public Sub CompilingCodeWithSameParseOptionsInMultipleSyntaxTreesShouldReportOnlyNonDuplicates() + Dim dict1 = New Dictionary(Of String, Object) + dict1.Add("1", Nothing) + Dim dict2 = New Dictionary(Of String, Object) + dict2.Add("2", Nothing) + + Dim parseOptions1 = New VisualBasicParseOptions().WithPreprocessorSymbols(dict1) + Dim parseOptions2 = New VisualBasicParseOptions().WithPreprocessorSymbols(dict2) + + Dim syntaxTree1 = Parse(String.Empty, options:=parseOptions1) + Dim syntaxTree2 = Parse(String.Empty, options:=parseOptions2) + Dim syntaxTree3 = Parse(String.Empty, options:=parseOptions2) + + Dim options = New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary) + Dim compilation = CreateCompilationWithMscorlib({syntaxTree1, syntaxTree2, syntaxTree3}, options:=options) + Dim diagnostics = compilation.GetDiagnostics() + + CompilationUtils.AssertTheseDiagnostics(diagnostics, + +BC31030: Conditional compilation constant '1' is not valid: Identifier expected. + +~ +BC31030: Conditional compilation constant '2' is not valid: Identifier expected. + +~ +) + + Assert.True(diagnostics(0).Location.SourceTree.Equals(syntaxTree1)) + Assert.True(diagnostics(1).Location.SourceTree.Equals(syntaxTree2)) + End Sub + + + Public Sub DiagnositcsInCompilationOptionsParseOptionsAreDedupedWithParseTreesParseOptions() + Dim dict1 = New Dictionary(Of String, Object) + dict1.Add("1", Nothing) + Dim dict2 = New Dictionary(Of String, Object) + dict2.Add("2", Nothing) + + Dim parseOptions1 = New VisualBasicParseOptions().WithPreprocessorSymbols(dict1) + Dim parseOptions2 = New VisualBasicParseOptions().WithPreprocessorSymbols(dict2) + + Dim syntaxTree1 = Parse(String.Empty, options:=parseOptions1) + Dim syntaxTree2 = Parse(String.Empty, options:=parseOptions2) + + Dim options = New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary, parseOptions:=parseOptions1) + Dim compilation = CreateCompilationWithMscorlibAndVBRuntime({syntaxTree1, syntaxTree2}, options:=options) + Dim diagnostics = compilation.GetDiagnostics() + + CompilationUtils.AssertTheseDiagnostics(diagnostics, + +BC31030: Conditional compilation constant '1' is not valid: Identifier expected. +BC31030: Conditional compilation constant '2' is not valid: Identifier expected. + +~ +) + + Assert.Equal(diagnostics(0).Arguments, {"Identifier expected.", "2"}) + Assert.True(diagnostics(0).Location.SourceTree.Equals(syntaxTree2)) ' Syntax tree parse options are reported in CompilationStage.Parse + + Assert.Equal(diagnostics(1).Arguments, {"Identifier expected.", "1"}) + Assert.Null(diagnostics(1).Location.SourceTree) ' Compilation parse options are reported in CompilationStage.Declare + End Sub + + + Public Sub DiagnositcsInCompilationOptionsParseOptionsAreReportedSeparately() + Dim dict1 = New Dictionary(Of String, Object) + dict1.Add("1", Nothing) + Dim dict2 = New Dictionary(Of String, Object) + dict2.Add("2", Nothing) + + Dim parseOptions1 = New VisualBasicParseOptions().WithPreprocessorSymbols(dict1) + Dim parseOptions2 = New VisualBasicParseOptions().WithPreprocessorSymbols(dict2) + + Dim options = New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary, parseOptions:=parseOptions1) + + CompilationUtils.AssertTheseDiagnostics(options.Errors, + +BC31030: Conditional compilation constant '1' is not valid: Identifier expected. +) + + Dim syntaxTree = Parse(String.Empty, options:=parseOptions2) + Dim compilation = CreateCompilationWithMscorlibAndVBRuntime({syntaxTree}, options:=options) + Dim diagnostics = compilation.GetDiagnostics() + + CompilationUtils.AssertTheseDiagnostics(diagnostics, + +BC31030: Conditional compilation constant '1' is not valid: Identifier expected. +BC31030: Conditional compilation constant '2' is not valid: Identifier expected. + +~ +) + + Assert.Equal(diagnostics(0).Arguments, {"Identifier expected.", "2"}) + Assert.True(diagnostics(0).Location.SourceTree.Equals(syntaxTree)) ' Syntax tree parse options are reported in CompilationStage.Parse + + Assert.Equal(diagnostics(1).Arguments, {"Identifier expected.", "1"}) + Assert.Null(diagnostics(1).Location.SourceTree) ' Compilation parse options are reported in CompilationStage.Declare + End Sub + Private Shared Function DequeueCompilationEvents(eventQueue As AsyncQueue(Of CompilationEvent), ByRef compilationStartedFired As Boolean, ByRef declaredSymbolNames As HashSet(Of String), ByRef completedCompilationUnits As HashSet(Of String)) As Boolean compilationStartedFired = False declaredSymbolNames = New HashSet(Of String)() diff --git a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests.vb b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests.vb index 13ba1cd65eff8..4a136b584d8ad 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests.vb @@ -8,7 +8,7 @@ Imports Roslyn.Test.Utilities Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Semantics Partial Public Class IOperationTests - Inherits BasicTestBase + Inherits SemanticModelTestBase Public Sub InvalidUserDefinedOperators() diff --git a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ISymbolInitializer.vb b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ISymbolInitializer.vb index e4f2330030e18..808597b00435b 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ISymbolInitializer.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ISymbolInitializer.vb @@ -8,7 +8,7 @@ Imports Roslyn.Test.Utilities Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Semantics Partial Public Class IOperationTests - Inherits BasicTestBase + Inherits SemanticModelTestBase Public Sub NoInitializers() diff --git a/src/Compilers/VisualBasic/Test/Semantic/Semantics/ScriptSemanticsTests.vb b/src/Compilers/VisualBasic/Test/Semantic/Semantics/ScriptSemanticsTests.vb index b671645b96aa7..dc2eb37ff93d7 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Semantics/ScriptSemanticsTests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Semantics/ScriptSemanticsTests.vb @@ -172,14 +172,6 @@ System.Console.WriteLine(1) Return node5 End Function - - - Public Sub NoNeedToTestSourceCodeKindInteractive() -#Disable Warning BC40000 - Assert.Throws(Of ArgumentOutOfRangeException)(Function() New VisualBasicParseOptions(kind:=SourceCodeKind.Interactive)) -#Enable Warning BC40000 - End Sub - End Class End Namespace diff --git a/src/Compilers/VisualBasic/Test/Syntax/Parser/ParseLanguageVersionTests.vb b/src/Compilers/VisualBasic/Test/Syntax/Parser/ParseLanguageVersionTests.vb index 5be70a5393bf5..194c43b2084b9 100644 --- a/src/Compilers/VisualBasic/Test/Syntax/Parser/ParseLanguageVersionTests.vb +++ b/src/Compilers/VisualBasic/Test/Syntax/Parser/ParseLanguageVersionTests.vb @@ -23,8 +23,8 @@ Class C1 End Class ]]>.Value, LanguageVersion.VisualBasic9, - Diagnostic(ERRID.ERR_LanguageVersion, "Async").WithArguments("9.0", "async methods or lambdas").WithLocation(3, 5), - Diagnostic(ERRID.ERR_LanguageVersion, "Async").WithArguments("9.0", "async methods or lambdas").WithLocation(7, 5)) + Diagnostic(ERRID.ERR_LanguageVersion, "Async").WithArguments("9.0", "async methods or lambdas", "11").WithLocation(3, 5), + Diagnostic(ERRID.ERR_LanguageVersion, "Async").WithArguments("9.0", "async methods or lambdas", "11").WithLocation(7, 5)) End Sub <[Fact]> @@ -40,10 +40,10 @@ Module M1 End Module ]]>.Value, LanguageVersion.VisualBasic9, - Diagnostic(ERRID.ERR_LanguageVersion, "Iterator").WithArguments("9.0", "iterators").WithLocation(3, 13), - Diagnostic(ERRID.ERR_LanguageVersion, "Yield").WithArguments("9.0", "iterators").WithLocation(4, 9), - Diagnostic(ERRID.ERR_LanguageVersion, "Yield").WithArguments("9.0", "iterators").WithLocation(5, 9), - Diagnostic(ERRID.ERR_LanguageVersion, "Yield").WithArguments("9.0", "iterators").WithLocation(6, 9)) + Diagnostic(ERRID.ERR_LanguageVersion, "Iterator").WithArguments("9.0", "iterators", "11").WithLocation(3, 13), + Diagnostic(ERRID.ERR_LanguageVersion, "Yield").WithArguments("9.0", "iterators", "11").WithLocation(4, 9), + Diagnostic(ERRID.ERR_LanguageVersion, "Yield").WithArguments("9.0", "iterators", "11").WithLocation(5, 9), + Diagnostic(ERRID.ERR_LanguageVersion, "Yield").WithArguments("9.0", "iterators", "11").WithLocation(6, 9)) End Sub <[Fact]> @@ -57,7 +57,7 @@ Module M1 End Module ]]>.Value, LanguageVersion.VisualBasic9, - Diagnostic(ERRID.ERR_LanguageVersion, "From").WithArguments("9.0", "collection initializers").WithLocation(4, 51)) + Diagnostic(ERRID.ERR_LanguageVersion, "From").WithArguments("9.0", "collection initializers", "10").WithLocation(4, 51)) End Sub <[Fact]> @@ -71,8 +71,8 @@ Interface IVariant(Of Out R, In A) End Interface ]]>.Value, LanguageVersion.VisualBasic9, - Diagnostic(ERRID.ERR_LanguageVersion, "Out").WithArguments("9.0", "variance").WithLocation(2, 23), - Diagnostic(ERRID.ERR_LanguageVersion, "In").WithArguments("9.0", "variance").WithLocation(2, 30)) + Diagnostic(ERRID.ERR_LanguageVersion, "Out").WithArguments("9.0", "variance", "10").WithLocation(2, 23), + Diagnostic(ERRID.ERR_LanguageVersion, "In").WithArguments("9.0", "variance", "10").WithLocation(2, 30)) End Sub <[Fact]> @@ -95,7 +95,7 @@ Module M1 End Module ]]>.Value, LanguageVersion.VisualBasic9, - Diagnostic(ERRID.ERR_LanguageVersion, "Async").WithArguments("9.0", "async methods or lambdas").WithLocation(3, 13)) + Diagnostic(ERRID.ERR_LanguageVersion, "Async").WithArguments("9.0", "async methods or lambdas", "11").WithLocation(3, 13)) End Sub <[Fact]> @@ -137,8 +137,8 @@ Class C1 End Class ]]>.Value, LanguageVersion.VisualBasic9, - Diagnostic(ERRID.ERR_LanguageVersion, "Public Property Name As String").WithArguments("9.0", "auto-implemented properties").WithLocation(3, 5), - Diagnostic(ERRID.ERR_LanguageVersion, "Public Property Owner As String = ""DefaultName""").WithArguments("9.0", "auto-implemented properties").WithLocation(4, 5)) + Diagnostic(ERRID.ERR_LanguageVersion, "Public Property Name As String").WithArguments("9.0", "auto-implemented properties", "10").WithLocation(3, 5), + Diagnostic(ERRID.ERR_LanguageVersion, "Public Property Owner As String = ""DefaultName""").WithArguments("9.0", "auto-implemented properties", "10").WithLocation(4, 5)) End Sub <[Fact]> @@ -215,7 +215,7 @@ Class C1 End Class ]]>.Value, LanguageVersion.VisualBasic9, - Diagnostic(ERRID.ERR_LanguageVersion, "NameOf").WithArguments("9.0", "'nameof' expressions").WithLocation(5, 41)) + Diagnostic(ERRID.ERR_LanguageVersion, "NameOf").WithArguments("9.0", "'nameof' expressions", "14").WithLocation(5, 41)) End Sub <[Fact]> @@ -231,8 +231,8 @@ Class C1 End Class ]]>.Value, LanguageVersion.VisualBasic9, - Diagnostic(ERRID.ERR_LanguageVersion, "?").WithArguments("9.0", "null conditional operations").WithLocation(4, 31), - Diagnostic(ERRID.ERR_LanguageVersion, "?").WithArguments("9.0", "null conditional operations").WithLocation(5, 22)) + Diagnostic(ERRID.ERR_LanguageVersion, "?").WithArguments("9.0", "null conditional operations", "14").WithLocation(4, 31), + Diagnostic(ERRID.ERR_LanguageVersion, "?").WithArguments("9.0", "null conditional operations", "14").WithLocation(5, 22)) End Sub <[Fact]> @@ -251,7 +251,7 @@ End Namespace" For Each version In {LanguageVersion.VisualBasic9, LanguageVersion.VisualBasic10} ParseAndVerify(source, version, - Diagnostic(ERRID.ERR_LanguageVersion, "Global").WithArguments($"{CInt(version)}.0", "declaring a Global namespace").WithLocation(4, 11)) + Diagnostic(ERRID.ERR_LanguageVersion, "Global").WithArguments($"{CInt(version)}.0", "declaring a Global namespace", "11").WithLocation(4, 11)) Next For Each version In {LanguageVersion.VisualBasic11, LanguageVersion.VisualBasic12, LanguageVersion.VisualBasic14, VisualBasicParseOptions.Default.LanguageVersion} @@ -292,7 +292,7 @@ End Namespace" For Each version In {LanguageVersion.VisualBasic9, LanguageVersion.VisualBasic10} ParseAndVerify(source, version, - Diagnostic(ERRID.ERR_LanguageVersion, "Global").WithArguments($"{CInt(version)}.0", "declaring a Global namespace").WithLocation(4, 11)) + Diagnostic(ERRID.ERR_LanguageVersion, "Global").WithArguments($"{CInt(version)}.0", "declaring a Global namespace", "11").WithLocation(4, 11)) Next For Each version In {LanguageVersion.VisualBasic11, LanguageVersion.VisualBasic12, LanguageVersion.VisualBasic14, LanguageVersion.VisualBasic15, @@ -315,8 +315,8 @@ Module Module1 End Module ]]>.Value, LanguageVersion.VisualBasic12, - Diagnostic(ERRID.ERR_LanguageVersion, "$""world""").WithArguments("12.0", "interpolated strings").WithLocation(4, 18), - Diagnostic(ERRID.ERR_LanguageVersion, "$""hello {x1}""").WithArguments("12.0", "interpolated strings").WithLocation(5, 18)) + Diagnostic(ERRID.ERR_LanguageVersion, "$""world""").WithArguments("12.0", "interpolated strings", "14").WithLocation(4, 18), + Diagnostic(ERRID.ERR_LanguageVersion, "$""hello {x1}""").WithArguments("12.0", "interpolated strings", "14").WithLocation(5, 18)) End Sub @@ -334,8 +334,8 @@ Module Module1 End Module ]]>.Value, LanguageVersion.VisualBasic14, - Diagnostic(ERRID.ERR_LanguageVersion, "(1, 2)").WithArguments("14.0", "tuples").WithLocation(4, 18), - Diagnostic(ERRID.ERR_LanguageVersion, "(A:=1, B:=2)").WithArguments("14.0", "tuples").WithLocation(5, 18)) + Diagnostic(ERRID.ERR_LanguageVersion, "(1, 2)").WithArguments("14.0", "tuples", "15").WithLocation(4, 18), + Diagnostic(ERRID.ERR_LanguageVersion, "(A:=1, B:=2)").WithArguments("14.0", "tuples", "15").WithLocation(5, 18)) End Sub @@ -353,7 +353,7 @@ Module Module1 End Module ]]>.Value, LanguageVersion.VisualBasic14, - Diagnostic(ERRID.ERR_LanguageVersion, "(Integer, Integer)").WithArguments("14.0", "tuples").WithLocation(4, 19), - Diagnostic(ERRID.ERR_LanguageVersion, "(A As Integer, B As Integer)").WithArguments("14.0", "tuples").WithLocation(5, 19)) + Diagnostic(ERRID.ERR_LanguageVersion, "(Integer, Integer)").WithArguments("14.0", "tuples", "15").WithLocation(4, 19), + Diagnostic(ERRID.ERR_LanguageVersion, "(A As Integer, B As Integer)").WithArguments("14.0", "tuples", "15").WithLocation(5, 19)) End Sub End Class \ No newline at end of file diff --git a/src/Compilers/VisualBasic/Test/Syntax/Parser/VisualBasicParseOptionsTests.vb b/src/Compilers/VisualBasic/Test/Syntax/Parser/VisualBasicParseOptionsTests.vb index 2003a22a49467..08e78851026b1 100644 --- a/src/Compilers/VisualBasic/Test/Syntax/Parser/VisualBasicParseOptionsTests.vb +++ b/src/Compilers/VisualBasic/Test/Syntax/Parser/VisualBasicParseOptionsTests.vb @@ -20,9 +20,6 @@ Public Class VisualBasicParseOptionsTests TestProperty(Function(old, value) old.WithKind(value), Function(opt) opt.Kind, SourceCodeKind.Script) TestProperty(Function(old, value) old.WithLanguageVersion(value), Function(opt) opt.LanguageVersion, LanguageVersion.VisualBasic9) TestProperty(Function(old, value) old.WithDocumentationMode(value), Function(opt) opt.DocumentationMode, DocumentationMode.None) - - Assert.Throws(Of ArgumentOutOfRangeException)(Function() VisualBasicParseOptions.Default.WithKind(DirectCast(Integer.MaxValue, SourceCodeKind))) - Assert.Throws(Of ArgumentOutOfRangeException)(Function() VisualBasicParseOptions.Default.WithLanguageVersion(DirectCast(1000, LanguageVersion))) End Sub @@ -51,44 +48,6 @@ Public Class VisualBasicParseOptionsTests Assert.Equal(0, VisualBasicParseOptions.Default.WithPreprocessorSymbols(syms).WithPreprocessorSymbols(CType(Nothing, ImmutableArray(Of KeyValuePair(Of String, Object)))).PreprocessorSymbols.Length) Assert.Equal(0, VisualBasicParseOptions.Default.WithPreprocessorSymbols(syms).WithPreprocessorSymbols(DirectCast(Nothing, IEnumerable(Of KeyValuePair(Of String, Object)))).PreprocessorSymbols.Length) Assert.Equal(0, VisualBasicParseOptions.Default.WithPreprocessorSymbols(syms).WithPreprocessorSymbols(DirectCast(Nothing, KeyValuePair(Of String, Object)())).PreprocessorSymbols.Length) - - Dim syms2 = {New KeyValuePair(Of String, Object)("A", 1), - New KeyValuePair(Of String, Object)("B", New List(Of String)()), - New KeyValuePair(Of String, Object)("C", 3)} - - Assert.Throws(Of ArgumentException)(Function() New VisualBasicParseOptions(preprocessorSymbols:=syms2)) - Assert.Throws(Of ArgumentException)(Function() VisualBasicParseOptions.Default.WithPreprocessorSymbols(syms2)) - End Sub - - - Public Sub ConstructorValidation() - Assert.Throws(Of ArgumentOutOfRangeException)(Function() New VisualBasicParseOptions(kind:=DirectCast(Int32.MaxValue, SourceCodeKind))) - Assert.Throws(Of ArgumentOutOfRangeException)(Function() New VisualBasicParseOptions(languageVersion:=DirectCast(1000, LanguageVersion))) - End Sub - - - Public Sub InvalidDefineSymbols() - - ' Command line: error BC31030: Project-level conditional compilation constant 'xxx' is not valid: Identifier expected - - Dim syms = ImmutableArray.Create(New KeyValuePair(Of String, Object)("", 1)) - Assert.Throws(Of ArgumentException)(Function() New VisualBasicParseOptions(preprocessorSymbols:=syms)) - - syms = ImmutableArray.Create(New KeyValuePair(Of String, Object)(" ", 1)) - Assert.Throws(Of ArgumentException)(Function() New VisualBasicParseOptions(preprocessorSymbols:=syms)) - - syms = ImmutableArray.Create(New KeyValuePair(Of String, Object)("Good", 1), - New KeyValuePair(Of String, Object)(Nothing, 2)) - Assert.Throws(Of ArgumentException)(Function() New VisualBasicParseOptions(preprocessorSymbols:=syms)) - - syms = ImmutableArray.Create(New KeyValuePair(Of String, Object)("Good", 1), - New KeyValuePair(Of String, Object)("Bad.Symbol", 2)) - Assert.Throws(Of ArgumentException)(Function() New VisualBasicParseOptions(preprocessorSymbols:=syms)) - - syms = ImmutableArray.Create(New KeyValuePair(Of String, Object)("123", 1), - New KeyValuePair(Of String, Object)("Bad/Symbol", 2), - New KeyValuePair(Of String, Object)("Good", 3)) - Assert.Throws(Of ArgumentException)(Function() New VisualBasicParseOptions(preprocessorSymbols:=syms)) End Sub @@ -264,4 +223,166 @@ Public Class VisualBasicParseOptionsTests "PreprocessorSymbols", "SpecifiedLanguageVersion") End Sub + + + Public Sub SpecifiedKindIsMappedCorrectly() + Dim options = New VisualBasicParseOptions() + Assert.Equal(SourceCodeKind.Regular, options.Kind) + Assert.Equal(SourceCodeKind.Regular, options.SpecifiedKind) + + options.Errors.Verify() + + options = New VisualBasicParseOptions(kind:=SourceCodeKind.Regular) + Assert.Equal(SourceCodeKind.Regular, options.Kind) + Assert.Equal(SourceCodeKind.Regular, options.SpecifiedKind) + + options.Errors.Verify() + + options = New VisualBasicParseOptions(kind:=SourceCodeKind.Script) + Assert.Equal(SourceCodeKind.Script, options.Kind) + Assert.Equal(SourceCodeKind.Script, options.SpecifiedKind) + + options.Errors.Verify() + +#Disable Warning BC40000 ' SourceCodeKind.Interactive is obsolete + options = New VisualBasicParseOptions(kind:=SourceCodeKind.Interactive) + Assert.Equal(SourceCodeKind.Script, options.Kind) + Assert.Equal(SourceCodeKind.Interactive, options.SpecifiedKind) +#Enable Warning BC40000 ' SourceCodeKind.Interactive is obsolete + + options.Errors.Verify(Diagnostic(ERRID.ERR_BadSourceCodeKind).WithArguments("Interactive").WithLocation(1, 1)) + + options = New VisualBasicParseOptions(kind:=CType(Int32.MinValue, SourceCodeKind)) + Assert.Equal(SourceCodeKind.Regular, options.Kind) + Assert.Equal(CType(Int32.MinValue, SourceCodeKind), options.SpecifiedKind) + + options.Errors.Verify(Diagnostic(ERRID.ERR_BadSourceCodeKind).WithArguments("-2147483648").WithLocation(1, 1)) + End Sub + + + Public Sub TwoOptionsWithDifferentSpecifiedKindShouldNotHaveTheSameHashCodes() + Dim options1 = New VisualBasicParseOptions(kind:=SourceCodeKind.Script) + Dim options2 = New VisualBasicParseOptions(kind:=SourceCodeKind.Script) + + Assert.Equal(options1.GetHashCode(), options2.GetHashCode()) + + ' They both map internally to SourceCodeKind.Script +#Disable Warning BC40000 ' SourceCodeKind.Interactive is obsolete + options1 = New VisualBasicParseOptions(kind:=SourceCodeKind.Script) + options2 = New VisualBasicParseOptions(kind:=SourceCodeKind.Interactive) +#Enable Warning BC40000 ' SourceCodeKind.Interactive Is obsolete + + Assert.NotEqual(options1.GetHashCode(), options2.GetHashCode()) + End Sub + + + Public Sub TwoOptionsWithDifferentSpecifiedKindShouldNotBeEqual() + Dim options1 = New VisualBasicParseOptions(kind:=SourceCodeKind.Script) + Dim options2 = New VisualBasicParseOptions(kind:=SourceCodeKind.Script) + + Assert.True(options1.Equals(options2)) + + ' They both map internally to SourceCodeKind.Script +#Disable Warning BC40000 ' SourceCodeKind.Interactive is obsolete + options1 = New VisualBasicParseOptions(kind:=SourceCodeKind.Script) + options2 = New VisualBasicParseOptions(kind:=SourceCodeKind.Interactive) +#Enable Warning BC40000 ' SourceCodeKind.Interactive Is obsolete + + Assert.False(options1.Equals(options2)) + End Sub + + + Public Sub BadSourceCodeKindShouldProduceDiagnostics() +#Disable Warning BC40000 ' Type Or member Is obsolete + Dim options = New VisualBasicParseOptions(kind:=SourceCodeKind.Interactive) +#Enable Warning BC40000 ' Type Or member Is obsolete + + options.Errors.Verify(Diagnostic(ERRID.ERR_BadSourceCodeKind).WithArguments("Interactive").WithLocation(1, 1)) + End Sub + + + Public Sub BadDocumentationModeShouldProduceDiagnostics() + Dim options = New VisualBasicParseOptions(documentationMode:=CType(100, DocumentationMode)) + + options.Errors.Verify(Diagnostic(ERRID.ERR_BadDocumentationMode).WithArguments("100").WithLocation(1, 1)) + End Sub + + + Public Sub BadLanguageVersionShouldProduceDiagnostics() + Dim options = New VisualBasicParseOptions(languageVersion:=DirectCast(10000, LanguageVersion)) + + options.Errors.Verify(Diagnostic(ERRID.ERR_BadLanguageVersion).WithArguments("10000").WithLocation(1, 1)) + End Sub + + + Public Sub BadPreProcessorSymbolsShouldProduceDiagnostics() + Dim symbols = New Dictionary(Of String, Object) + symbols.Add("test", Nothing) + symbols.Add("1", Nothing) + Dim options = New VisualBasicParseOptions(preprocessorSymbols:=symbols) + + options.Errors.Verify(Diagnostic(ERRID.ERR_ConditionalCompilationConstantNotValid).WithArguments("Identifier expected.", "1").WithLocation(1, 1)) + End Sub + + + Public Sub BadSourceCodeKindShouldProduceDiagnostics_WithVariation() +#Disable Warning BC40000 ' Type Or member Is obsolete + Dim options = New VisualBasicParseOptions().WithKind(SourceCodeKind.Interactive) +#Enable Warning BC40000 ' Type Or member Is obsolete + + options.Errors.Verify(Diagnostic(ERRID.ERR_BadSourceCodeKind).WithArguments("Interactive").WithLocation(1, 1)) + End Sub + + + Public Sub BadDocumentationModeShouldProduceDiagnostics_WithVariation() + Dim options = New VisualBasicParseOptions().WithDocumentationMode(CType(100, DocumentationMode)) + + options.Errors.Verify(Diagnostic(ERRID.ERR_BadDocumentationMode).WithArguments("100").WithLocation(1, 1)) + End Sub + + + Public Sub BadLanguageVersionShouldProduceDiagnostics_WithVariation() + Dim options = New VisualBasicParseOptions().WithLanguageVersion(DirectCast(10000, LanguageVersion)) + + options.Errors.Verify(Diagnostic(ERRID.ERR_BadLanguageVersion).WithArguments("10000").WithLocation(1, 1)) + End Sub + + + Public Sub BadPreProcessorSymbolsShouldProduceDiagnostics_EmptyString() + Dim symbols = New Dictionary(Of String, Object) + symbols.Add("", Nothing) + Dim options = New VisualBasicParseOptions().WithPreprocessorSymbols(symbols) + + options.Errors.Verify(Diagnostic(ERRID.ERR_ConditionalCompilationConstantNotValid).WithArguments("Identifier expected.", "").WithLocation(1, 1)) + End Sub + + + Public Sub BadPreProcessorSymbolsShouldProduceDiagnostics_WhiteSpacetring() + Dim symbols = New Dictionary(Of String, Object) + symbols.Add(" ", Nothing) + Dim options = New VisualBasicParseOptions().WithPreprocessorSymbols(symbols) + + options.Errors.Verify(Diagnostic(ERRID.ERR_ConditionalCompilationConstantNotValid).WithArguments("Identifier expected.", " ").WithLocation(1, 1)) + End Sub + + + Public Sub BadPreProcessorSymbolsShouldProduceDiagnostics_SymbolWithDots() + Dim symbols = New Dictionary(Of String, Object) + symbols.Add("Good", Nothing) + symbols.Add("Bad.Symbol", Nothing) + Dim options = New VisualBasicParseOptions().WithPreprocessorSymbols(symbols) + + options.Errors.Verify(Diagnostic(ERRID.ERR_ConditionalCompilationConstantNotValid).WithArguments("Identifier expected.", "Bad.Symbol").WithLocation(1, 1)) + End Sub + + + Public Sub BadPreProcessorSymbolsShouldProduceDiagnostics_SymbolWithSlashes() + Dim symbols = New Dictionary(Of String, Object) + symbols.Add("Good", Nothing) + symbols.Add("Bad\\Symbol", Nothing) + Dim options = New VisualBasicParseOptions().WithPreprocessorSymbols(symbols) + + options.Errors.Verify(Diagnostic(ERRID.ERR_ConditionalCompilationConstantNotValid).WithArguments("Identifier expected.", "Bad\\Symbol").WithLocation(1, 1)) + End Sub + End Class diff --git a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj index 3813a1af181ec..9e0ad1df1f55b 100644 --- a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj +++ b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj @@ -204,6 +204,7 @@ + diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/UpgradeProject/UpgradeProjectTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/UpgradeProject/UpgradeProjectTests.cs new file mode 100644 index 0000000000000..ce971a1e8b182 --- /dev/null +++ b/src/EditorFeatures/CSharpTest/Diagnostics/UpgradeProject/UpgradeProjectTests.cs @@ -0,0 +1,249 @@ +// 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.Linq; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.UpgradeProject; +using Microsoft.CodeAnalysis.Diagnostics; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.Async +{ + public partial class UpgradeProjectTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest + { + internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) + => (null, new CSharpUpgradeProjectCodeFixProvider()); + + private async Task TestLanguageVersionUpgradedAsync( + string initialMarkup, + LanguageVersion expected, + ParseOptions parseOptions, + int index = 0) + { + var parameters = new TestParameters(parseOptions: parseOptions); + using (var workspace = CreateWorkspaceFromOptions(initialMarkup, parameters)) + { + var actions = await GetCodeActionsAsync(workspace, parameters); + var operations = await VerifyInputsAndGetOperationsAsync(index, actions, priority: null); + + var appliedChanges = ApplyOperationsAndGetSolution(workspace, operations); + var oldSolution = appliedChanges.Item1; + var newSolution = appliedChanges.Item2; + Assert.True(newSolution.Projects.Where(p => p.Language == LanguageNames.CSharp) + .All(p => ((CSharpParseOptions)p.ParseOptions).SpecifiedLanguageVersion == expected)); + } + + await TestAsync(initialMarkup, initialMarkup, parseOptions); // no change to markup + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)] + public async Task UpgradeProjectToDefault() + { + await TestLanguageVersionUpgradedAsync( +@" +class Program +{ + void A() + { + var x = [|(1, 2)|]; + } +}", + LanguageVersion.Default, + new CSharpParseOptions(LanguageVersion.CSharp6)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)] + public async Task UpgradeProjectToCSharp7() + { + await TestLanguageVersionUpgradedAsync( +@" +class Program +{ + void A() + { + var x = [|(1, 2)|]; + } +}", + LanguageVersion.CSharp7, + new CSharpParseOptions(LanguageVersion.CSharp6), + index: 1); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)] + public async Task UpgradeAllProjectsToDefault() + { + await TestLanguageVersionUpgradedAsync( +@" + + +class C +{ + void A() + { + var x = [|(1, 2)|]; + } +} + + + + + + + + + + +", + LanguageVersion.Default, + parseOptions: null, + index: 2); + + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)] + public async Task UpgradeAllProjectsToCSharp7() + { + await TestLanguageVersionUpgradedAsync( +@" + + +class C +{ + void A() + { + var x = [|(1, 2)|]; + } +} + + + + + + + + + + +", + LanguageVersion.Default, + parseOptions: null, + index: 2); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)] + public async Task ListAllSuggestions() + { + await TestExactActionSetOfferedAsync( + +@" + + +class C +{ + void A() + { + var x = [|(1, 2)|]; + } +} + + + + + + +", + new[] { + string.Format(CSharpFeaturesResources.Upgrade_this_project_to_csharp_language_version_0, "default"), + string.Format(CSharpFeaturesResources.Upgrade_this_project_to_csharp_language_version_0, "7"), + string.Format(CSharpFeaturesResources.Upgrade_all_csharp_projects_to_language_version_0, "default"), + string.Format(CSharpFeaturesResources.Upgrade_all_csharp_projects_to_language_version_0, "7") + }); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)] + public async Task FixAllProjectsNotOffered() + { + await TestExactActionSetOfferedAsync( + +@" + + +class C +{ + void A() + { + var x = [|(1, 2)|]; + } +} + + + + +", + new[] { + string.Format(CSharpFeaturesResources.Upgrade_this_project_to_csharp_language_version_0, "default"), + string.Format(CSharpFeaturesResources.Upgrade_this_project_to_csharp_language_version_0, "7") + }); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)] + public async Task OnlyOfferFixAllProjectsToCSharp7WhenApplicable() + { + await TestExactActionSetOfferedAsync( + +@" + + +class C +{ + void A() + { + var x = [|(1, 2)|]; + } +} + + + + + + +", + new[] { + string.Format(CSharpFeaturesResources.Upgrade_this_project_to_csharp_language_version_0, "default"), + string.Format(CSharpFeaturesResources.Upgrade_this_project_to_csharp_language_version_0, "7"), + string.Format(CSharpFeaturesResources.Upgrade_all_csharp_projects_to_language_version_0, "default") + }); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)] + public async Task OnlyOfferFixAllProjectsToDefaultWhenApplicable() + { + await TestExactActionSetOfferedAsync( + +@" + + +class C +{ + void A() + { + var x = [|(1, 2)|]; + } +} + + + + + + +", + new[] { + string.Format(CSharpFeaturesResources.Upgrade_this_project_to_csharp_language_version_0, "default"), + string.Format(CSharpFeaturesResources.Upgrade_this_project_to_csharp_language_version_0, "7"), + string.Format(CSharpFeaturesResources.Upgrade_all_csharp_projects_to_language_version_0, "7") + }); + } + } +} \ No newline at end of file diff --git a/src/EditorFeatures/CSharpTest/UseCollectionInitializer/UseCollectionInitializerTests.cs b/src/EditorFeatures/CSharpTest/UseCollectionInitializer/UseCollectionInitializerTests.cs index f2d5bbf56e157..6c66c5e9405d6 100644 --- a/src/EditorFeatures/CSharpTest/UseCollectionInitializer/UseCollectionInitializerTests.cs +++ b/src/EditorFeatures/CSharpTest/UseCollectionInitializer/UseCollectionInitializerTests.cs @@ -699,6 +699,74 @@ static void Main(string[] args) var myStringList = myStringArray?.ToList() ?? new [||]List(); myStringList.Add(""Done""); } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCollectionInitializer)] + [WorkItem(17823, "https://github.com/dotnet/roslyn/issues/17823")] + public async Task TestMissingWhenReferencedInInitializer() + { + await TestMissingInRegularAndScriptAsync( +@" +using System.Collections.Generic; + +class C +{ + static void M() + { + var items = new [||]List(); + items[0] = items[0]; + } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCollectionInitializer)] + [WorkItem(17823, "https://github.com/dotnet/roslyn/issues/17823")] + public async Task TestWhenReferencedInInitializer() + { + await TestInRegularAndScript1Async( +@" +using System.Collections.Generic; + +class C +{ + static void M() + { + var items = new [||]List(); + items[0] = 1; + items[1] = items[0]; + } +}", +@" +using System.Collections.Generic; + +class C +{ + static void M() + { + var items = new [||]List + { + [0] = 1 + }; + items[1] = items[0]; + } +}"); + } + + [WorkItem(17853, "https://github.com/dotnet/roslyn/issues/17853")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseObjectInitializer)] + public async Task TestMissingForDynamic() + { + await TestMissingInRegularAndScriptAsync( +@"using System.Dynamic; + +class C +{ + void Foo() + { + dynamic body = [||]new ExpandoObject(); + body[0] = new ExpandoObject(); + } }"); } } diff --git a/src/EditorFeatures/CSharpTest/UseObjectInitializer/UseObjectInitializerTests.cs b/src/EditorFeatures/CSharpTest/UseObjectInitializer/UseObjectInitializerTests.cs index 835f78d056bfa..38ccaf37899fc 100644 --- a/src/EditorFeatures/CSharpTest/UseObjectInitializer/UseObjectInitializerTests.cs +++ b/src/EditorFeatures/CSharpTest/UseObjectInitializer/UseObjectInitializerTests.cs @@ -462,6 +462,23 @@ C Add(int x) { c.a = 1; return c; } +}"); + } + + [WorkItem(17853, "https://github.com/dotnet/roslyn/issues/17853")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseObjectInitializer)] + public async Task TestMissingForDynamic() + { + await TestMissingInRegularAndScriptAsync( +@"using System.Dynamic; + +class C +{ + void Foo() + { + dynamic body = [||]new ExpandoObject(); + body.content = new ExpandoObject(); + } }"); } } diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/SignatureHelp/Controller.Session.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/SignatureHelp/Controller.Session.cs index dd1a0a0e276a4..206bd8e674947 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/SignatureHelp/Controller.Session.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/SignatureHelp/Controller.Session.cs @@ -9,6 +9,15 @@ internal partial class Controller { internal partial class Session : Session { + /// + /// When ther user moves the caret we issue retrigger commands. There may be a long + /// chain of these, and they may take time for each to process. This can be visible + /// to the user as a long delay before the signature help items update. To avoid this + /// we keep track if there is new outstanding retrigger command and we bail on the + /// computation if another is in the queue. + /// + private int _retriggerId; + public Session(Controller controller, ISignatureHelpPresenterSession presenterSession) : base(controller, new ModelComputation(controller, TaskScheduler.Default), presenterSession) { diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/SignatureHelp/Controller.Session_ComputeModel.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/SignatureHelp/Controller.Session_ComputeModel.cs index a702e347f64b0..2d7c928be780d 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/SignatureHelp/Controller.Session_ComputeModel.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/SignatureHelp/Controller.Session_ComputeModel.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -22,15 +23,7 @@ internal partial class Controller internal partial class Session { public void ComputeModel( - IList providers, - SignatureHelpTriggerInfo triggerInfo) - { - ComputeModel(providers, SpecializedCollections.EmptyList(), triggerInfo); - } - - public void ComputeModel( - IList matchedProviders, - IList unmatchedProviders, + ImmutableArray providers, SignatureHelpTriggerInfo triggerInfo) { AssertIsForeground(); @@ -38,16 +31,31 @@ public void ComputeModel( var caretPosition = Controller.TextView.GetCaretPoint(Controller.SubjectBuffer).Value; var disconnectedBufferGraph = new DisconnectedBufferGraph(Controller.SubjectBuffer, Controller.TextView.TextBuffer); + // If this is a retrigger command then update the retrigger-id. This way + // any in-flight retrigger-updates will immediately bail out. + if (IsNonTypeCharRetrigger(triggerInfo)) + { + Interlocked.Increment(ref _retriggerId); + } + + var localId = _retriggerId; + // If we've already computed a model, then just use that. Otherwise, actually // compute a new model and send that along. Computation.ChainTaskAndNotifyControllerWhenFinished( - (model, cancellationToken) => ComputeModelInBackgroundAsync(model, matchedProviders, unmatchedProviders, caretPosition, disconnectedBufferGraph, triggerInfo, cancellationToken)); + (model, cancellationToken) => ComputeModelInBackgroundAsync( + localId, model, providers, caretPosition, + disconnectedBufferGraph, triggerInfo, cancellationToken)); } + private static bool IsNonTypeCharRetrigger(SignatureHelpTriggerInfo triggerInfo) + => triggerInfo.TriggerReason == SignatureHelpTriggerReason.RetriggerCommand && + triggerInfo.TriggerCharacter == null; + private async Task ComputeModelInBackgroundAsync( + int localRetriggerId, Model currentModel, - IList matchedProviders, - IList unmatchedProviders, + ImmutableArray providers, SnapshotPoint caretPosition, DisconnectedBufferGraph disconnectedBufferGraph, SignatureHelpTriggerInfo triggerInfo, @@ -68,30 +76,36 @@ private async Task ComputeModelInBackgroundAsync( if (triggerInfo.TriggerReason == SignatureHelpTriggerReason.RetriggerCommand) { - if (currentModel == null || - (triggerInfo.TriggerCharacter.HasValue && !currentModel.Provider.IsRetriggerCharacter(triggerInfo.TriggerCharacter.Value))) + if (currentModel == null) + { + return null; + } + + if (triggerInfo.TriggerCharacter.HasValue && + !currentModel.Provider.IsRetriggerCharacter(triggerInfo.TriggerCharacter.Value)) { return currentModel; } } // first try to query the providers that can trigger on the specified character - var result = await ComputeItemsAsync(matchedProviders, caretPosition, triggerInfo, document, cancellationToken).ConfigureAwait(false); - var provider = result.Item1; - var items = result.Item2; + var providerAndItemsOpt = await ComputeItemsAsync( + localRetriggerId, providers, caretPosition, + triggerInfo, document, cancellationToken).ConfigureAwait(false); - if (provider == null) + if (providerAndItemsOpt == null) { - // no match, so now query the other providers - result = await ComputeItemsAsync(unmatchedProviders, caretPosition, triggerInfo, document, cancellationToken).ConfigureAwait(false); - provider = result.Item1; - items = result.Item2; + // Another retrigger was enqueued while we were inflight. Just + // stop all work and return the last computed model. We'll compute + // the correct model when we process the other retrigger task. + return currentModel; + } - if (provider == null) - { - // the other providers didn't produce items either, so we don't produce a model - return null; - } + var (provider, items) = providerAndItemsOpt.Value; + if (provider == null) + { + // No provider produced items. So we can't produce a model + return null; } if (currentModel != null && @@ -158,17 +172,18 @@ private static SignatureHelpItem GetSelectedItem(Model currentModel, SignatureHe } private static bool DisplayPartsMatch(SignatureHelpItem i1, SignatureHelpItem i2) - { - return i1.GetAllParts().SequenceEqual(i2.GetAllParts(), CompareParts); - } + => i1.GetAllParts().SequenceEqual(i2.GetAllParts(), CompareParts); private static bool CompareParts(TaggedText p1, TaggedText p2) - { - return p1.ToString() == p2.ToString(); - } - - private async Task> ComputeItemsAsync( - IList providers, + => p1.ToString() == p2.ToString(); + + /// + /// Returns null if our work was preempted and we want to return the + /// previous model we've computed. + /// + private async Task<(ISignatureHelpProvider provider, SignatureHelpItems items)?> ComputeItemsAsync( + int localRetriggerId, + ImmutableArray providers, SnapshotPoint caretPosition, SignatureHelpTriggerInfo triggerInfo, Document document, @@ -183,6 +198,14 @@ private async Task> ComputeIte // to the extension crashing. foreach (var provider in providers) { + // If this is a retrigger command, and another retrigger command has already + // been issued then we can bail out immediately. + if (IsNonTypeCharRetrigger(triggerInfo) && + localRetriggerId != _retriggerId) + { + return null; + } + cancellationToken.ThrowIfCancellationRequested(); var currentItems = await provider.GetItemsAsync(document, caretPosition, triggerInfo, cancellationToken).ConfigureAwait(false); @@ -204,7 +227,7 @@ private async Task> ComputeIte } } - return Tuple.Create(bestProvider, bestItems); + return (bestProvider, bestItems); } catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) { @@ -227,4 +250,4 @@ private bool IsBetter(SignatureHelpItems bestItems, TextSpan? currentTextSpan) } } } -} +} \ No newline at end of file diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/SignatureHelp/Controller.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/SignatureHelp/Controller.cs index 348f3971b9b05..3f3c3888c42af 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/SignatureHelp/Controller.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/SignatureHelp/Controller.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using Microsoft.CodeAnalysis.Editor.Commands; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Host.Mef; @@ -23,7 +24,7 @@ internal partial class Controller : private static readonly object s_controllerPropertyKey = new object(); private readonly IList> _allProviders; - private IList _providers; + private ImmutableArray _providers; private IContentType _lastSeenContentType; public Controller( @@ -48,7 +49,7 @@ internal Controller( IList providers) : base(textView, subjectBuffer, presenter, asyncListener, documentProvider, "SignatureHelp") { - _providers = providers; + _providers = providers.ToImmutableArray(); } internal static Controller GetInstance( @@ -98,7 +99,8 @@ internal override void OnModelUpdated(Model modelOpt) } } - private void StartSession(IList providers, SignatureHelpTriggerInfo triggerInfo) + private void StartSession( + ImmutableArray providers, SignatureHelpTriggerInfo triggerInfo) { AssertIsForeground(); VerifySessionIsInactive(); @@ -107,7 +109,7 @@ private void StartSession(IList providers, SignatureHelp this.sessionOpt.ComputeModel(providers, triggerInfo); } - private IList GetProviders() + private ImmutableArray GetProviders() { this.AssertIsForeground(); @@ -120,7 +122,8 @@ private IList GetProviders() var document = snapshot.GetOpenDocumentInCurrentContextWithChanges(); if (document != null) { - _providers = document.Project.LanguageServices.WorkspaceServices.SelectMatchingExtensionValues(_allProviders, this.SubjectBuffer.ContentType); + _providers = document.Project.LanguageServices.WorkspaceServices.SelectMatchingExtensionValues( + _allProviders, this.SubjectBuffer.ContentType).ToImmutableArray(); _lastSeenContentType = currentContentType; } } diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/SignatureHelp/Controller_TypeChar.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/SignatureHelp/Controller_TypeChar.cs index ca6f7ff868523..bba2e0978c164 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/SignatureHelp/Controller_TypeChar.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/SignatureHelp/Controller_TypeChar.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.Editor.Commands; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; @@ -70,9 +71,7 @@ void ICommandHandler.ExecuteCommand(TypeCharCommandArgs arg // care of cases where the filtered set of providers didn't provide anything but one of the // other providers could still be valid, but doesn't explicitly treat the typed character as // a trigger character. - var filteredProviders = FilterProviders(allProviders, args.TypedChar); - var textuallyTriggeredProviders = filteredProviders.Item1; - var untriggeredProviders = filteredProviders.Item2; + var (textuallyTriggeredProviders, untriggeredProviders) = FilterProviders(allProviders, args.TypedChar); var triggerInfo = new SignatureHelpTriggerInfo(SignatureHelpTriggerReason.TypeCharCommand, args.TypedChar); if (!IsSessionActive) @@ -112,7 +111,8 @@ void ICommandHandler.ExecuteCommand(TypeCharCommandArgs arg // it was in a string like: Foo(bar, "( // // Or it can trigger a new list. Ask the computation to compute again. - sessionOpt.ComputeModel(textuallyTriggeredProviders, untriggeredProviders, triggerInfo); + sessionOpt.ComputeModel( + textuallyTriggeredProviders.Concat(untriggeredProviders), triggerInfo); computed = true; } @@ -124,12 +124,13 @@ void ICommandHandler.ExecuteCommand(TypeCharCommandArgs arg } } - private Tuple, List> FilterProviders(IList providers, char ch) + private (ImmutableArray matched, ImmutableArray unmatched) FilterProviders( + ImmutableArray providers, char ch) { AssertIsForeground(); - var matchedProviders = new List(); - var unmatchedProviders = new List(); + var matchedProviders = ArrayBuilder.GetInstance(); + var unmatchedProviders = ArrayBuilder.GetInstance(); foreach (var provider in providers) { if (provider.IsTriggerCharacter(ch)) @@ -142,7 +143,7 @@ private Tuple, List> Filter } } - return Tuple.Create(matchedProviders, unmatchedProviders); + return (matchedProviders.ToImmutableAndFree(), unmatchedProviders.ToImmutableAndFree()); } } } diff --git a/src/EditorFeatures/TestUtilities/Traits.cs b/src/EditorFeatures/TestUtilities/Traits.cs index fd0c80efea176..9392224cca979 100644 --- a/src/EditorFeatures/TestUtilities/Traits.cs +++ b/src/EditorFeatures/TestUtilities/Traits.cs @@ -30,6 +30,7 @@ public static class Features public const string CodeActionsAddConstructorParametersFromMembers = "CodeActions.AddConstructorParametersFromMembers"; public const string CodeActionsAddDocCommentNodes = "CodeActions.AddDocCommentParamNodes"; public const string CodeActionsAddAwait = "CodeActions.AddAwait"; + public const string CodeActionsUpgradeProject = "CodeActions.UpgradeProject"; public const string CodeActionsAddBraces = "CodeActions.AddBraces"; public const string CodeActionsAddImport = "CodeActions.AddImport"; public const string CodeActionsAddMissingReference = "CodeActions.AddMissingReference"; diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/ReplaceMethodWithProperty/ReplaceMethodWithPropertyTests.vb b/src/EditorFeatures/VisualBasicTest/CodeActions/ReplaceMethodWithProperty/ReplaceMethodWithPropertyTests.vb index 5c6606ca4b449..b942e455936e2 100644 --- a/src/EditorFeatures/VisualBasicTest/CodeActions/ReplaceMethodWithProperty/ReplaceMethodWithPropertyTests.vb +++ b/src/EditorFeatures/VisualBasicTest/CodeActions/ReplaceMethodWithProperty/ReplaceMethodWithPropertyTests.vb @@ -27,6 +27,22 @@ End class", End class") End Function + + + Public Async Function TestMissingParameterList() As Task + Await TestInRegularAndScript1Async( +"class C + function [||]GetFoo as integer + End function +End class", +"class C + ReadOnly Property Foo as integer + Get + End Get + End Property +End class") + End Function + Public Async Function TestMethodWithoutGetName() As Task Await TestInRegularAndScriptAsync( diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/MakeMethodAsynchronous/MakeMethodAsynchronousTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/MakeMethodAsynchronous/MakeMethodAsynchronousTests.vb index 719f3e7b0ae11..6e0137addc6f3 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/MakeMethodAsynchronous/MakeMethodAsynchronousTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/MakeMethodAsynchronous/MakeMethodAsynchronousTests.vb @@ -68,8 +68,7 @@ Module Program Async Function TestAsync() As Task(Of Integer) Await Task.Delay(1) Function Sub - End Module" - ) + End Module") End Function @@ -389,5 +388,25 @@ End Module Await TestAsync(initial, expected, ignoreTrivia:=False) End Function + + + + Public Async Function TestWithMissingParameterList() As Task + Await TestInRegularAndScriptAsync( +"Imports System +Imports System.Threading.Tasks +Module Program + Sub Test ' Comment + [|Await Task.Delay(1)|] + End Sub +End Module", +"Imports System +Imports System.Threading.Tasks +Module Program + Async Function TestAsync As Task ' Comment + Await Task.Delay(1) + End Function +End Module", ignoreTrivia:=False) + End Function End Class End Namespace \ No newline at end of file diff --git a/src/Features/CSharp/Portable/CSharpFeatures.csproj b/src/Features/CSharp/Portable/CSharpFeatures.csproj index 90fa8ec9f995e..5590e581df768 100644 --- a/src/Features/CSharp/Portable/CSharpFeatures.csproj +++ b/src/Features/CSharp/Portable/CSharpFeatures.csproj @@ -83,6 +83,7 @@ + @@ -480,4 +481,4 @@ - \ No newline at end of file + diff --git a/src/Features/CSharp/Portable/CSharpFeaturesResources.Designer.cs b/src/Features/CSharp/Portable/CSharpFeaturesResources.Designer.cs index 78a45aaa6c2b7..f9fa5f636b782 100644 --- a/src/Features/CSharp/Portable/CSharpFeaturesResources.Designer.cs +++ b/src/Features/CSharp/Portable/CSharpFeaturesResources.Designer.cs @@ -1054,6 +1054,24 @@ internal static string unchecked_statement { } } + /// + /// Looks up a localized string similar to Upgrade all C# projects to language version '{0}'. + /// + internal static string Upgrade_all_csharp_projects_to_language_version_0 { + get { + return ResourceManager.GetString("Upgrade_all_csharp_projects_to_language_version_0", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Upgrade this project to C# language version '{0}'. + /// + internal static string Upgrade_this_project_to_csharp_language_version_0 { + get { + return ResourceManager.GetString("Upgrade_this_project_to_csharp_language_version_0", resourceCulture); + } + } + /// /// Looks up a localized string similar to Use explicit type. /// diff --git a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx index d3cad8be21562..0bc16a7cfdd9a 100644 --- a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx +++ b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx @@ -482,6 +482,12 @@ Autoselect disabled due to possible deconstruction declaration. + + Upgrade this project to C# language version '{0}' + + + Upgrade all C# projects to language version '{0}' + <class name> diff --git a/src/Features/CSharp/Portable/UpgradeProject/CSharpUpgradeProjectCodeFixProvider.cs b/src/Features/CSharp/Portable/UpgradeProject/CSharpUpgradeProjectCodeFixProvider.cs new file mode 100644 index 0000000000000..e1df4e147a9bc --- /dev/null +++ b/src/Features/CSharp/Portable/UpgradeProject/CSharpUpgradeProjectCodeFixProvider.cs @@ -0,0 +1,87 @@ +// 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.Generic; +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Threading; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.UpgradeProject; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.UpgradeProject +{ + [ExportCodeFixProvider(LanguageNames.CSharp), Shared] + internal class CSharpUpgradeProjectCodeFixProvider : AbstractUpgradeProjectCodeFixProvider + { + private const string CS8022 = nameof(CS8022); // error CS8022: Feature is not available in C# 1. Please use language version X or greater. + private const string CS8023 = nameof(CS8023); // error CS8023: Feature is not available in C# 2. Please use language version X or greater. + private const string CS8024 = nameof(CS8024); // error CS8024: Feature is not available in C# 3. Please use language version X or greater. + private const string CS8025 = nameof(CS8025); // error CS8025: Feature is not available in C# 4. Please use language version X or greater. + private const string CS8026 = nameof(CS8026); // error CS8026: Feature is not available in C# 5. Please use language version X or greater. + private const string CS8059 = nameof(CS8059); // error CS8059: Feature is not available in C# 6. Please use language version X or greater. + + public override ImmutableArray FixableDiagnosticIds { get; } = + ImmutableArray.Create(CS8022, CS8023, CS8024, CS8025, CS8026, CS8059); + + public override string UpgradeThisProjectResource => CSharpFeaturesResources.Upgrade_this_project_to_csharp_language_version_0; + public override string UpgradeAllProjectsResource => CSharpFeaturesResources.Upgrade_all_csharp_projects_to_language_version_0; + + public override ImmutableArray SuggestedVersions(ImmutableArray diagnostics) + { + var required = RequiredVersion(diagnostics); + var builder = ArrayBuilder.GetInstance(1); + + var generic = required <= LanguageVersion.Default.MapSpecifiedToEffectiveVersion() + ? LanguageVersion.Default // for all versions prior to current Default + : LanguageVersion.Latest; // for more recent versions + + builder.Add(generic.ToDisplayString()); + builder.Add(required.ToDisplayString()); // also suggest the specific required version + + return builder.ToImmutableAndFree(); + } + + private static LanguageVersion RequiredVersion(ImmutableArray diagnostics) + { + LanguageVersion max = 0; + foreach (var diagnostic in diagnostics) + { + if (diagnostic.Properties.TryGetValue(DiagnosticPropertyConstants.RequiredLanguageVersion, out string requiredVersion) && + requiredVersion.TryParse(out var required)) + { + max = max > required ? max : required; + } + } + + return max; + } + + public override Solution UpgradeProject(Project project, string newVersion) + { + var parseOptions = (CSharpParseOptions)project.ParseOptions; + if (IsUpgrade(parseOptions, newVersion)) + { + Contract.ThrowIfFalse(newVersion.TryParse(out var parsedNewVersion)); + return project.Solution.WithProjectParseOptions(project.Id, parseOptions.WithLanguageVersion(parsedNewVersion)); + } + else + { + // when fixing all projects in a solution, don't downgrade those with newer language versions + return project.Solution; + } + } + + public override bool IsUpgrade(ParseOptions projectOptions, string newVersion) + { + var parseOptions = (CSharpParseOptions)projectOptions; + Contract.ThrowIfFalse(newVersion.TryParse(out var parsedNewVersion)); + + // treat equivalent versions (one generic and one specific) to be a valid upgrade + return parsedNewVersion.MapSpecifiedToEffectiveVersion() >= parseOptions.LanguageVersion && + parseOptions.SpecifiedLanguageVersion.ToDisplayString() != newVersion; + } + } +} diff --git a/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs b/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs index 39b97d194b488..a2672a62b64ee 100644 --- a/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs +++ b/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs @@ -35,7 +35,7 @@ public async Task ScanDesignerAttributesAsync(Document // same service run in both inproc and remote host, but remote host will not have RemoteHostClient service, // so inproc one will always run - var client = await workspace.GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); + var client = await workspace.TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); if (client != null && !document.IsOpen()) { // run designer attributes scanner on remote host diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticAnalyzerExecutor.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticAnalyzerExecutor.cs index 6b789a4d9b78c..89627512a2b28 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticAnalyzerExecutor.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticAnalyzerExecutor.cs @@ -65,7 +65,7 @@ public async Task + @@ -729,4 +730,4 @@ - \ No newline at end of file + diff --git a/src/Features/Core/Portable/FindUsages/IDefinitionsAndReferencesFactory.cs b/src/Features/Core/Portable/FindUsages/IDefinitionsAndReferencesFactory.cs index 023e524e7abc2..27922fd1ddd2d 100644 --- a/src/Features/Core/Portable/FindUsages/IDefinitionsAndReferencesFactory.cs +++ b/src/Features/Core/Portable/FindUsages/IDefinitionsAndReferencesFactory.cs @@ -164,6 +164,14 @@ public static DefinitionItem ToDefinitionItem( bool includeHiddenLocations, HashSet uniqueSpans = null) { + // Ensure we're working with the original definition for the symbol. I.e. When we're + // creating definition items, we want to create them for types like Dictionary + // not some random instantiation of that type. + // + // This ensures that the type will both display properly to the user, as well as ensuring + // that we can accurately resolve the type later on when we try to navigate to it. + definition = definition.OriginalDefinition; + var displayParts = definition.ToDisplayParts(GetFormat(definition)).ToTaggedText(); var nameDisplayParts = definition.ToDisplayParts(s_namePartsFormat).ToTaggedText(); diff --git a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs index f624eefe995fd..1c6630641c3ba 100644 --- a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs +++ b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs @@ -309,11 +309,7 @@ private async Task> GetGenerateInNewFileOperati { var syntaxFacts = _document.Document.GetLanguageService(); var fileBanner = syntaxFacts.GetFileBanner(_document.Root); - - if (fileBanner.Any(syntaxFacts.IsRegularComment)) - { - newRoot = newRoot.WithPrependedLeadingTrivia(fileBanner); - } + newRoot = newRoot.WithPrependedLeadingTrivia(fileBanner); } return await CreateAddDocumentAndUpdateUsingsOrImportsOperationsAsync( diff --git a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.Remote.cs b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.Remote.cs index 40f71afa2b598..44250a2ac2a47 100644 --- a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.Remote.cs +++ b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.Remote.cs @@ -45,7 +45,7 @@ private static async Task GetRemoteHostClientAsync(Project pro return null; } - return await project.Solution.Workspace.GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); + return await project.Solution.Workspace.TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); } } } \ No newline at end of file diff --git a/src/Features/Core/Portable/TodoComments/AbstractTodoCommentService.cs b/src/Features/Core/Portable/TodoComments/AbstractTodoCommentService.cs index 516e98cdfe6bc..c3b2d08c2313b 100644 --- a/src/Features/Core/Portable/TodoComments/AbstractTodoCommentService.cs +++ b/src/Features/Core/Portable/TodoComments/AbstractTodoCommentService.cs @@ -26,7 +26,7 @@ public async Task> GetTodoCommentsAsync(Document document, Im { // same service run in both inproc and remote host, but remote host will not have RemoteHostClient service, // so inproc one will always run - var client = await document.Project.Solution.Workspace.GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); + var client = await document.Project.Solution.Workspace.TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); if (client != null && !document.IsOpen()) { // run todo scanner on remote host. diff --git a/src/Features/Core/Portable/UpgradeProject/AbstractUpgradeProjectCodeFixProvider.cs b/src/Features/Core/Portable/UpgradeProject/AbstractUpgradeProjectCodeFixProvider.cs new file mode 100644 index 0000000000000..6773832bc2d23 --- /dev/null +++ b/src/Features/Core/Portable/UpgradeProject/AbstractUpgradeProjectCodeFixProvider.cs @@ -0,0 +1,98 @@ +// 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.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using static Microsoft.CodeAnalysis.CodeActions.CodeAction; + +namespace Microsoft.CodeAnalysis.UpgradeProject +{ + internal abstract partial class AbstractUpgradeProjectCodeFixProvider : CodeFixProvider + { + public abstract ImmutableArray SuggestedVersions(ImmutableArray diagnostics); + public abstract Solution UpgradeProject(Project project, string version); + public abstract bool IsUpgrade(ParseOptions projectOptions, string newVersion); + public abstract string UpgradeThisProjectResource { get; } + public abstract string UpgradeAllProjectsResource { get; } + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + var diagnostics = context.Diagnostics; + + context.RegisterFixes(GetUpgradeProjectCodeActionsAsync(context), diagnostics); + return Task.CompletedTask; + } + + protected ImmutableArray GetUpgradeProjectCodeActionsAsync(CodeFixContext context) + { + var project = context.Document.Project; + var solution = project.Solution; + var newVersions = SuggestedVersions(context.Diagnostics); + var result = new List(); + var language = project.Language; + + foreach (var newVersion in newVersions) + { + var fixOneProjectTitle = string.Format(UpgradeThisProjectResource, newVersion); + + var fixOneProject = new ParseOptionsChangeAction(fixOneProjectTitle, + _ => Task.FromResult(UpgradeProject(project, newVersion))); + + result.Add(fixOneProject); + } + + foreach (var newVersion in newVersions) + { + if (solution.Projects.Count(p => CanUpgrade(p, language, newVersion)) > 1) + { + var fixAllProjectsTitle = string.Format(UpgradeAllProjectsResource, newVersion); + + var fixAllProjects = new ParseOptionsChangeAction(fixAllProjectsTitle, + ct => Task.FromResult(UpgradeAllProjects(solution, language, newVersion, ct))); + + result.Add(fixAllProjects); + } + } + + return result.AsImmutable(); + } + + public Solution UpgradeAllProjects(Solution solution, string language, string version, CancellationToken cancellationToken) + { + var currentSolution = solution; + foreach (var projectId in solution.Projects.Select(p => p.Id)) + { + cancellationToken.ThrowIfCancellationRequested(); + var currentProject = currentSolution.GetProject(projectId); + + if (CanUpgrade(currentProject, language, version)) + { + currentSolution = UpgradeProject(currentProject, version); + } + } + + return currentSolution; + } + + private bool CanUpgrade(Project project, string language, string version) + { + return project.Language == language && IsUpgrade(project.ParseOptions, version); + } + } + + internal class ParseOptionsChangeAction : SolutionChangeAction + { + public ParseOptionsChangeAction(string title, Func> createChangedSolution) + : base(title, createChangedSolution, equivalenceKey: null) + { + } + + protected override Task> ComputePreviewOperationsAsync(CancellationToken cancellationToken) + => Task.FromResult(Enumerable.Empty()); + } +} \ No newline at end of file diff --git a/src/Features/Core/Portable/UseCollectionInitializer/AbstractUseCollectionInitializerCodeFixProvider.cs b/src/Features/Core/Portable/UseCollectionInitializer/AbstractUseCollectionInitializerCodeFixProvider.cs index de7a34b87d079..4711c5a402e84 100644 --- a/src/Features/Core/Portable/UseCollectionInitializer/AbstractUseCollectionInitializerCodeFixProvider.cs +++ b/src/Features/Core/Portable/UseCollectionInitializer/AbstractUseCollectionInitializerCodeFixProvider.cs @@ -50,7 +50,7 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context) return SpecializedTasks.EmptyTask; } - protected override Task FixAllAsync( + protected override async Task FixAllAsync( Document document, ImmutableArray diagnostics, SyntaxEditor editor, CancellationToken cancellationToken) { @@ -76,7 +76,9 @@ protected override Task FixAllAsync( // We're going to be continually editing this tree. Track all the nodes we // care about so we can find them across each edit. - var currentRoot = originalRoot.TrackNodes(originalObjectCreationNodes); + document = document.WithSyntaxRoot(originalRoot.TrackNodes(originalObjectCreationNodes)); + var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var currentRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); while (originalObjectCreationNodes.Count > 0) { @@ -84,7 +86,7 @@ protected override Task FixAllAsync( var objectCreation = currentRoot.GetCurrentNodes(originalObjectCreation).Single(); var analyzer = new ObjectCreationExpressionAnalyzer( - syntaxFacts, objectCreation); + semanticModel, syntaxFacts, objectCreation, cancellationToken); var matches = analyzer.Analyze(); if (matches == null || matches.Value.Length == 0) { @@ -103,11 +105,12 @@ protected override Task FixAllAsync( subEditor.RemoveNode(match); } - currentRoot = subEditor.GetChangedRoot(); + document = document.WithSyntaxRoot(subEditor.GetChangedRoot()); + semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + currentRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); } editor.ReplaceNode(originalRoot, currentRoot); - return SpecializedTasks.EmptyTask; } protected abstract TStatementSyntax GetNewStatement( diff --git a/src/Features/Core/Portable/UseCollectionInitializer/AbstractUseCollectionInitializerDiagnosticAnalyzer.cs b/src/Features/Core/Portable/UseCollectionInitializer/AbstractUseCollectionInitializerDiagnosticAnalyzer.cs index f445c79c931f4..73540a1f13c71 100644 --- a/src/Features/Core/Portable/UseCollectionInitializer/AbstractUseCollectionInitializerDiagnosticAnalyzer.cs +++ b/src/Features/Core/Portable/UseCollectionInitializer/AbstractUseCollectionInitializerDiagnosticAnalyzer.cs @@ -63,10 +63,12 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context, INamedTypeSymbol ien return; } + var semanticModel = context.SemanticModel; var objectCreationExpression = (TObjectCreationExpressionSyntax)context.Node; var language = objectCreationExpression.Language; var syntaxTree = objectCreationExpression.SyntaxTree; var cancellationToken = context.CancellationToken; + var optionSet = context.Options.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult(); if (optionSet == null) { @@ -89,7 +91,7 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context, INamedTypeSymbol ien } var analyzer = new ObjectCreationExpressionAnalyzer( - GetSyntaxFactsService(), objectCreationExpression); + semanticModel, GetSyntaxFactsService(), objectCreationExpression, cancellationToken); var matches = analyzer.Analyze(); if (matches == null || matches.Value.Length == 0) { diff --git a/src/Features/Core/Portable/UseCollectionInitializer/ObjectCreationExpressionAnalyzer.cs b/src/Features/Core/Portable/UseCollectionInitializer/ObjectCreationExpressionAnalyzer.cs index 463e99aac9bdd..20ded5dbafd1c 100644 --- a/src/Features/Core/Portable/UseCollectionInitializer/ObjectCreationExpressionAnalyzer.cs +++ b/src/Features/Core/Portable/UseCollectionInitializer/ObjectCreationExpressionAnalyzer.cs @@ -2,7 +2,10 @@ using System.Collections; using System.Collections.Immutable; +using System.Linq; +using System.Threading; using Microsoft.CodeAnalysis.LanguageServices; +using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.UseCollectionInitializer { @@ -22,18 +25,25 @@ internal struct ObjectCreationExpressionAnalyzer< where TExpressionStatementSyntax : TStatementSyntax where TVariableDeclaratorSyntax : SyntaxNode { + private readonly SemanticModel _semanticModel; private readonly ISyntaxFactsService _syntaxFacts; private readonly TObjectCreationExpressionSyntax _objectCreationExpression; + private readonly CancellationToken _cancellationToken; private TStatementSyntax _containingStatement; private SyntaxNodeOrToken _valuePattern; + private ISymbol _variableSymbol; public ObjectCreationExpressionAnalyzer( + SemanticModel semanticModel, ISyntaxFactsService syntaxFacts, - TObjectCreationExpressionSyntax objectCreationExpression) : this() + TObjectCreationExpressionSyntax objectCreationExpression, + CancellationToken cancellationToken) : this() { + _semanticModel = semanticModel; _syntaxFacts = syntaxFacts; _objectCreationExpression = objectCreationExpression; + _cancellationToken = cancellationToken; } internal ImmutableArray? Analyze() @@ -146,6 +156,21 @@ private bool TryAnalyzeIndexAssignment( return false; } + // If we're initializing a variable, then we can't reference that variable on the right + // side of the initialization. Rewriting this into a collection initializer would lead + // to a definite-assignment error. + if (_variableSymbol != null) + { + foreach (var child in right.DescendantNodesAndSelf().OfType()) + { + if (ValuePatternMatches(child) && + _variableSymbol.Equals(_semanticModel.GetSymbolInfo(child, _cancellationToken).GetAnySymbol())) + { + return false; + } + } + } + instance = _syntaxFacts.GetExpressionOfElementAccessExpression(left); return true; } @@ -228,6 +253,14 @@ private bool TryInitializeAssignmentCase() return false; } + var typeInfo = _semanticModel.GetTypeInfo(left, _cancellationToken); + if (typeInfo.Type is IDynamicTypeSymbol || typeInfo.ConvertedType is IDynamicTypeSymbol) + { + // Not supported if we're initializing something dynamic. The object we're instantiating + // may not have the members that we're trying to access on the dynamic object. + return false; + } + _valuePattern = left; return true; } @@ -245,12 +278,22 @@ private bool TryInitializeVariableDeclarationCase() return false; } + var symbol = _semanticModel.GetDeclaredSymbol(containingDeclarator, _cancellationToken); + if (symbol is ILocalSymbol local && + local.Type is IDynamicTypeSymbol) + { + // Not supported if we're creating a dynamic local. The object we're instantiating + // may not have the members that we're trying to access on the dynamic object. + return false; + } + if (!_syntaxFacts.IsDeclaratorOfLocalDeclarationStatement(containingDeclarator, _containingStatement)) { return false; } _valuePattern = _syntaxFacts.GetIdentifierOfVariableDeclarator(containingDeclarator); + _variableSymbol = _semanticModel.GetDeclaredSymbol(containingDeclarator); return true; } } diff --git a/src/Features/Core/Portable/UseObjectInitializer/AbstractUseObjectInitializerCodeFixProvider.cs b/src/Features/Core/Portable/UseObjectInitializer/AbstractUseObjectInitializerCodeFixProvider.cs index 18437df8d3bb1..bb8fa328ffdab 100644 --- a/src/Features/Core/Portable/UseObjectInitializer/AbstractUseObjectInitializerCodeFixProvider.cs +++ b/src/Features/Core/Portable/UseObjectInitializer/AbstractUseObjectInitializerCodeFixProvider.cs @@ -48,7 +48,7 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context) return SpecializedTasks.EmptyTask; } - protected override Task FixAllAsync( + protected override async Task FixAllAsync( Document document, ImmutableArray diagnostics, SyntaxEditor editor, CancellationToken cancellationToken) { @@ -74,7 +74,10 @@ protected override Task FixAllAsync( // We're going to be continually editing this tree. Track all the nodes we // care about so we can find them across each edit. - var currentRoot = originalRoot.TrackNodes(originalObjectCreationNodes); + document = document.WithSyntaxRoot(originalRoot.TrackNodes(originalObjectCreationNodes)); + + var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var currentRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); while (originalObjectCreationNodes.Count > 0) { @@ -82,7 +85,7 @@ protected override Task FixAllAsync( var objectCreation = currentRoot.GetCurrentNodes(originalObjectCreation).Single(); var analyzer = new ObjectCreationExpressionAnalyzer( - syntaxFacts, objectCreation); + semanticModel, syntaxFacts, objectCreation, cancellationToken); var matches = analyzer.Analyze(); if (matches == null || matches.Value.Length == 0) @@ -102,11 +105,12 @@ protected override Task FixAllAsync( subEditor.RemoveNode(match.Statement); } - currentRoot = subEditor.GetChangedRoot(); + document = document.WithSyntaxRoot(subEditor.GetChangedRoot()); + semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + currentRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); } editor.ReplaceNode(editor.OriginalRoot, currentRoot); - return SpecializedTasks.EmptyTask; } protected abstract TStatementSyntax GetNewStatement( diff --git a/src/Features/Core/Portable/UseObjectInitializer/AbstractUseObjectInitializerDiagnosticAnalyzer.cs b/src/Features/Core/Portable/UseObjectInitializer/AbstractUseObjectInitializerDiagnosticAnalyzer.cs index 5039c02b1c6b9..2edb99d4e2470 100644 --- a/src/Features/Core/Portable/UseObjectInitializer/AbstractUseObjectInitializerDiagnosticAnalyzer.cs +++ b/src/Features/Core/Portable/UseObjectInitializer/AbstractUseObjectInitializerDiagnosticAnalyzer.cs @@ -70,7 +70,7 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context) var syntaxFacts = GetSyntaxFactsService(); var analyzer = new ObjectCreationExpressionAnalyzer( - syntaxFacts, objectCreationExpression); + context.SemanticModel, syntaxFacts, objectCreationExpression, context.CancellationToken); var result = analyzer.Analyze(); if (result == null || result.Value.Length == 0) diff --git a/src/Features/Core/Portable/UseObjectInitializer/ObjectCreationExpressionAnalyzer.cs b/src/Features/Core/Portable/UseObjectInitializer/ObjectCreationExpressionAnalyzer.cs index efbfd83047821..4e6236cfb76b0 100644 --- a/src/Features/Core/Portable/UseObjectInitializer/ObjectCreationExpressionAnalyzer.cs +++ b/src/Features/Core/Portable/UseObjectInitializer/ObjectCreationExpressionAnalyzer.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Threading; using Microsoft.CodeAnalysis.LanguageServices; namespace Microsoft.CodeAnalysis.UseObjectInitializer @@ -26,13 +27,19 @@ internal struct ObjectCreationExpressionAnalyzer< private TStatementSyntax _containingStatement; private SyntaxNodeOrToken _valuePattern; + private readonly SemanticModel _semanticModel; + private readonly CancellationToken _cancellationToken; public ObjectCreationExpressionAnalyzer( + SemanticModel semanticModel, ISyntaxFactsService syntaxFacts, - TObjectCreationExpressionSyntax objectCreationExpression) : this() + TObjectCreationExpressionSyntax objectCreationExpression, + CancellationToken cancellationToken) : this() { + _semanticModel = semanticModel; _syntaxFacts = syntaxFacts; _objectCreationExpression = objectCreationExpression; + _cancellationToken = cancellationToken; } internal ImmutableArray>? Analyze() @@ -237,6 +244,14 @@ private bool TryInitializeAssignmentCase() return false; } + var typeInfo = _semanticModel.GetTypeInfo(left, _cancellationToken); + if (typeInfo.Type is IDynamicTypeSymbol || typeInfo.ConvertedType is IDynamicTypeSymbol) + { + // Not supported if we're initializing something dynamic. The object we're instantiating + // may not have the members that we're trying to access on the dynamic object. + return false; + } + _valuePattern = left; return true; } @@ -254,6 +269,15 @@ private bool TryInitializeVariableDeclarationCase() return false; } + var symbol = _semanticModel.GetDeclaredSymbol(containingDeclarator, _cancellationToken); + if (symbol is ILocalSymbol local && + local.Type is IDynamicTypeSymbol) + { + // Not supported if we're creating a dynamic local. The object we're instantiating + // may not have the members that we're trying to access on the dynamic object. + return false; + } + if (!_syntaxFacts.IsDeclaratorOfLocalDeclarationStatement(containingDeclarator, _containingStatement)) { return false; diff --git a/src/Features/VisualBasic/Portable/MakeMethodAsynchronous/VisualBasicMakeMethodAsynchronousCodeFixProvider.vb b/src/Features/VisualBasic/Portable/MakeMethodAsynchronous/VisualBasicMakeMethodAsynchronousCodeFixProvider.vb index ae917aee388df..cdb6ebfa661c4 100644 --- a/src/Features/VisualBasic/Portable/MakeMethodAsynchronous/VisualBasicMakeMethodAsynchronousCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/MakeMethodAsynchronous/VisualBasicMakeMethodAsynchronousCodeFixProvider.vb @@ -91,16 +91,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.MakeMethodAsynchronous ' Have to convert this sub into a func. Dim subStatement = node.SubOrFunctionStatement - Dim asClause = SyntaxFactory.SimpleAsClause(taskType.GenerateTypeSyntax()). - WithTrailingTrivia(subStatement.ParameterList.GetTrailingTrivia()) + Dim asClause = + SyntaxFactory.SimpleAsClause(taskType.GenerateTypeSyntax()). + WithTrailingTrivia( + If(subStatement.ParameterList?.GetTrailingTrivia(), + subStatement.GetTrailingTrivia())) Dim functionStatement = SyntaxFactory.FunctionStatement( subStatement.AttributeLists, subStatement.Modifiers.Add(s_asyncToken), SyntaxFactory.Token(SyntaxKind.FunctionKeyword).WithTriviaFrom(subStatement.SubOrFunctionKeyword), - subStatement.Identifier, - subStatement.TypeParameterList, - subStatement.ParameterList.WithoutTrailingTrivia(), + subStatement.Identifier.WithTrailingTrivia(), + subStatement.TypeParameterList?.WithoutTrailingTrivia(), + subStatement.ParameterList?.WithoutTrailingTrivia(), asClause, subStatement.HandlesClause, subStatement.ImplementsClause) diff --git a/src/Features/VisualBasic/Portable/ReplaceMethodWithProperty/VisualBasicReplaceMethodWithPropertyService.vb b/src/Features/VisualBasic/Portable/ReplaceMethodWithProperty/VisualBasicReplaceMethodWithPropertyService.vb index 1fa8b4890be97..a28363beaff80 100644 --- a/src/Features/VisualBasic/Portable/ReplaceMethodWithProperty/VisualBasicReplaceMethodWithPropertyService.vb +++ b/src/Features/VisualBasic/Portable/ReplaceMethodWithProperty/VisualBasicReplaceMethodWithPropertyService.vb @@ -39,8 +39,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP Return Nothing End If - If position > containingMethod.ParameterList.Span.End Then - Return Nothing + ' Parameter lists in VB are optional and may not be provided. + If containingMethod.ParameterList Is Nothing Then + If position > containingMethod.Span.End Then + Return Nothing + End If + Else + If position > containingMethod.ParameterList.Span.End Then + Return Nothing + End If End If Return containingMethod diff --git a/src/Scripting/CSharpTest/InteractiveSessionTests.cs b/src/Scripting/CSharpTest/InteractiveSessionTests.cs index 61c61df7bd1b3..ff43b9b45bfd8 100644 --- a/src/Scripting/CSharpTest/InteractiveSessionTests.cs +++ b/src/Scripting/CSharpTest/InteractiveSessionTests.cs @@ -34,7 +34,7 @@ public class InteractiveSessionTests : TestBase #region Namespaces, Types - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/17869")] public async Task CompilationChain_NestedTypesClass() { var script = CSharpScript.Create(@" @@ -59,7 +59,7 @@ class InnerClass } } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/17869")] public async Task CompilationChain_NestedTypesStruct() { var script = CSharpScript.Create(@" diff --git a/src/Scripting/CSharpTest/ScriptTests.cs b/src/Scripting/CSharpTest/ScriptTests.cs index 479948b6d07e0..df24c3924da74 100644 --- a/src/Scripting/CSharpTest/ScriptTests.cs +++ b/src/Scripting/CSharpTest/ScriptTests.cs @@ -178,6 +178,82 @@ public void Do() , ScriptOptions.Default.WithReferences(MscorlibRef, SystemRef, SystemCoreRef, CSharpRef)); } + [WorkItem(6676, "https://github.com/dotnet/roslyn/issues/6676")] + [Fact] + public void TestRunEmbeddedStatementNotFollowedBySemicolon() + { + var exceptionThrown = false; + + try + { + var state = CSharpScript.RunAsync(@"if (true) + System.Console.WriteLine(true)", globals: new ScriptTests()); + } + catch (CompilationErrorException ex) + { + exceptionThrown = true; + ex.Diagnostics.Verify( + // (2,32): error CS1002: ; expected + // System.Console.WriteLine(true) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(2, 32)); + } + + Assert.True(exceptionThrown); + } + + [WorkItem(6676, "https://github.com/dotnet/roslyn/issues/6676")] + [Fact] + public void TestRunEmbeddedStatementFollowedBySemicolon() + { + var state = CSharpScript.RunAsync(@"if (true) +System.Console.WriteLine(true);", globals: new ScriptTests()); + Assert.Null(state.Exception); + } + + [WorkItem(6676, "https://github.com/dotnet/roslyn/issues/6676")] + [Fact] + public void TestRunStatementFollowedBySpace() + { + var state = CSharpScript.RunAsync(@"System.Console.WriteLine(true) ", globals: new ScriptTests()); + Assert.Null(state.Exception); + } + + [WorkItem(6676, "https://github.com/dotnet/roslyn/issues/6676")] + [Fact] + public void TestRunStatementFollowedByNewLineNoSemicolon() + { + var state = CSharpScript.RunAsync(@" +System.Console.WriteLine(true) + +", globals: new ScriptTests()); + Assert.Null(state.Exception); + } + + [WorkItem(6676, "https://github.com/dotnet/roslyn/issues/6676")] + [Fact] + public void TestRunEmbeddedNoSemicolonFollowedByAnotherStatement() + { + var exceptionThrown = false; + + try + { + var state = CSharpScript.RunAsync(@"if (e) a = b +throw e;", globals: new ScriptTests()); + } + catch (CompilationErrorException ex) + { + exceptionThrown = true; + // Verify that it produces a single ExpectedSemicolon error. + // No duplicates for the same error. + ex.Diagnostics.Verify( + // (1,13): error CS1002: ; expected + // if (e) a = b + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 13)); + } + + Assert.True(exceptionThrown); + } + [Fact] public async Task TestRunScriptWithGlobals() { diff --git a/src/Setup/DevDivInsertionFiles/BuildDevDivInsertionFiles.vb b/src/Setup/DevDivInsertionFiles/BuildDevDivInsertionFiles.vb index c2a1678cc372c..c87871539325a 100644 --- a/src/Setup/DevDivInsertionFiles/BuildDevDivInsertionFiles.vb +++ b/src/Setup/DevDivInsertionFiles/BuildDevDivInsertionFiles.vb @@ -323,6 +323,7 @@ Public Class BuildDevDivInsertionFiles "Microsoft.CodeAnalysis.VisualBasic.Workspaces.dll", "Microsoft.CodeAnalysis.Workspaces.dll", "Microsoft.DiaSymReader.dll", + "Microsoft.DiaSymReader.Converter.Xml.dll", "Microsoft.DiaSymReader.Native.amd64.dll", "Microsoft.DiaSymReader.Native.x86.dll", "Microsoft.DiaSymReader.PortablePdb.dll", @@ -832,7 +833,6 @@ Public Class BuildDevDivInsertionFiles add("Exes\VBCSCompiler\VBCSCompiler.exe.config") add("Exes\InteractiveHost\InteractiveHost.exe.config") add("Exes\csi\csi.rsp") - add("Exes\Pdb2Xml\Microsoft.DiaSymReader.PortablePdb.dll") add("Vsix\Roslyn.Deployment.Full.Next\remoteSymbolSearchUpdateEngine.servicehub.service.json") add("Vsix\Roslyn.Deployment.Full.Next\snapshotService.servicehub.service.json") add("Vsix\VisualStudioInteractiveComponents\CSharpInteractive.rsp") @@ -860,6 +860,8 @@ Public Class BuildDevDivInsertionFiles add("UnitTests\EditorServicesTest\Esent.Interop.dll") add("UnitTests\EditorServicesTest\Moq.dll") add("UnitTests\EditorServicesTest\Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll") + add("UnitTests\EditorServicesTest\Microsoft.DiaSymReader.PortablePdb.dll") + add("UnitTests\EditorServicesTest\Microsoft.DiaSymReader.Converter.Xml.dll") add("UnitTests\EditorServicesTest\Microsoft.DiaSymReader.dll") add("UnitTests\EditorServicesTest\Microsoft.DiaSymReader.Native.amd64.dll") add("UnitTests\EditorServicesTest\Microsoft.DiaSymReader.Native.x86.dll") diff --git a/src/Setup/Templates/project.json b/src/Setup/Templates/project.json index 6b9683b1e1590..3ae9e64d4472f 100644 --- a/src/Setup/Templates/project.json +++ b/src/Setup/Templates/project.json @@ -14,7 +14,7 @@ "System.Collections.Immutable": "1.1.36", "System.Reflection.Metadata": "1.0.21", "Microsoft.VisualStudio.TemplateWizardInterface": "8.0.0.0-alpha", - "VSLangProj": "7.0.3300-alpha" + "VSLangProj": "7.0.3300" }, "frameworks": { "net46": { } diff --git a/src/Test/PdbUtilities/Pdb/ISymUnmanagedSourceServerModule.cs b/src/Test/PdbUtilities/Pdb/ISymUnmanagedSourceServerModule.cs deleted file mode 100644 index 1ba371a063d5a..0000000000000 --- a/src/Test/PdbUtilities/Pdb/ISymUnmanagedSourceServerModule.cs +++ /dev/null @@ -1,19 +0,0 @@ -// 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.Runtime.InteropServices; - -namespace Roslyn.Test.PdbUtilities -{ - [ComImport] - [Guid("997DD0CC-A76F-4c82-8D79-EA87559D27AD")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - [ComVisible(false)] - public interface ISymUnmanagedSourceServerModule - { - // returns the source server data for the module - // caller must free using CoTaskMemFree() - [PreserveSig] - unsafe int GetSourceServerData(out int pDataByteCount, out byte* ppData); - } -} diff --git a/src/Test/PdbUtilities/Pdb/PdbToXml.cs b/src/Test/PdbUtilities/Pdb/PdbToXml.cs deleted file mode 100644 index c07625a72c670..0000000000000 --- a/src/Test/PdbUtilities/Pdb/PdbToXml.cs +++ /dev/null @@ -1,1626 +0,0 @@ -// 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.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Reflection.Metadata; -using System.Reflection.Metadata.Ecma335; -using System.Reflection.PortableExecutable; -using System.Runtime.InteropServices; -using System.Text; -using System.Xml; -using Microsoft.CodeAnalysis.Debugging; -using Microsoft.DiaSymReader; -using Roslyn.Utilities; - -using PooledStringBuilder = Microsoft.CodeAnalysis.Collections.PooledStringBuilder; - -namespace Roslyn.Test.PdbUtilities -{ - /// - /// Class to write out XML for a PDB. - /// - public sealed class PdbToXmlConverter - { - // For printing integers in a standard hex format. - private const string IntHexFormat = "0x{0:X}"; - - private readonly MetadataReader _metadataReader; - private readonly ISymUnmanagedReader3 _symReader; - private readonly PdbToXmlOptions _options; - private readonly XmlWriter _writer; - - private static readonly XmlWriterSettings s_xmlWriterSettings = new XmlWriterSettings - { - Encoding = Encoding.UTF8, - Indent = true, - IndentChars = " ", - NewLineChars = "\r\n", - }; - - private PdbToXmlConverter(XmlWriter writer, ISymUnmanagedReader3 symReader, MetadataReader metadataReader, PdbToXmlOptions options) - { - _symReader = symReader; - _metadataReader = metadataReader; - _writer = writer; - _options = options; - } - - public static string DeltaPdbToXml(Stream deltaPdb, IEnumerable methodTokens) - { - var writer = new StringWriter(); - ToXml( - writer, - deltaPdb, - metadataReaderOpt: null, - options: PdbToXmlOptions.IncludeTokens, - methodHandles: methodTokens.Select(token => (MethodDefinitionHandle)MetadataTokens.Handle(token))); - - return writer.ToString(); - } - - public static string ToXml(Stream pdbStream, Stream peStream, PdbToXmlOptions options = PdbToXmlOptions.ResolveTokens, string methodName = null) - { - var writer = new StringWriter(); - ToXml(writer, pdbStream, peStream, options, methodName); - return writer.ToString(); - } - - public static string ToXml(Stream pdbStream, byte[] peImage, PdbToXmlOptions options = PdbToXmlOptions.ResolveTokens, string methodName = null) - { - var writer = new StringWriter(); - ToXml(writer, pdbStream, new MemoryStream(peImage), options, methodName); - return writer.ToString(); - } - - public static void ToXml(TextWriter xmlWriter, Stream pdbStream, Stream peStream, PdbToXmlOptions options = PdbToXmlOptions.Default, string methodName = null) - { - IEnumerable methodHandles; - - using (var peReader = new PEReader(peStream, PEStreamOptions.LeaveOpen)) - { - var metadataReader = peReader.GetMetadataReader(); - - if (string.IsNullOrEmpty(methodName)) - { - methodHandles = metadataReader.MethodDefinitions; - } - else - { - var matching = metadataReader.MethodDefinitions. - Where(methodHandle => GetQualifiedMethodName(metadataReader, methodHandle) == methodName).ToArray(); - - if (matching.Length == 0) - { - xmlWriter.WriteLine(""); - xmlWriter.WriteLine($""); - xmlWriter.WriteLine(""); - - foreach (var methodHandle in metadataReader.MethodDefinitions) - { - xmlWriter.Write(""); - xmlWriter.WriteLine(); - } - - xmlWriter.WriteLine(""); - xmlWriter.WriteLine(""); - - return; - } - - methodHandles = matching; - } - - ToXml(xmlWriter, pdbStream, metadataReader, options, methodHandles); - } - } - - /// - /// Load the PDB given the parameters at the ctor and spew it out to the XmlWriter specified - /// at the ctor. - /// - private static void ToXml(TextWriter xmlWriter, Stream pdbStream, MetadataReader metadataReaderOpt, PdbToXmlOptions options, IEnumerable methodHandles) - { - Debug.Assert(pdbStream != null); - Debug.Assert((options & PdbToXmlOptions.ResolveTokens) == 0 || metadataReaderOpt != null); - - using (var writer = XmlWriter.Create(xmlWriter, s_xmlWriterSettings)) - { - // metadata reader is on stack -> no owner needed - var symReader = SymReaderFactory.CreateReader(pdbStream, metadataReaderOpt, metadataMemoryOwnerOpt: null); - - try - { - var converter = new PdbToXmlConverter(writer, symReader, metadataReaderOpt, options); - converter.WriteRoot(methodHandles ?? metadataReaderOpt.MethodDefinitions); - } - finally - { - ((ISymUnmanagedDispose)symReader).Destroy(); - } - } - } - - private void WriteRoot(IEnumerable methodHandles) - { - _writer.WriteStartDocument(); - _writer.WriteStartElement("symbols"); - - var documents = _symReader.GetDocuments(); - var documentIndex = BuildDocumentIndex(documents); - var methodTokenMap = BuildMethodTokenMap(); - - if ((_options & PdbToXmlOptions.ExcludeDocuments) == 0) - { - WriteDocuments(documents, documentIndex); - } - - if ((_options & PdbToXmlOptions.ExcludeMethods) == 0) - { - WriteEntryPoint(); - WriteAllMethods(methodHandles, methodTokenMap, documentIndex); - WriteAllMethodSpans(); - } - - if ((_options & PdbToXmlOptions.IncludeSourceServerInformation) != 0) - { - WriteSourceServerInformation(); - } - - _writer.WriteEndElement(); - } - - private void WriteAllMethods(IEnumerable methodHandles, ImmutableArray tokenMap, IReadOnlyDictionary documentIndex) - { - _writer.WriteStartElement("methods"); - - foreach (var methodHandle in methodHandles) - { - WriteMethod(methodHandle, tokenMap, documentIndex); - } - - _writer.WriteEndElement(); - } - - private void WriteMethod(MethodDefinitionHandle methodHandle, ImmutableArray tokenMap, IReadOnlyDictionary documentIndex) - { - int token = _metadataReader.GetToken(methodHandle); - ISymUnmanagedMethod method = _symReader.GetMethod(token); - - byte[] cdi = null; - var sequencePoints = ImmutableArray.Empty; - ISymUnmanagedAsyncMethod asyncMethod = null; - ISymUnmanagedScope rootScope = null; - - if ((_options & PdbToXmlOptions.ExcludeCustomDebugInformation) == 0) - { - cdi = _symReader.GetCustomDebugInfoBytes(token, methodVersion: 1); - } - - if (method != null) - { - if ((_options & PdbToXmlOptions.ExcludeAsyncInfo) == 0) - { - asyncMethod = method.AsAsyncMethod(); - } - - if ((_options & PdbToXmlOptions.ExcludeSequencePoints) == 0) - { - sequencePoints = method.GetSequencePoints().ToImmutableArray(); - } - - if ((_options & PdbToXmlOptions.ExcludeScopes) == 0) - { - rootScope = method.GetRootScope(); - } - } - - if (cdi == null && sequencePoints.IsEmpty && rootScope == null && asyncMethod == null) - { - // no debug info to write - return; - } - - _writer.WriteStartElement("method"); - - int methodRowId = MetadataTokens.GetRowNumber(methodHandle); - int tokenToDisplay = (methodRowId <= tokenMap.Length) ? MetadataTokens.GetToken(tokenMap[methodRowId - 1]) : token; - - WriteMethodAttributes(tokenToDisplay, isReference: false); - - if (cdi != null) - { - WriteCustomDebugInfo(cdi); - } - - if (!sequencePoints.IsEmpty) - { - WriteSequencePoints(sequencePoints, documentIndex); - } - - if (rootScope != null) - { - WriteScopes(rootScope); - } - - if (asyncMethod != null) - { - WriteAsyncInfo(asyncMethod); - } - - _writer.WriteEndElement(); - } - - /// - /// Given a byte array of custom debug info, parse the array and write out XML describing - /// its structure and contents. - /// - private void WriteCustomDebugInfo(byte[] bytes) - { - var records = CustomDebugInfoReader.GetCustomDebugInfoRecords(bytes).ToArray(); - - _writer.WriteStartElement("customDebugInfo"); - - foreach (var record in records) - { - if (record.Version != CustomDebugInfoConstants.Version) - { - WriteUnknownCustomDebugInfo(record); - } - else - { - switch (record.Kind) - { - case CustomDebugInfoKind.UsingGroups: - WriteUsingCustomDebugInfo(record); - break; - case CustomDebugInfoKind.ForwardMethodInfo: - WriteForwardCustomDebugInfo(record); - break; - case CustomDebugInfoKind.ForwardModuleInfo: - WriteForwardToModuleCustomDebugInfo(record); - break; - case CustomDebugInfoKind.StateMachineHoistedLocalScopes: - WriteStateMachineHoistedLocalScopesCustomDebugInfo(record); - break; - case CustomDebugInfoKind.StateMachineTypeName: - WriteForwardIteratorCustomDebugInfo(record); - break; - case CustomDebugInfoKind.DynamicLocals: - WriteDynamicLocalsCustomDebugInfo(record); - break; - case CustomDebugInfoKind.EditAndContinueLocalSlotMap: - WriteEditAndContinueLocalSlotMap(record); - break; - case CustomDebugInfoKind.EditAndContinueLambdaMap: - WriteEditAndContinueLambdaMap(record); - break; - case CustomDebugInfoKind.TupleElementNames: - WriteTupleElementNamesCustomDebugInfo(record); - break; - default: - WriteUnknownCustomDebugInfo(record); - break; - } - } - } - - _writer.WriteEndElement(); //customDebugInfo - } - - /// - /// If the custom debug info is in a format that we don't understand, then we will - /// just print a standard record header followed by the rest of the record as a - /// single hex string. - /// - private void WriteUnknownCustomDebugInfo(CustomDebugInfoRecord record) - { - _writer.WriteStartElement("unknown"); - _writer.WriteAttributeString("kind", record.Kind.ToString()); - _writer.WriteAttributeString("version", CultureInvariantToString(record.Version)); - - PooledStringBuilder pooled = PooledStringBuilder.GetInstance(); - StringBuilder builder = pooled.Builder; - foreach (byte b in record.Data) - { - builder.AppendFormat("{0:X2}", b); - } - - _writer.WriteAttributeString("payload", pooled.ToStringAndFree()); - - _writer.WriteEndElement(); //unknown - } - - /// - /// For each namespace declaration enclosing a method (innermost-to-outermost), there is a count - /// of the number of imports in that declaration. - /// - /// - /// There's always at least one entry (for the global namespace). - /// - private void WriteUsingCustomDebugInfo(CustomDebugInfoRecord record) - { - Debug.Assert(record.Kind == CustomDebugInfoKind.UsingGroups); - - _writer.WriteStartElement("using"); - - ImmutableArray counts = CustomDebugInfoReader.DecodeUsingRecord(record.Data); - - foreach (short importCount in counts) - { - _writer.WriteStartElement("namespace"); - _writer.WriteAttributeString("usingCount", CultureInvariantToString(importCount)); - _writer.WriteEndElement(); //namespace - } - - _writer.WriteEndElement(); //using - } - - /// - /// This indicates that further information can be obtained by looking at the custom debug - /// info of another method (specified by token). - /// - /// - /// Emitting tokens makes tests more fragile. - /// - private void WriteForwardCustomDebugInfo(CustomDebugInfoRecord record) - { - Debug.Assert(record.Kind == CustomDebugInfoKind.ForwardMethodInfo); - - _writer.WriteStartElement("forward"); - - int token = CustomDebugInfoReader.DecodeForwardRecord(record.Data); - WriteMethodAttributes(token, isReference: true); - - _writer.WriteEndElement(); //forward - } - - /// - /// This indicates that further information can be obtained by looking at the custom debug - /// info of another method (specified by token). - /// - /// - /// Appears when there are extern aliases and edit-and-continue is disabled. - /// Emitting tokens makes tests more fragile. - /// - private void WriteForwardToModuleCustomDebugInfo(CustomDebugInfoRecord record) - { - Debug.Assert(record.Kind == CustomDebugInfoKind.ForwardModuleInfo); - - _writer.WriteStartElement("forwardToModule"); - - int token = CustomDebugInfoReader.DecodeForwardRecord(record.Data); - WriteMethodAttributes(token, isReference: true); - - _writer.WriteEndElement(); //forwardToModule - } - - /// - /// Appears when iterator locals have to lifted into fields. Contains a list of buckets with - /// start and end offsets (presumably, into IL). - /// TODO: comment when the structure is understood. - /// - /// - /// Appears when there are locals in iterator methods. - /// - private void WriteStateMachineHoistedLocalScopesCustomDebugInfo(CustomDebugInfoRecord record) - { - Debug.Assert(record.Kind == CustomDebugInfoKind.StateMachineHoistedLocalScopes); - - _writer.WriteStartElement("hoistedLocalScopes"); - - var scopes = CustomDebugInfoReader.DecodeStateMachineHoistedLocalScopesRecord(record.Data); - - foreach (StateMachineHoistedLocalScope scope in scopes) - { - _writer.WriteStartElement("slot"); - - if (!scope.IsDefault) - { - _writer.WriteAttributeString("startOffset", AsILOffset(scope.StartOffset)); - _writer.WriteAttributeString("endOffset", AsILOffset(scope.EndOffset)); - } - - _writer.WriteEndElement(); //slot - } - - _writer.WriteEndElement(); - } - - /// - /// Contains a name string. - /// TODO: comment when the structure is understood. - /// - /// - /// Appears when are iterator methods. - /// - private void WriteForwardIteratorCustomDebugInfo(CustomDebugInfoRecord record) - { - Debug.Assert(record.Kind == CustomDebugInfoKind.StateMachineTypeName); - - _writer.WriteStartElement("forwardIterator"); - - string name = CustomDebugInfoReader.DecodeForwardIteratorRecord(record.Data); - - _writer.WriteAttributeString("name", name); - - _writer.WriteEndElement(); //forwardIterator - } - - /// - /// Contains a list of buckets, each of which contains a number of flags, a slot ID, and a name. - /// TODO: comment when the structure is understood. - /// - /// - /// Appears when there are dynamic locals. - /// - private void WriteDynamicLocalsCustomDebugInfo(CustomDebugInfoRecord record) - { - Debug.Assert(record.Kind == CustomDebugInfoKind.DynamicLocals); - - _writer.WriteStartElement("dynamicLocals"); - - var dynamicLocals = CustomDebugInfoReader.DecodeDynamicLocalsRecord(record.Data); - - foreach (DynamicLocalInfo dynamicLocal in dynamicLocals) - { - ulong flags = dynamicLocal.Flags; - int flagCount = dynamicLocal.FlagCount; - - PooledStringBuilder pooled = PooledStringBuilder.GetInstance(); - StringBuilder flagsBuilder = pooled.Builder; - for (int f = 0; f < flagCount; f++) - { - flagsBuilder.Append((flags >> f) & 1UL); - } - - _writer.WriteStartElement("bucket"); - _writer.WriteAttributeString("flagCount", CultureInvariantToString(flagCount)); - _writer.WriteAttributeString("flags", pooled.ToStringAndFree()); - _writer.WriteAttributeString("slotId", CultureInvariantToString(dynamicLocal.SlotId)); - _writer.WriteAttributeString("localName", dynamicLocal.LocalName); - _writer.WriteEndElement(); //bucket - } - - _writer.WriteEndElement(); //dynamicLocals - } - - private void WriteTupleElementNamesCustomDebugInfo(CustomDebugInfoRecord record) - { - Debug.Assert(record.Kind == CustomDebugInfoKind.TupleElementNames); - - _writer.WriteStartElement("tupleElementNames"); - - var tuples = CustomDebugInfoReader.DecodeTupleElementNamesRecord(record.Data); - - foreach (var tuple in tuples) - { - _writer.WriteStartElement("local"); - _writer.WriteAttributeString("elementNames", JoinNames(tuple.ElementNames)); - _writer.WriteAttributeString("slotIndex", CultureInvariantToString(tuple.SlotIndex)); - _writer.WriteAttributeString("localName", tuple.LocalName); - _writer.WriteAttributeString("scopeStart", AsILOffset(tuple.ScopeStart)); - _writer.WriteAttributeString("scopeEnd", AsILOffset(tuple.ScopeEnd)); - _writer.WriteEndElement(); - } - - _writer.WriteEndElement(); - } - - private static string JoinNames(ImmutableArray names) - { - var pooledBuilder = PooledStringBuilder.GetInstance(); - var builder = pooledBuilder.Builder; - foreach (var name in names) - { - builder.Append('|'); - if (name != null) - { - builder.Append(name); - } - } - return pooledBuilder.ToStringAndFree(); - } - - private unsafe void WriteEditAndContinueLocalSlotMap(CustomDebugInfoRecord record) - { - Debug.Assert(record.Kind == CustomDebugInfoKind.EditAndContinueLocalSlotMap); - - _writer.WriteStartElement("encLocalSlotMap"); - try - { - int syntaxOffsetBaseline = -1; - - fixed (byte* compressedSlotMapPtr = &record.Data.ToArray()[0]) - { - var blobReader = new BlobReader(compressedSlotMapPtr, record.Data.Length); - - while (blobReader.RemainingBytes > 0) - { - byte b = blobReader.ReadByte(); - - if (b == 0xff) - { - if (!blobReader.TryReadCompressedInteger(out syntaxOffsetBaseline)) - { - _writer.WriteElementString("baseline", "?"); - return; - } - - syntaxOffsetBaseline = -syntaxOffsetBaseline; - continue; - } - - _writer.WriteStartElement("slot"); - - if (b == 0) - { - // short-lived temp, no info - _writer.WriteAttributeString("kind", "temp"); - } - else - { - int synthesizedKind = (b & 0x3f) - 1; - bool hasOrdinal = (b & (1 << 7)) != 0; - - int syntaxOffset; - bool badSyntaxOffset = !blobReader.TryReadCompressedInteger(out syntaxOffset); - syntaxOffset += syntaxOffsetBaseline; - - int ordinal = 0; - bool badOrdinal = hasOrdinal && !blobReader.TryReadCompressedInteger(out ordinal); - - _writer.WriteAttributeString("kind", CultureInvariantToString(synthesizedKind)); - _writer.WriteAttributeString("offset", badSyntaxOffset ? "?" : CultureInvariantToString(syntaxOffset)); - - if (badOrdinal || hasOrdinal) - { - _writer.WriteAttributeString("ordinal", badOrdinal ? "?" : CultureInvariantToString(ordinal)); - } - } - - _writer.WriteEndElement(); - } - } - } - finally - { - _writer.WriteEndElement(); //encLocalSlotMap - } - } - - private unsafe void WriteEditAndContinueLambdaMap(CustomDebugInfoRecord record) - { - Debug.Assert(record.Kind == CustomDebugInfoKind.EditAndContinueLambdaMap); - - _writer.WriteStartElement("encLambdaMap"); - try - { - if (record.Data.Length == 0) - { - return; - } - - int methodOrdinal = -1; - int syntaxOffsetBaseline = -1; - int closureCount; - - fixed (byte* blobPtr = &record.Data.ToArray()[0]) - { - var blobReader = new BlobReader(blobPtr, record.Data.Length); - - if (!blobReader.TryReadCompressedInteger(out methodOrdinal)) - { - _writer.WriteElementString("methodOrdinal", "?"); - _writer.WriteEndElement(); - return; - } - - // [-1, inf) - methodOrdinal--; - _writer.WriteElementString("methodOrdinal", CultureInvariantToString(methodOrdinal)); - - if (!blobReader.TryReadCompressedInteger(out syntaxOffsetBaseline)) - { - _writer.WriteElementString("baseline", "?"); - _writer.WriteEndElement(); - return; - } - - syntaxOffsetBaseline = -syntaxOffsetBaseline; - if (!blobReader.TryReadCompressedInteger(out closureCount)) - { - _writer.WriteElementString("closureCount", "?"); - _writer.WriteEndElement(); - return; - } - - for (int i = 0; i < closureCount; i++) - { - _writer.WriteStartElement("closure"); - try - { - int syntaxOffset; - if (!blobReader.TryReadCompressedInteger(out syntaxOffset)) - { - _writer.WriteElementString("offset", "?"); - break; - } - - _writer.WriteAttributeString("offset", CultureInvariantToString(syntaxOffset + syntaxOffsetBaseline)); - } - finally - { - _writer.WriteEndElement(); - } - } - - while (blobReader.RemainingBytes > 0) - { - _writer.WriteStartElement("lambda"); - try - { - int syntaxOffset; - if (!blobReader.TryReadCompressedInteger(out syntaxOffset)) - { - _writer.WriteElementString("offset", "?"); - return; - } - - _writer.WriteAttributeString("offset", CultureInvariantToString(syntaxOffset + syntaxOffsetBaseline)); - - int closureOrdinal; - if (!blobReader.TryReadCompressedInteger(out closureOrdinal)) - { - _writer.WriteElementString("closure", "?"); - return; - } - - closureOrdinal -= 2; - - if (closureOrdinal == -2) - { - _writer.WriteAttributeString("closure", "this"); - } - else if (closureOrdinal != -1) - { - _writer.WriteAttributeString("closure", - CultureInvariantToString(closureOrdinal) + (closureOrdinal >= closureCount ? " (invalid)" : "")); - } - } - finally - { - _writer.WriteEndElement(); - } - } - } - } - finally - { - _writer.WriteEndElement(); //encLocalSlotMap - } - } - - private void WriteScopes(ISymUnmanagedScope rootScope) - { - // The root scope is always empty. The first scope opened by SymWriter is the child of the root scope. - if (rootScope.GetNamespaces().Length == 0 && rootScope.GetLocals().Length == 0 && rootScope.GetConstants().Length == 0) - { - foreach (ISymUnmanagedScope child in rootScope.GetChildren()) - { - WriteScope(child, isRoot: false); - } - } - else - { - // This shouldn't be executed for PDBs generated via SymWriter. - WriteScope(rootScope, isRoot: true); - } - } - - private void WriteScope(ISymUnmanagedScope scope, bool isRoot) - { - _writer.WriteStartElement(isRoot ? "rootScope" : "scope"); - _writer.WriteAttributeString("startOffset", AsILOffset(scope.GetStartOffset())); - _writer.WriteAttributeString("endOffset", AsILOffset(scope.GetEndOffset())); - - if ((_options & PdbToXmlOptions.ExcludeNamespaces) == 0) - { - foreach (ISymUnmanagedNamespace @namespace in scope.GetNamespaces()) - { - WriteNamespace(@namespace); - } - } - - WriteLocals(scope); - - foreach (ISymUnmanagedScope child in scope.GetChildren()) - { - WriteScope(child, isRoot: false); - } - - _writer.WriteEndElement(); - } - - private void WriteNamespace(ISymUnmanagedNamespace @namespace) - { - string rawName = @namespace.GetName(); - - string alias; - string externAlias; - string target; - ImportTargetKind kind; - VBImportScopeKind scope; - - try - { - if (rawName.Length == 0) - { - externAlias = null; - var parsingSucceeded = CustomDebugInfoReader.TryParseVisualBasicImportString(rawName, out alias, out target, out kind, out scope); - Debug.Assert(parsingSucceeded); - } - else - { - switch (rawName[0]) - { - case 'U': - case 'A': - case 'X': - case 'Z': - case 'E': - case 'T': - scope = VBImportScopeKind.Unspecified; - if (!CustomDebugInfoReader.TryParseCSharpImportString(rawName, out alias, out externAlias, out target, out kind)) - { - throw new InvalidOperationException($"Invalid import '{rawName}'"); - } - break; - - default: - externAlias = null; - if (!CustomDebugInfoReader.TryParseVisualBasicImportString(rawName, out alias, out target, out kind, out scope)) - { - throw new InvalidOperationException($"Invalid import '{rawName}'"); - } - break; - } - } - } - catch (ArgumentException) when ((_options & PdbToXmlOptions.ThrowOnError) == 0) - { - _writer.WriteStartElement("invalid-custom-data"); - _writer.WriteAttributeString("raw", rawName); - _writer.WriteEndElement(); - return; - } - - switch (kind) - { - case ImportTargetKind.CurrentNamespace: - Debug.Assert(alias == null); - Debug.Assert(externAlias == null); - Debug.Assert(scope == VBImportScopeKind.Unspecified); - - _writer.WriteStartElement("currentnamespace"); - _writer.WriteAttributeString("name", target); - _writer.WriteEndElement(); - break; - - case ImportTargetKind.DefaultNamespace: - Debug.Assert(alias == null); - Debug.Assert(externAlias == null); - Debug.Assert(scope == VBImportScopeKind.Unspecified); - - _writer.WriteStartElement("defaultnamespace"); - _writer.WriteAttributeString("name", target); - _writer.WriteEndElement(); - break; - - case ImportTargetKind.MethodToken: - Debug.Assert(alias == null); - Debug.Assert(externAlias == null); - Debug.Assert(scope == VBImportScopeKind.Unspecified); - - int token = Convert.ToInt32(target); - _writer.WriteStartElement("importsforward"); - WriteMethodAttributes(token, isReference: true); - _writer.WriteEndElement(); - break; - - case ImportTargetKind.XmlNamespace: - Debug.Assert(externAlias == null); - - _writer.WriteStartElement("xmlnamespace"); - _writer.WriteAttributeString("prefix", alias); - _writer.WriteAttributeString("name", target); - WriteScopeAttribute(scope); - _writer.WriteEndElement(); - break; - - case ImportTargetKind.NamespaceOrType: - Debug.Assert(externAlias == null); - - _writer.WriteStartElement("alias"); - _writer.WriteAttributeString("name", alias); - _writer.WriteAttributeString("target", target); - _writer.WriteAttributeString("kind", "namespace"); // Strange, but retaining to avoid breaking tests. - WriteScopeAttribute(scope); - _writer.WriteEndElement(); - break; - - case ImportTargetKind.Namespace: - if (alias != null) - { - _writer.WriteStartElement("alias"); - _writer.WriteAttributeString("name", alias); - if (externAlias != null) - { - _writer.WriteAttributeString("qualifier", externAlias); - } - - _writer.WriteAttributeString("target", target); - _writer.WriteAttributeString("kind", "namespace"); - Debug.Assert(scope == VBImportScopeKind.Unspecified); // Only C# hits this case. - _writer.WriteEndElement(); - } - else - { - _writer.WriteStartElement("namespace"); - if (externAlias != null) _writer.WriteAttributeString("qualifier", externAlias); - _writer.WriteAttributeString("name", target); - WriteScopeAttribute(scope); - _writer.WriteEndElement(); - } - - break; - - case ImportTargetKind.Type: - Debug.Assert(externAlias == null); - if (alias != null) - { - _writer.WriteStartElement("alias"); - _writer.WriteAttributeString("name", alias); - _writer.WriteAttributeString("target", target); - _writer.WriteAttributeString("kind", "type"); - Debug.Assert(scope == VBImportScopeKind.Unspecified); // Only C# hits this case. - _writer.WriteEndElement(); - } - else - { - _writer.WriteStartElement("type"); - _writer.WriteAttributeString("name", target); - WriteScopeAttribute(scope); - _writer.WriteEndElement(); - } - - break; - - case ImportTargetKind.Assembly: - Debug.Assert(alias != null); - Debug.Assert(externAlias == null); - Debug.Assert(scope == VBImportScopeKind.Unspecified); - if (target == null) - { - _writer.WriteStartElement("extern"); - _writer.WriteAttributeString("alias", alias); - _writer.WriteEndElement(); - } - else - { - _writer.WriteStartElement("externinfo"); - _writer.WriteAttributeString("alias", alias); - _writer.WriteAttributeString("assembly", target); - _writer.WriteEndElement(); - } - - break; - - case ImportTargetKind.Defunct: - Debug.Assert(alias == null); - Debug.Assert(scope == VBImportScopeKind.Unspecified); - _writer.WriteStartElement("defunct"); - _writer.WriteAttributeString("name", rawName); - _writer.WriteEndElement(); - break; - - default: - Debug.Assert(false, "Unexpected import kind '" + kind + "'"); - _writer.WriteStartElement("unknown"); - _writer.WriteAttributeString("name", rawName); - _writer.WriteEndElement(); - break; - } - } - - private void WriteScopeAttribute(VBImportScopeKind scope) - { - if (scope == VBImportScopeKind.File) - { - _writer.WriteAttributeString("importlevel", "file"); - } - else if (scope == VBImportScopeKind.Project) - { - _writer.WriteAttributeString("importlevel", "project"); - } - else - { - Debug.Assert(scope == VBImportScopeKind.Unspecified, "Unexpected scope '" + scope + "'"); - } - } - - private void WriteAsyncInfo(ISymUnmanagedAsyncMethod asyncMethod) - { - _writer.WriteStartElement("asyncInfo"); - - var catchOffset = asyncMethod.GetCatchHandlerILOffset(); - if (catchOffset >= 0) - { - _writer.WriteStartElement("catchHandler"); - _writer.WriteAttributeString("offset", AsILOffset(catchOffset)); - _writer.WriteEndElement(); - } - - _writer.WriteStartElement("kickoffMethod"); - WriteMethodAttributes(asyncMethod.GetKickoffMethod(), isReference: true); - _writer.WriteEndElement(); - - foreach (var info in asyncMethod.GetAsyncStepInfos()) - { - _writer.WriteStartElement("await"); - _writer.WriteAttributeString("yield", AsILOffset(info.YieldOffset)); - _writer.WriteAttributeString("resume", AsILOffset(info.ResumeOffset)); - WriteMethodAttributes(info.ResumeMethod, isReference: true); - _writer.WriteEndElement(); - } - - _writer.WriteEndElement(); - } - - private void WriteLocals(ISymUnmanagedScope scope) - { - foreach (ISymUnmanagedVariable l in scope.GetLocals()) - { - _writer.WriteStartElement("local"); - _writer.WriteAttributeString("name", l.GetName()); - - // NOTE: VB emits "fake" locals for resumable locals which are actually backed by fields. - // These locals always map to the slot #0 which is just a valid number that is - // not used. Only scoping information is used by EE in this case. - _writer.WriteAttributeString("il_index", CultureInvariantToString(l.GetSlot())); - - _writer.WriteAttributeString("il_start", AsILOffset(scope.GetStartOffset())); - _writer.WriteAttributeString("il_end", AsILOffset(scope.GetEndOffset())); - _writer.WriteAttributeString("attributes", CultureInvariantToString(l.GetAttributes())); - _writer.WriteEndElement(); - } - - foreach (ISymUnmanagedConstant constant in scope.GetConstants()) - { - string name = constant.GetName(); - byte[] signature = constant.GetSignature(); - object value = constant.GetValue(); - - _writer.WriteStartElement("constant"); - _writer.WriteAttributeString("name", name); - - if (value is int && - (int)value == 0 && - (signature[0] == (byte)ConstantTypeCode.NullReference || - signature[0] == (int)SignatureTypeCode.Object || - signature[0] == (int)SignatureTypeCode.String || - (signature.Length > 2 && signature[0] == (int)SignatureTypeCode.GenericTypeInstance && signature[1] == (byte)ConstantTypeCode.NullReference))) - { - _writer.WriteAttributeString("value", "null"); - - if (signature[0] == (int)SignatureTypeCode.String) - { - _writer.WriteAttributeString("type", "String"); - } - else if (signature[0] == (int)SignatureTypeCode.Object) - { - _writer.WriteAttributeString("type", "Object"); - } - else - { - _writer.WriteAttributeString("signature", FormatLocalConstantSignature(signature)); - } - } - else if (value == null) - { - // empty string - if (signature[0] == (byte)SignatureTypeCode.String) - { - _writer.WriteAttributeString("value", ""); - _writer.WriteAttributeString("type", "String"); - } - else - { - _writer.WriteAttributeString("value", "null"); - _writer.WriteAttributeString("unknown-signature", BitConverter.ToString(signature.ToArray())); - } - } - else if (value is decimal) - { - // TODO: check that the signature is a TypeRef - _writer.WriteAttributeString("value", ((decimal)value).ToString(CultureInfo.InvariantCulture)); - _writer.WriteAttributeString("type", value.GetType().Name); - } - else if (value is double && signature[0] != (byte)SignatureTypeCode.Double) - { - // TODO: check that the signature is a TypeRef - _writer.WriteAttributeString("value", DateTimeUtilities.ToDateTime((double)value).ToString(CultureInfo.InvariantCulture)); - _writer.WriteAttributeString("type", "DateTime"); - } - else - { - string str = value as string; - if (str != null) - { - _writer.WriteAttributeString("value", StringUtilities.EscapeNonPrintableCharacters(str)); - } - else - { - _writer.WriteAttributeString("value", string.Format(CultureInfo.InvariantCulture, "{0}", value)); - } - - var runtimeType = GetConstantRuntimeType(signature); - if (runtimeType == null && - (value is sbyte || value is byte || value is short || value is ushort || - value is int || value is uint || value is long || value is ulong)) - { - _writer.WriteAttributeString("signature", FormatLocalConstantSignature(signature)); - } - else if (runtimeType == value.GetType()) - { - _writer.WriteAttributeString("type", ((SignatureTypeCode)signature[0]).ToString()); - } - else - { - _writer.WriteAttributeString("runtime-type", value.GetType().Name); - _writer.WriteAttributeString("unknown-signature", BitConverter.ToString(signature.ToArray())); - } - } - - _writer.WriteEndElement(); - } - } - - private unsafe string FormatLocalConstantSignature(byte[] signature) - { - fixed (byte* sigPtr = signature.ToArray()) - { - var sigReader = new BlobReader(sigPtr, signature.Length); - var decoder = new SignatureDecoder(ConstantSignatureVisualizer.Instance, _metadataReader, genericContext: null); - return decoder.DecodeType(ref sigReader, allowTypeSpecifications: true); - } - } - - private sealed class ConstantSignatureVisualizer : ISignatureTypeProvider - { - public static readonly ConstantSignatureVisualizer Instance = new ConstantSignatureVisualizer(); - - public string GetArrayType(string elementType, ArrayShape shape) - { - return elementType + "[" + new string(',', shape.Rank) + "]"; - } - - public string GetByReferenceType(string elementType) - { - return elementType + "&"; - } - - public string GetFunctionPointerType(MethodSignature signature) - { - // TODO: - return "method-ptr"; - } - - public string GetGenericInstantiation(string genericType, ImmutableArray typeArguments) - { - // using {} since the result is embedded in XML - return genericType + "{" + string.Join(", ", typeArguments) + "}"; - } - - public string GetGenericMethodParameter(object genericContext, int index) - { - return "!!" + index; - } - - public string GetGenericTypeParameter(object genericContext, int index) - { - return "!" + index; - } - - public string GetModifiedType(string modifier, string unmodifiedType, bool isRequired) - { - return (isRequired ? "modreq" : "modopt") + "(" + modifier + ") " + unmodifiedType; - } - - public string GetPinnedType(string elementType) - { - return "pinned " + elementType; - } - - public string GetPointerType(string elementType) - { - return elementType + "*"; - } - - public string GetPrimitiveType(PrimitiveTypeCode typeCode) - { - return typeCode.ToString(); - } - - public string GetSZArrayType(string elementType) - { - return elementType + "[]"; - } - - public string GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) - { - var typeDef = reader.GetTypeDefinition(handle); - var name = reader.GetString(typeDef.Name); - return typeDef.Namespace.IsNil ? name : reader.GetString(typeDef.Namespace) + "." + name; - } - - public string GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) - { - var typeRef = reader.GetTypeReference(handle); - var name = reader.GetString(typeRef.Name); - return typeRef.Namespace.IsNil ? name : reader.GetString(typeRef.Namespace) + "." + name; - } - - public string GetTypeFromSpecification(MetadataReader reader, object genericContext, TypeSpecificationHandle handle, byte rawTypeKind) - { - var sigReader = reader.GetBlobReader(reader.GetTypeSpecification(handle).Signature); - return new SignatureDecoder(Instance, reader, genericContext).DecodeType(ref sigReader); - } - } - - private static Type GetConstantRuntimeType(byte[] signature) - { - switch ((SignatureTypeCode)signature[0]) - { - case SignatureTypeCode.Boolean: - case SignatureTypeCode.Byte: - case SignatureTypeCode.SByte: - case SignatureTypeCode.Int16: - return typeof(short); - - case SignatureTypeCode.Char: - case SignatureTypeCode.UInt16: - return typeof(ushort); - - case SignatureTypeCode.Int32: - return typeof(int); - - case SignatureTypeCode.UInt32: - return typeof(uint); - - case SignatureTypeCode.Int64: - return typeof(long); - - case SignatureTypeCode.UInt64: - return typeof(ulong); - - case SignatureTypeCode.Single: - return typeof(float); - - case SignatureTypeCode.Double: - return typeof(double); - - case SignatureTypeCode.String: - return typeof(string); - } - - return null; - } - - private void WriteSequencePoints(ImmutableArray sequencePoints, IReadOnlyDictionary documentIndex) - { - Debug.Assert(!sequencePoints.IsDefaultOrEmpty); - - _writer.WriteStartElement("sequencePoints"); - - // Write out sequence points - foreach (var sequencePoint in sequencePoints) - { - _writer.WriteStartElement("entry"); - _writer.WriteAttributeString("offset", AsILOffset(sequencePoint.Offset)); - - if (sequencePoint.IsHidden) - { - if (sequencePoint.StartLine != sequencePoint.EndLine || sequencePoint.StartColumn != 0 || sequencePoint.EndColumn != 0) - { - _writer.WriteAttributeString("hidden", "invalid"); - } - else - { - _writer.WriteAttributeString("hidden", XmlConvert.ToString(true)); - } - } - else - { - _writer.WriteAttributeString("startLine", CultureInvariantToString(sequencePoint.StartLine)); - _writer.WriteAttributeString("startColumn", CultureInvariantToString(sequencePoint.StartColumn)); - _writer.WriteAttributeString("endLine", CultureInvariantToString(sequencePoint.EndLine)); - _writer.WriteAttributeString("endColumn", CultureInvariantToString(sequencePoint.EndColumn)); - } - - int documentId; - string documentName = sequencePoint.Document.GetName(); - if (documentName.Length > 0) - { - if (documentIndex.TryGetValue(documentName, out documentId)) - { - _writer.WriteAttributeString("document", CultureInvariantToString(documentId)); - } - else - { - _writer.WriteAttributeString("document", "?"); - } - } - - _writer.WriteEndElement(); - } - - _writer.WriteEndElement(); // sequencepoints - } - - private unsafe ImmutableArray BuildMethodTokenMap() - { - if (!(_symReader is ISymUnmanagedReader4 symReader4) || - symReader4.GetPortableDebugMetadata(out byte* metadata, out int size) != 0) - { - return ImmutableArray.Empty; - } - - var reader = new MetadataReader(metadata, size); - - return (from handle in reader.GetEditAndContinueMapEntries() - where handle.Kind == HandleKind.MethodDebugInformation - select MetadataTokens.MethodDefinitionHandle(MetadataTokens.GetRowNumber(handle))).ToImmutableArray(); - } - - private IReadOnlyDictionary BuildDocumentIndex(IReadOnlyList documents) - { - var index = new Dictionary(documents.Count); - - int id = 1; - foreach (var document in documents) - { - string name = document.GetName(); - - // Native PDB doesn't allow no-name documents. SymWriter silently ignores them. - // In Portable PDB all methods must be contained in a document, whose name may be empty. - // Skip such documents - like they were never in the document table. - if (name.Length == 0) - { - continue; - } - - // Skip adding dups into the index, but increment id so that we - // can tell what methods are referring to the duplicate. - if (!index.ContainsKey(name)) - { - index.Add(name, id); - } - - id++; - } - - return index; - } - - private void WriteDocuments(IEnumerable documents, IReadOnlyDictionary documentIndex) - { - bool hasDocument = false; - - foreach (var doc in documents) - { - string name = doc.GetName(); - - int id; - if (!documentIndex.TryGetValue(name, out id)) - { - continue; - } - - if (!hasDocument) - { - _writer.WriteStartElement("files"); - } - - hasDocument = true; - - _writer.WriteStartElement("file"); - - _writer.WriteAttributeString("id", CultureInvariantToString(id)); - _writer.WriteAttributeString("name", name); - _writer.WriteAttributeString("language", doc.GetLanguage().ToString()); - _writer.WriteAttributeString("languageVendor", doc.GetLanguageVendor().ToString()); - _writer.WriteAttributeString("documentType", doc.GetDocumentType().ToString()); - - var checkSum = string.Concat(doc.GetChecksum().Select(b => $"{b,2:X}, ")); - - if (!string.IsNullOrEmpty(checkSum)) - { - _writer.WriteAttributeString("checkSumAlgorithmId", doc.GetHashAlgorithm().ToString()); - _writer.WriteAttributeString("checkSum", checkSum); - } - - _writer.WriteEndElement(); - } - - if (hasDocument) - { - _writer.WriteEndElement(); - } - } - - private void WriteAllMethodSpans() - { - if ((_options & PdbToXmlOptions.IncludeMethodSpans) == 0) - { - return; - } - - _writer.WriteStartElement("method-spans"); - - foreach (ISymUnmanagedDocument doc in _symReader.GetDocuments()) - { - foreach (ISymUnmanagedMethod method in _symReader.GetMethodsInDocument(doc)) - { - _writer.WriteStartElement("method"); - - WriteMethodAttributes(method.GetToken(), isReference: true); - - foreach (var methodDocument in method.GetDocumentsForMethod()) - { - _writer.WriteStartElement("document"); - - int startLine, endLine; - ((ISymEncUnmanagedMethod)method).GetSourceExtentInDocument(methodDocument, out startLine, out endLine); - - _writer.WriteAttributeString("startLine", startLine.ToString()); - _writer.WriteAttributeString("endLine", endLine.ToString()); - - _writer.WriteEndElement(); - } - - _writer.WriteEndElement(); - } - } - - _writer.WriteEndElement(); - } - - // Write out a reference to the entry point method (if one exists) - private void WriteEntryPoint() - { - int token = _symReader.GetUserEntryPoint(); - if (token != 0) - { - _writer.WriteStartElement("entryPoint"); - WriteMethodAttributes(token, isReference: true); - _writer.WriteEndElement(); - } - } - - // Write out XML snippet to refer to the given method. - private void WriteMethodAttributes(int token, bool isReference) - { - if ((_options & PdbToXmlOptions.ResolveTokens) != 0) - { - var handle = MetadataTokens.Handle(token); - - try - { - switch (handle.Kind) - { - case HandleKind.MethodDefinition: - WriteResolvedToken((MethodDefinitionHandle)handle, isReference); - break; - - case HandleKind.MemberReference: - WriteResolvedToken((MemberReferenceHandle)handle); - break; - - default: - WriteToken(token); - _writer.WriteAttributeString("error", $"Unexpected token type: {handle.Kind}"); - break; - } - } - catch (BadImageFormatException e) // TODO: filter - { - if ((_options & PdbToXmlOptions.ThrowOnError) != 0) - { - throw; - } - - WriteToken(token); - _writer.WriteAttributeString("metadata-error", e.Message); - } - } - - if ((_options & PdbToXmlOptions.IncludeTokens) != 0) - { - WriteToken(token); - } - } - - private static string GetQualifiedMethodName(MetadataReader metadataReader, MethodDefinitionHandle methodHandle) - { - var method = metadataReader.GetMethodDefinition(methodHandle); - var containingTypeHandle = method.GetDeclaringType(); - - string fullTypeName = GetFullTypeName(metadataReader, containingTypeHandle); - string methodName = metadataReader.GetString(method.Name); - - return fullTypeName != null ? fullTypeName + "." + methodName : methodName; - } - - private void WriteResolvedToken(MethodDefinitionHandle methodHandle, bool isReference) - { - var method = _metadataReader.GetMethodDefinition(methodHandle); - - // type name - var containingTypeHandle = method.GetDeclaringType(); - var fullName = GetFullTypeName(_metadataReader, containingTypeHandle); - if (fullName != null) - { - _writer.WriteAttributeString(isReference ? "declaringType" : "containingType", fullName); - } - - // method name - _writer.WriteAttributeString(isReference ? "methodName" : "name", _metadataReader.GetString(method.Name)); - - // parameters: - var parameterNames = (from paramHandle in method.GetParameters() - let parameter = _metadataReader.GetParameter(paramHandle) - where parameter.SequenceNumber > 0 // exclude return parameter - select parameter.Name.IsNil ? "?" : _metadataReader.GetString(parameter.Name)).ToArray(); - - if (parameterNames.Length > 0) - { - _writer.WriteAttributeString("parameterNames", string.Join(", ", parameterNames)); - } - } - - private void WriteResolvedToken(MemberReferenceHandle memberRefHandle) - { - var memberRef = _metadataReader.GetMemberReference(memberRefHandle); - - // type name - string fullName = GetFullTypeName(_metadataReader, memberRef.Parent); - if (fullName != null) - { - _writer.WriteAttributeString("declaringType", fullName); - } - - // method name - _writer.WriteAttributeString("methodName", _metadataReader.GetString(memberRef.Name)); - } - - private static bool IsNested(TypeAttributes flags) - { - return (flags & ((TypeAttributes)0x00000006)) != 0; - } - - private static string GetFullTypeName(MetadataReader metadataReader, EntityHandle handle) - { - if (handle.IsNil) - { - return null; - } - - if (handle.Kind == HandleKind.TypeDefinition) - { - var type = metadataReader.GetTypeDefinition((TypeDefinitionHandle)handle); - string name = metadataReader.GetString(type.Name); - - while (IsNested(type.Attributes)) - { - var enclosingType = metadataReader.GetTypeDefinition(type.GetDeclaringType()); - name = metadataReader.GetString(enclosingType.Name) + "+" + name; - type = enclosingType; - } - - if (type.Namespace.IsNil) - { - return name; - } - - return metadataReader.GetString(type.Namespace) + "." + name; - } - - if (handle.Kind == HandleKind.TypeReference) - { - var typeRef = metadataReader.GetTypeReference((TypeReferenceHandle)handle); - string name = metadataReader.GetString(typeRef.Name); - if (typeRef.Namespace.IsNil) - { - return name; - } - - return metadataReader.GetString(typeRef.Namespace) + "." + name; - } - - return string.Format("", AsToken(metadataReader.GetToken(handle))); - } - - private unsafe void WriteSourceServerInformation() - { - var srcsvrModule = _symReader as ISymUnmanagedSourceServerModule; - if (srcsvrModule != null) - { - _writer.WriteStartElement("srcsvr"); - - Marshal.ThrowExceptionForHR(srcsvrModule.GetSourceServerData(out int length, out byte* data)); - - try - { - string str = Encoding.UTF8.GetString(data, length); - - try - { - _writer.WriteCData(str); - } - catch (ArgumentException) - { - try - { - _writer.WriteValue(str); - } - catch (ArgumentException) - { - _writer.WriteAttributeString("encoding", "base64"); - var bytes = new byte[length]; - Marshal.Copy((IntPtr)data, bytes, 0, bytes.Length); - _writer.WriteBase64(bytes, 0, bytes.Length); - } - } - } - finally - { - Marshal.FreeCoTaskMem((IntPtr)data); - } - - _writer.WriteEndElement(); - } - } - - #region Utils - - private void WriteToken(int token) - { - _writer.WriteAttributeString("token", AsToken(token)); - } - - internal static string AsToken(int i) - { - return string.Format(CultureInfo.InvariantCulture, "0x{0:x}", i); - } - - internal static string AsILOffset(int i) - { - return string.Format(CultureInfo.InvariantCulture, "0x{0:x}", i); - } - - internal static string CultureInvariantToString(int input) - { - return input.ToString(CultureInfo.InvariantCulture); - } - - #endregion - } -} diff --git a/src/Test/PdbUtilities/Pdb/PdbToXmlOptions.cs b/src/Test/PdbUtilities/Pdb/PdbToXmlOptions.cs deleted file mode 100644 index 4006e72c8637e..0000000000000 --- a/src/Test/PdbUtilities/Pdb/PdbToXmlOptions.cs +++ /dev/null @@ -1,24 +0,0 @@ -// 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; - -namespace Roslyn.Test.PdbUtilities -{ - [Flags] - public enum PdbToXmlOptions - { - Default = 0, - ThrowOnError = 1 << 1, - ResolveTokens = 1 << 2, - IncludeTokens = 1 << 3, - IncludeMethodSpans = 1 << 4, - ExcludeDocuments = 1 << 5, - ExcludeMethods = 1 << 6, - ExcludeSequencePoints = 1 << 7, - ExcludeScopes = 1 << 8, - ExcludeNamespaces = 1 << 9, - ExcludeAsyncInfo = 1 << 10, - ExcludeCustomDebugInformation = 1 << 11, - IncludeSourceServerInformation = 1 << 12, - } -} diff --git a/src/Test/PdbUtilities/PdbUtilities.csproj b/src/Test/PdbUtilities/PdbUtilities.csproj index 4771c41914514..04a81b92e355e 100644 --- a/src/Test/PdbUtilities/PdbUtilities.csproj +++ b/src/Test/PdbUtilities/PdbUtilities.csproj @@ -34,9 +34,6 @@ - - - diff --git a/src/Test/Utilities/Portable/CommonTestBase.CompilationVerifier.cs b/src/Test/Utilities/Portable/CommonTestBase.CompilationVerifier.cs index 429ccc0fcbf79..67a41c836262f 100644 --- a/src/Test/Utilities/Portable/CommonTestBase.CompilationVerifier.cs +++ b/src/Test/Utilities/Portable/CommonTestBase.CompilationVerifier.cs @@ -14,6 +14,7 @@ using Microsoft.CodeAnalysis.CodeGen; using Microsoft.CodeAnalysis.Emit; using Microsoft.DiaSymReader; +using Microsoft.DiaSymReader.Tools; using Roslyn.Test.PdbUtilities; using Roslyn.Test.Utilities; using Xunit; diff --git a/src/Test/Utilities/Portable/Compilation/CompilationDifference.cs b/src/Test/Utilities/Portable/Compilation/CompilationDifference.cs index 46044d89c731e..ffd7fd92992ea 100644 --- a/src/Test/Utilities/Portable/Compilation/CompilationDifference.cs +++ b/src/Test/Utilities/Portable/Compilation/CompilationDifference.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.DiaSymReader.Tools; using Roslyn.Test.MetadataUtilities; using Roslyn.Test.PdbUtilities; using Roslyn.Test.Utilities; diff --git a/src/Test/Utilities/Portable/Compilation/CompilationExtensions.cs b/src/Test/Utilities/Portable/Compilation/CompilationExtensions.cs index 7ad9f69b0e77c..935062d382779 100644 --- a/src/Test/Utilities/Portable/Compilation/CompilationExtensions.cs +++ b/src/Test/Utilities/Portable/Compilation/CompilationExtensions.cs @@ -159,7 +159,7 @@ internal static void VerifyOperationTree(this Compilation compilation, SyntaxNod var actualTextBuilder = new StringBuilder(); SemanticModel model = compilation.GetSemanticModel(node.SyntaxTree); AppendOperationTree(model, node, actualTextBuilder); - VerifyOperationTree(expectedOperationTree, actualTextBuilder.ToString()); + OperationTreeVerifier.Verify(expectedOperationTree, actualTextBuilder.ToString()); } internal static void VerifyOperationTree(this Compilation compilation, string expectedOperationTree, bool skipImplicitlyDeclaredSymbols = false) @@ -218,7 +218,7 @@ internal static void VerifyOperationTree(this Compilation compilation, string sy actualTextBuilder.Append(Environment.NewLine); } - VerifyOperationTree(expectedOperationTree, actualTextBuilder.ToString()); + OperationTreeVerifier.Verify(expectedOperationTree, actualTextBuilder.ToString()); } private static void AppendOperationTree(SemanticModel model, SyntaxNode node, StringBuilder actualTextBuilder, int initialIndent = 0) @@ -235,15 +235,6 @@ private static void AppendOperationTree(SemanticModel model, SyntaxNode node, St } } - private static void VerifyOperationTree(string expectedOperationTree, string actualOperationTree) - { - char[] newLineChars = Environment.NewLine.ToCharArray(); - string actual = actualOperationTree.Trim(newLineChars); - expectedOperationTree = expectedOperationTree.Trim(newLineChars); - expectedOperationTree = Regex.Replace(expectedOperationTree, "([^\r])\n", "$1" + Environment.NewLine); - AssertEx.AreEqual(expectedOperationTree, actual); - } - internal static bool CanHaveExecutableCodeBlock(ISymbol symbol) { switch (symbol.Kind) diff --git a/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs b/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs index 6c47686b35e67..dbdacbf4e69d2 100644 --- a/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs +++ b/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs @@ -5,8 +5,10 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Text; +using System.Text.RegularExpressions; using Microsoft.CodeAnalysis.Semantics; using Microsoft.CodeAnalysis.Test.Extensions; +using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.Test.Utilities @@ -42,6 +44,15 @@ public static string GetOperationTree(IOperation operation, int initialIndent = return walker._builder.ToString(); } + public static void Verify(string expectedOperationTree, string actualOperationTree) + { + char[] newLineChars = Environment.NewLine.ToCharArray(); + string actual = actualOperationTree.Trim(newLineChars); + expectedOperationTree = expectedOperationTree.Trim(newLineChars); + expectedOperationTree = Regex.Replace(expectedOperationTree, "([^\r])\n", "$1" + Environment.NewLine); + AssertEx.AreEqual(expectedOperationTree, actual); + } + #region Logging helpers private void LogCommonPropertiesAndNewLine(IOperation operation) @@ -208,7 +219,8 @@ private void VisitInstanceExpression(IOperation instance) internal override void VisitNoneOperation(IOperation operation) { - Assert.True(false, "Encountered an IOperation with `Kind == OperationKind.None` while walking the operation tree."); + LogString("IOperation: "); + LogCommonPropertiesAndNewLine(operation); } public override void VisitBlockStatement(IBlockStatement operation) diff --git a/src/Test/Utilities/Portable/Extensions/SemanticModelExtensions.cs b/src/Test/Utilities/Portable/Extensions/SemanticModelExtensions.cs new file mode 100644 index 0000000000000..a69daa2103b26 --- /dev/null +++ b/src/Test/Utilities/Portable/Extensions/SemanticModelExtensions.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.CodeAnalysis.Test.Extensions +{ + public static class SemanticModelExtensions + { + public static IOperation GetOperationInternal(this SemanticModel model, SyntaxNode node) + { + // Invoke the GetOperationInternal API to by-pass the IOperation feature flag check. + return model.GetOperationInternal(node); + } + } +} diff --git a/src/Test/Utilities/Portable/Mocks/TestMessageProvider.cs b/src/Test/Utilities/Portable/Mocks/TestMessageProvider.cs index bb48912b1aeff..1b7bec1626f2e 100644 --- a/src/Test/Utilities/Portable/Mocks/TestMessageProvider.cs +++ b/src/Test/Utilities/Portable/Mocks/TestMessageProvider.cs @@ -425,5 +425,21 @@ public override int ERR_BadAssemblyName throw new NotImplementedException(); } } + + public override int ERR_BadSourceCodeKind + { + get + { + throw new NotImplementedException(); + } + } + + public override int ERR_BadDocumentationMode + { + get + { + throw new NotImplementedException(); + } + } } } diff --git a/src/Test/Utilities/Portable/Pdb/PdbValidation.cs b/src/Test/Utilities/Portable/Pdb/PdbValidation.cs index 60de4cf5a127d..8a9ad9f6e5033 100644 --- a/src/Test/Utilities/Portable/Pdb/PdbValidation.cs +++ b/src/Test/Utilities/Portable/Pdb/PdbValidation.cs @@ -15,6 +15,7 @@ using System.Xml.Linq; using Microsoft.CodeAnalysis.CodeGen; using Microsoft.CodeAnalysis.Emit; +using Microsoft.DiaSymReader.Tools; using Roslyn.Test.MetadataUtilities; using Roslyn.Test.PdbUtilities; using Roslyn.Test.Utilities; diff --git a/src/Test/Utilities/Portable/ReflectionAssert.cs b/src/Test/Utilities/Portable/ReflectionAssert.cs index 02975f6f13380..0ff79f98b163c 100644 --- a/src/Test/Utilities/Portable/ReflectionAssert.cs +++ b/src/Test/Utilities/Portable/ReflectionAssert.cs @@ -16,7 +16,7 @@ public static void AssertPublicAndInternalFieldsAndProperties(Type targetType, p properties.Where(p => p.GetMethod != null && !p.GetMethod.IsPrivate).Select(p => p.Name)). OrderBy(s => s); - AssertEx.SetEqual(fieldsAndProps, expectedFieldsAndProperties, itemSeparator: "\r\n"); + AssertEx.SetEqual(expectedFieldsAndProperties, fieldsAndProps, itemSeparator: "\r\n"); } } } diff --git a/src/Test/Utilities/Portable/TestUtilities.csproj b/src/Test/Utilities/Portable/TestUtilities.csproj index df3a0fce2a553..37716cf9d1cef 100644 --- a/src/Test/Utilities/Portable/TestUtilities.csproj +++ b/src/Test/Utilities/Portable/TestUtilities.csproj @@ -129,6 +129,7 @@ + diff --git a/src/Test/Utilities/Portable/project.json b/src/Test/Utilities/Portable/project.json index e1bf3ce3734fb..15418c3d12fbb 100644 --- a/src/Test/Utilities/Portable/project.json +++ b/src/Test/Utilities/Portable/project.json @@ -4,6 +4,7 @@ "Microsoft.CSharp": "4.3.0", "Microsoft.NETCore.Platforms": "1.1.0", "Microsoft.NETCore.Portable.Compatibility": "1.0.1", + "Microsoft.DiaSymReader.Converter.Xml": "1.0.0-beta1-61515-01", "System.Collections.Concurrent": "4.3.0", "System.Console": "4.3.0", "System.Diagnostics.Debug": "4.3.0", diff --git a/src/Tools/MicroBuild/publish-assets.ps1 b/src/Tools/MicroBuild/publish-assets.ps1 index 889c6514d89fa..3bb73342affbd 100644 --- a/src/Tools/MicroBuild/publish-assets.ps1 +++ b/src/Tools/MicroBuild/publish-assets.ps1 @@ -41,6 +41,7 @@ try switch ($branchName) { "dev15.0.x" { } + "dev15.1.x" { } "master" { } "dev_pilchie_Fix389698" { } default diff --git a/src/Tools/Source/Pdb2Xml/Pdb2Xml.csproj b/src/Tools/Source/Pdb2Xml/Pdb2Xml.csproj deleted file mode 100644 index 5ccbdf94eea3c..0000000000000 --- a/src/Tools/Source/Pdb2Xml/Pdb2Xml.csproj +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - AnyCPU - AnyCPU - {CF450DCE-D12B-4A11-8D2D-A7A125372C48} - Exe - Pdb2Xml - Pdb2Xml - v4.6 - false - true - true - - - - - - - - - {afde6bea-5038-4a4a-a88e-dbd2e4088eed} - PdbUtilities - - - - - \ No newline at end of file diff --git a/src/Tools/Source/Pdb2Xml/PdbToXmlApp.cs b/src/Tools/Source/Pdb2Xml/PdbToXmlApp.cs deleted file mode 100644 index a477deb21c452..0000000000000 --- a/src/Tools/Source/Pdb2Xml/PdbToXmlApp.cs +++ /dev/null @@ -1,132 +0,0 @@ -// 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.Generic; -using System.IO; -using System.Linq; -using System.Text; -using Roslyn.Test.PdbUtilities; - -internal static class PdbToXmlApp -{ - public static int Main(string[] args) - { - if (args.Length == 0) - { - Console.WriteLine("Usage: Pdb2Xml [] [/tokens] [/methodSpans] [/delta] [/srcsvr]"); - return 1; - } - - var argList = new List(args); - - bool isDelta = argList.Remove("/delta"); - - var options = PdbToXmlOptions.ResolveTokens; - - if (argList.Remove("/tokens")) - { - options |= PdbToXmlOptions.IncludeTokens; - } - - if (argList.Remove("/methodSpans")) - { - options |= PdbToXmlOptions.IncludeMethodSpans; - } - - if (argList.Remove("/srcsvr")) - { - options |= PdbToXmlOptions.IncludeSourceServerInformation; - } - - string peFile; - string pdbFile; - - if (isDelta) - { - peFile = null; - pdbFile = argList[0]; - } - else - { - peFile = argList[0]; - pdbFile = Path.ChangeExtension(peFile, ".pdb"); - } - - if (peFile != null && !File.Exists(peFile)) - { - Console.WriteLine($"File not found: {peFile}"); - return 2; - } - - if (!File.Exists(pdbFile)) - { - Console.WriteLine($"PDB File not found: {pdbFile}"); - return 2; - } - - string xmlFile; - if (argList.Count > 1) - { - xmlFile = argList[1]; - } - else - { - xmlFile = Path.ChangeExtension(pdbFile, ".xml"); - } - - if (File.Exists(xmlFile)) - { - try - { - File.Delete(xmlFile); - } - catch - { - } - } - - try - { - if (isDelta) - { - GenXmlFromDeltaPdb(pdbFile, xmlFile, options); - } - else - { - GenXmlFromPdb(peFile, pdbFile, xmlFile, options); - } - } - catch (Exception e) - { - Console.Error.WriteLine(e.Message); - return e.HResult; - } - - Console.WriteLine($"PDB dump written to {xmlFile}"); - return 0; - } - - public static void GenXmlFromPdb(string exePath, string pdbPath, string outPath, PdbToXmlOptions options) - { - using (var exebits = new FileStream(exePath, FileMode.Open, FileAccess.Read)) - { - using (var pdbbits = new FileStream(pdbPath, FileMode.Open, FileAccess.Read)) - { - using (var sw = new StreamWriter(outPath, append: false, encoding: Encoding.UTF8)) - { - PdbToXmlConverter.ToXml(sw, pdbbits, exebits, options); - } - } - } - } - - public static void GenXmlFromDeltaPdb(string pdbPath, string outPath, PdbToXmlOptions options) - { - using (var deltaPdb = new FileStream(pdbPath, FileMode.Open, FileAccess.Read)) - { - // There is no easy way to enumerate all method tokens that are present in the PDB. - // So dump the first 255 method tokens (the ones that are not present will be skipped): - File.WriteAllText(outPath, PdbToXmlConverter.DeltaPdbToXml(deltaPdb, Enumerable.Range(0x06000001, 255))); - } - } -} \ No newline at end of file diff --git a/src/Tools/Source/Pdb2Xml/project.json b/src/Tools/Source/Pdb2Xml/project.json deleted file mode 100644 index 42a5721332053..0000000000000 --- a/src/Tools/Source/Pdb2Xml/project.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "dependencies": { - "Microsoft.DiaSymReader": "1.1.0" - }, - "frameworks": { - "net46": { } - }, - "runtimes": { - "win7": { } - } -} diff --git a/src/VisualStudio/CSharp/Impl/ProjectSystemShim/CSharpProjectShim.cs b/src/VisualStudio/CSharp/Impl/ProjectSystemShim/CSharpProjectShim.cs index 1f7e5388a92ac..8ce6069dde630 100644 --- a/src/VisualStudio/CSharp/Impl/ProjectSystemShim/CSharpProjectShim.cs +++ b/src/VisualStudio/CSharp/Impl/ProjectSystemShim/CSharpProjectShim.cs @@ -256,8 +256,7 @@ protected override ParseOptions CreateParseOptions(CommandLineArguments commandL documentationMode = DocumentationMode.Diagnose; } - var languageVersion = CompilationOptionsConversion.GetLanguageVersion(GetStringOption(CompilerOptions.OPTID_COMPATIBILITY, defaultValue: "")) - ?? CSharpParseOptions.Default.LanguageVersion; + GetStringOption(CompilerOptions.OPTID_COMPATIBILITY, defaultValue: "").TryParse(out var languageVersion); return options.WithKind(SourceCodeKind.Regular) .WithLanguageVersion(languageVersion) diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs index 67b4699f0bd88..b01e8e4986660 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs @@ -195,6 +195,21 @@ internal override bool CanRenameFilesDuringCodeActions(CodeAnalysis.Project proj return true; } + protected override bool CanApplyParseOptionChange(ParseOptions oldOptions, ParseOptions newOptions, CodeAnalysis.Project project) + { + var parseOptionsService = project.LanguageServices.GetService(); + if (parseOptionsService == null) + { + return false; + } + + // Currently, only changes to the LanguageVersion of parse options are supported. + var newLanguageVersion = parseOptionsService.GetLanguageVersion(newOptions); + var updated = parseOptionsService.WithLanguageVersion(oldOptions, newLanguageVersion); + + return newOptions == updated; + } + public override bool CanApplyChange(ApplyChangesKind feature) { switch (feature) @@ -211,6 +226,7 @@ public override bool CanApplyChange(ApplyChangesKind feature) case ApplyChangesKind.AddAdditionalDocument: case ApplyChangesKind.RemoveAdditionalDocument: case ApplyChangesKind.ChangeAdditionalDocument: + case ApplyChangesKind.ChangeParseOptions: return true; default: @@ -271,6 +287,44 @@ private string GetAnalyzerPath(AnalyzerReference analyzerReference) return analyzerReference.FullPath; } + protected override void ApplyParseOptionsChanged(ProjectId projectId, ParseOptions options) + { + if (projectId == null) + { + throw new ArgumentNullException(nameof(projectId)); + } + + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + var parseOptionsService = CurrentSolution.GetProject(projectId).LanguageServices.GetService(); + Contract.ThrowIfNull(parseOptionsService, nameof(parseOptionsService)); + + string newVersion = parseOptionsService.GetLanguageVersion(options); + + GetProjectData(projectId, out var hostProject, out var hierarchy, out var project); + foreach (string configurationName in (object[])project.ConfigurationManager.ConfigurationRowNames) + { + switch (hostProject.Language) + { + case LanguageNames.CSharp: + var csharpProperties = (VSLangProj80.CSharpProjectConfigurationProperties3)project.ConfigurationManager + .ConfigurationRow(configurationName).Item(1).Object; + + if (newVersion != csharpProperties.LanguageVersion) + { + csharpProperties.LanguageVersion = newVersion; + } + break; + + case LanguageNames.VisualBasic: + throw new InvalidOperationException(ServicesVSResources.This_workspace_does_not_support_updating_Visual_Basic_parse_options); + } + } + } + protected override void ApplyAnalyzerReferenceAdded(ProjectId projectId, AnalyzerReference analyzerReference) { if (projectId == null) diff --git a/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs b/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs index 6ba0ffe12cebe..14afca7e2e101 100644 --- a/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs +++ b/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs @@ -130,6 +130,11 @@ public void Disable() } public Task GetRemoteHostClientAsync(CancellationToken cancellationToken) + { + return TryGetRemoteHostClientAsync(cancellationToken); + } + + public Task TryGetRemoteHostClientAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); diff --git a/src/VisualStudio/Core/Def/ServicesVSResources.Designer.cs b/src/VisualStudio/Core/Def/ServicesVSResources.Designer.cs index 77260a099d05c..9a90bfd80026a 100644 --- a/src/VisualStudio/Core/Def/ServicesVSResources.Designer.cs +++ b/src/VisualStudio/Core/Def/ServicesVSResources.Designer.cs @@ -2068,6 +2068,15 @@ internal static string This_item_cannot_be_deleted_because_it_is_used_by_an_exis } } + /// + /// Looks up a localized string similar to This workspace does not support updating Visual Basic parse options.. + /// + internal static string This_workspace_does_not_support_updating_Visual_Basic_parse_options { + get { + return ResourceManager.GetString("This_workspace_does_not_support_updating_Visual_Basic_parse_options", resourceCulture); + } + } + /// /// Looks up a localized string similar to This workspace only supports opening documents on the UI thread.. /// diff --git a/src/VisualStudio/Core/Def/ServicesVSResources.resx b/src/VisualStudio/Core/Def/ServicesVSResources.resx index 77896e13bcc33..d3cd253b098d0 100644 --- a/src/VisualStudio/Core/Def/ServicesVSResources.resx +++ b/src/VisualStudio/Core/Def/ServicesVSResources.resx @@ -501,6 +501,9 @@ Use the dropdown to view and switch to other projects this file may belong to. This workspace only supports opening documents on the UI thread. + + This workspace does not support updating Visual Basic parse options. + Synchronize {0} diff --git a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj index 2fc6ca257548f..94460a9125dd5 100644 --- a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj +++ b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj @@ -308,6 +308,7 @@ + @@ -705,4 +706,4 @@ - \ No newline at end of file + diff --git a/src/VisualStudio/Core/Def/project.json b/src/VisualStudio/Core/Def/project.json index 210116e448470..3a5ebe7cffffd 100644 --- a/src/VisualStudio/Core/Def/project.json +++ b/src/VisualStudio/Core/Def/project.json @@ -39,7 +39,8 @@ "Microsoft.DiaSymReader": "1.1.0", "Microsoft.MSXML": "8.0.0.0-alpha", "Microsoft.Build": "15.1.0-preview-000458-02", - "VSLangProj": "7.0.3300-alpha", + "VSLangProj": "7.0.3300", + "VSLangProj2": "7.0.5000", "VSLangProj80": "8.0.0.0-alpha", "VSLangProj140": "14.3.25407-alpha", "VsWebsite.Interop": "8.0.0.0-alpha", @@ -49,4 +50,4 @@ "frameworks": { "net46": {} } -} \ No newline at end of file +} diff --git a/src/VisualStudio/Core/Next/CodeLens/RemoteCodeLensReferencesService.cs b/src/VisualStudio/Core/Next/CodeLens/RemoteCodeLensReferencesService.cs index 019734cf1b7dd..af13826508b14 100644 --- a/src/VisualStudio/Core/Next/CodeLens/RemoteCodeLensReferencesService.cs +++ b/src/VisualStudio/Core/Next/CodeLens/RemoteCodeLensReferencesService.cs @@ -25,7 +25,7 @@ public async Task GetReferenceCountAsync(Solution solution, Docu return null; } - var remoteHostClient = await solution.Workspace.Services.GetService().GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); + var remoteHostClient = await solution.Workspace.Services.GetService().TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); if (remoteHostClient == null) { // remote host is not running. this can happen if remote host is disabled. @@ -46,7 +46,7 @@ public async Task> FindReferenceLocatio return null; } - var remoteHostClient = await solution.Workspace.Services.GetService().GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); + var remoteHostClient = await solution.Workspace.Services.GetService().TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); if (remoteHostClient == null) { // remote host is not running. this can happen if remote host is disabled. @@ -67,7 +67,7 @@ public async Task> FindReferenceMethodsAs return null; } - var remoteHostClient = await solution.Workspace.Services.GetService().GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); + var remoteHostClient = await solution.Workspace.Services.GetService().TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); if (remoteHostClient == null) { // remote host is not running. this can happen if remote host is disabled. @@ -88,7 +88,7 @@ public async Task GetFullyQualifiedName(Solution solution, DocumentId do return null; } - var remoteHostClient = await solution.Workspace.Services.GetService().GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); + var remoteHostClient = await solution.Workspace.Services.GetService().TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); if (remoteHostClient == null) { // remote host is not running. this can happen if remote host is disabled. diff --git a/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs b/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs index 8f9b992a94801..3fb35eb1332f3 100644 --- a/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs @@ -145,7 +145,7 @@ void Method() var asset = assetBuilder.Build(analyzerReference, CancellationToken.None); snapshotService.AddGlobalAsset(analyzerReference, asset, CancellationToken.None); - var client = await workspace.Services.GetService().GetRemoteHostClientAsync(CancellationToken.None); + var client = await workspace.Services.GetService().TryGetRemoteHostClientAsync(CancellationToken.None); await client.RunOnRemoteHostAsync( WellKnownRemoteHostServices.RemoteHostService, workspace.CurrentSolution, nameof(IRemoteHostService.SynchronizeGlobalAssetsAsync), (object)(new Checksum[] { asset.Checksum }), CancellationToken.None); diff --git a/src/VisualStudio/Razor/RazorLanguageServiceClientFactory.cs b/src/VisualStudio/Razor/RazorLanguageServiceClientFactory.cs index 3bd21c758463f..1dc4cc8683a85 100644 --- a/src/VisualStudio/Razor/RazorLanguageServiceClientFactory.cs +++ b/src/VisualStudio/Razor/RazorLanguageServiceClientFactory.cs @@ -12,7 +12,7 @@ internal static class RazorLanguageServiceClientFactory public static async Task CreateAsync(Workspace workspace, CancellationToken cancellationToken = default(CancellationToken)) { var clientFactory = workspace.Services.GetRequiredService(); - var client = await clientFactory.GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); + var client = await clientFactory.TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); return client == null ? null : new RazorLangaugeServiceClient(client); } } diff --git a/src/Workspaces/CSharp/Portable/CSharpWorkspace.csproj b/src/Workspaces/CSharp/Portable/CSharpWorkspace.csproj index 5aa30a750abf0..6d51aaf3ea98b 100644 --- a/src/Workspaces/CSharp/Portable/CSharpWorkspace.csproj +++ b/src/Workspaces/CSharp/Portable/CSharpWorkspace.csproj @@ -190,6 +190,7 @@ + @@ -224,7 +225,6 @@ - diff --git a/src/Workspaces/CSharp/Portable/Execution/CSharpOptionsSerializationService.cs b/src/Workspaces/CSharp/Portable/Execution/CSharpOptionsSerializationService.cs index 6f8cc080687e5..0a7b8ff375875 100644 --- a/src/Workspaces/CSharp/Portable/Execution/CSharpOptionsSerializationService.cs +++ b/src/Workspaces/CSharp/Portable/Execution/CSharpOptionsSerializationService.cs @@ -84,12 +84,8 @@ public override ParseOptions ReadParseOptionsFrom(ObjectReader reader, Cancellat var languageVersion = (LanguageVersion)reader.ReadInt32(); var preprocessorSymbolNames = reader.ReadArray(); - var options = new CSharpParseOptions(languageVersion, documentationMode, kind); - - // use WithPreprocessorSymbols instead of constructor to bypass preprocessor validation. - // https://github.com/dotnet/roslyn/issues/15797 - return options.WithPreprocessorSymbols(preprocessorSymbolNames) - .WithFeatures(features); + var options = new CSharpParseOptions(languageVersion, documentationMode, kind, preprocessorSymbolNames); + return options.WithFeatures(features); } } } diff --git a/src/Workspaces/CSharp/Portable/Extensions/SyntaxNodeExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/SyntaxNodeExtensions.cs index abffa4590d59e..d1aa715744715 100644 --- a/src/Workspaces/CSharp/Portable/Extensions/SyntaxNodeExtensions.cs +++ b/src/Workspaces/CSharp/Portable/Extensions/SyntaxNodeExtensions.cs @@ -186,50 +186,6 @@ public static NamespaceDeclarationSyntax GetInnermostNamespaceDeclarationWithUsi } } - // Matches the following: - // - // (whitespace* newline)+ - private static readonly Matcher s_oneOrMoreBlankLines; - - // Matches the following: - // - // (whitespace* (single-comment|multi-comment) whitespace* newline)+ OneOrMoreBlankLines - private static readonly Matcher s_bannerMatcher; - - // Used to match the following: - // - // (whitespace* (single-comment|multi-comment) whitespace* newline)+ blankLine* - private static readonly Matcher s_fileBannerMatcher; - - static SyntaxNodeExtensions() - { - var whitespace = Matcher.Repeat(Match(SyntaxKind.WhitespaceTrivia, "\\b")); - var endOfLine = Match(SyntaxKind.EndOfLineTrivia, "\\n"); - var singleBlankLine = Matcher.Sequence(whitespace, endOfLine); - - var shebangComment = Match(SyntaxKind.ShebangDirectiveTrivia, "#!"); - var singleLineComment = Match(SyntaxKind.SingleLineCommentTrivia, "//"); - var multiLineComment = Match(SyntaxKind.MultiLineCommentTrivia, "/**/"); - var anyCommentMatcher = Matcher.Choice(shebangComment, singleLineComment, multiLineComment); - - var commentLine = Matcher.Sequence(whitespace, anyCommentMatcher, whitespace, endOfLine); - - s_oneOrMoreBlankLines = Matcher.OneOrMore(singleBlankLine); - s_bannerMatcher = - Matcher.Sequence( - Matcher.OneOrMore(commentLine), - s_oneOrMoreBlankLines); - s_fileBannerMatcher = - Matcher.Sequence( - Matcher.OneOrMore(commentLine), - Matcher.Repeat(singleBlankLine)); - } - - private static Matcher Match(SyntaxKind kind, string description) - { - return Matcher.Single(t => t.Kind() == kind, description); - } - public static IEnumerable GetAllPrecedingTriviaToPreviousToken( this SyntaxNode node, SourceText sourceText = null, bool includePreviousTokenTrailingTriviaOnlyIfOnSameLine = false) @@ -559,120 +515,26 @@ public static IList> SplitNodesOnPreprocessorBoundaries GetLeadingBlankLines( - this TSyntaxNode node) - where TSyntaxNode : SyntaxNode - { - node.GetNodeWithoutLeadingBlankLines(out var blankLines); - return blankLines; - } - - public static TSyntaxNode GetNodeWithoutLeadingBlankLines( - this TSyntaxNode node) - where TSyntaxNode : SyntaxNode - { - return node.GetNodeWithoutLeadingBlankLines(out var blankLines); - } - - public static TSyntaxNode GetNodeWithoutLeadingBlankLines( - this TSyntaxNode node, out IEnumerable strippedTrivia) - where TSyntaxNode : SyntaxNode - { - var leadingTriviaToKeep = new List(node.GetLeadingTrivia()); - - var index = 0; - s_oneOrMoreBlankLines.TryMatch(leadingTriviaToKeep, ref index); - - strippedTrivia = new List(leadingTriviaToKeep.Take(index)); - - return node.WithLeadingTrivia(leadingTriviaToKeep.Skip(index)); - } - - public static IEnumerable GetLeadingBannerAndPreprocessorDirectives( - this TSyntaxNode node) - where TSyntaxNode : SyntaxNode - { - node.GetNodeWithoutLeadingBannerAndPreprocessorDirectives(out var leadingTrivia); - return leadingTrivia; - } - - public static TSyntaxNode GetNodeWithoutLeadingBannerAndPreprocessorDirectives( - this TSyntaxNode node) - where TSyntaxNode : SyntaxNode - { - return node.GetNodeWithoutLeadingBannerAndPreprocessorDirectives(out var strippedTrivia); - } - - public static TSyntaxNode GetNodeWithoutLeadingBannerAndPreprocessorDirectives( - this TSyntaxNode node, out IEnumerable strippedTrivia) - where TSyntaxNode : SyntaxNode - { - var leadingTrivia = node.GetLeadingTrivia(); - - // Rules for stripping trivia: - // 1) If there is a pp directive, then it (and all preceding trivia) *must* be stripped. - // This rule supersedes all other rules. - // 2) If there is a doc comment, it cannot be stripped. Even if there is a doc comment, - // followed by 5 new lines, then the doc comment still must stay with the node. This - // rule does *not* supersede rule 1. - // 3) Single line comments in a group (i.e. with no blank lines between them) belong to - // the node *iff* there is no blank line between it and the following trivia. - - List leadingTriviaToStrip, leadingTriviaToKeep; - - int ppIndex = -1; - for (int i = leadingTrivia.Count - 1; i >= 0; i--) - { - if (SyntaxFacts.IsPreprocessorDirective(leadingTrivia[i].Kind())) - { - ppIndex = i; - break; - } - } - - if (ppIndex != -1) - { - // We have a pp directive. it (and all previous trivia) must be stripped. - leadingTriviaToStrip = new List(leadingTrivia.Take(ppIndex + 1)); - leadingTriviaToKeep = new List(leadingTrivia.Skip(ppIndex + 1)); - } - else - { - leadingTriviaToKeep = new List(leadingTrivia); - leadingTriviaToStrip = new List(); - } + public static ImmutableArray GetLeadingBlankLines(this TSyntaxNode node) where TSyntaxNode : SyntaxNode + => CSharpSyntaxFactsService.Instance.GetLeadingBlankLines(node); - // Now, consume as many banners as we can. s_fileBannerMatcher will only be matched at - // the start of the file. - var index = 0; - while ( - s_oneOrMoreBlankLines.TryMatch(leadingTriviaToKeep, ref index) || - s_bannerMatcher.TryMatch(leadingTriviaToKeep, ref index) || - (node.FullSpan.Start == 0 && s_fileBannerMatcher.TryMatch(leadingTriviaToKeep, ref index))) - { - } + public static TSyntaxNode GetNodeWithoutLeadingBlankLines(this TSyntaxNode node) where TSyntaxNode : SyntaxNode + => CSharpSyntaxFactsService.Instance.GetNodeWithoutLeadingBlankLines(node); - leadingTriviaToStrip.AddRange(leadingTriviaToKeep.Take(index)); + public static TSyntaxNode GetNodeWithoutLeadingBlankLines(this TSyntaxNode node, out ImmutableArray strippedTrivia) where TSyntaxNode : SyntaxNode + => CSharpSyntaxFactsService.Instance.GetNodeWithoutLeadingBlankLines(node, out strippedTrivia); - strippedTrivia = leadingTriviaToStrip; - return node.WithLeadingTrivia(leadingTriviaToKeep.Skip(index)); - } - - public static ImmutableArray GetFileBanner(this SyntaxNode root) - { - Debug.Assert(root.FullSpan.Start == 0); + public static ImmutableArray GetLeadingBannerAndPreprocessorDirectives(this TSyntaxNode node) where TSyntaxNode : SyntaxNode + => CSharpSyntaxFactsService.Instance.GetLeadingBannerAndPreprocessorDirectives(node); - var leadingTrivia = root.GetLeadingTrivia(); - var index = 0; - s_fileBannerMatcher.TryMatch(leadingTrivia.ToList(), ref index); + public static TSyntaxNode GetNodeWithoutLeadingBannerAndPreprocessorDirectives(this TSyntaxNode node) where TSyntaxNode : SyntaxNode + => CSharpSyntaxFactsService.Instance.GetNodeWithoutLeadingBannerAndPreprocessorDirectives(node); - return ImmutableArray.CreateRange(leadingTrivia.Take(index)); - } + public static TSyntaxNode GetNodeWithoutLeadingBannerAndPreprocessorDirectives(this TSyntaxNode node, out ImmutableArray strippedTrivia) where TSyntaxNode : SyntaxNode + => CSharpSyntaxFactsService.Instance.GetNodeWithoutLeadingBannerAndPreprocessorDirectives(node, out strippedTrivia); public static bool IsAnyAssignExpression(this SyntaxNode node) - { - return SyntaxFacts.IsAssignmentExpression(node.Kind()); - } + => SyntaxFacts.IsAssignmentExpression(node.Kind()); public static bool IsCompoundAssignExpression(this SyntaxNode node) { diff --git a/src/Workspaces/CSharp/Portable/Extensions/SyntaxTriviaExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/SyntaxTriviaExtensions.cs index 4a28bf071aedf..58c78b45c672c 100644 --- a/src/Workspaces/CSharp/Portable/Extensions/SyntaxTriviaExtensions.cs +++ b/src/Workspaces/CSharp/Portable/Extensions/SyntaxTriviaExtensions.cs @@ -45,19 +45,13 @@ public static bool IsRegularOrDocComment(this SyntaxTrivia trivia) } public static bool IsSingleLineComment(this SyntaxTrivia trivia) - { - return trivia.Kind() == SyntaxKind.SingleLineCommentTrivia; - } + => trivia.Kind() == SyntaxKind.SingleLineCommentTrivia; public static bool IsMultiLineComment(this SyntaxTrivia trivia) - { - return trivia.Kind() == SyntaxKind.MultiLineCommentTrivia; - } + => trivia.Kind() == SyntaxKind.MultiLineCommentTrivia; public static bool IsShebangDirective(this SyntaxTrivia trivia) - { - return trivia.Kind() == SyntaxKind.ShebangDirectiveTrivia; - } + => trivia.Kind() == SyntaxKind.ShebangDirectiveTrivia; public static bool IsCompleteMultiLineComment(this SyntaxTrivia trivia) { diff --git a/src/Workspaces/CSharp/Portable/LanguageServices/CSharpParseOptionsService.cs b/src/Workspaces/CSharp/Portable/LanguageServices/CSharpParseOptionsService.cs new file mode 100644 index 0000000000000..e482bfb8ed0be --- /dev/null +++ b/src/Workspaces/CSharp/Portable/LanguageServices/CSharpParseOptionsService.cs @@ -0,0 +1,24 @@ +// 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.Composition; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp +{ + [ExportLanguageService(typeof(IParseOptionsService), LanguageNames.CSharp), Shared] + internal class CSharpParseOptionsService : IParseOptionsService + { + public string GetLanguageVersion(ParseOptions options) => + ((CSharpParseOptions)options).SpecifiedLanguageVersion.ToDisplayString(); + + public ParseOptions WithLanguageVersion(ParseOptions options, string version) + { + var csharpOptions = (CSharpParseOptions)options; + Contract.ThrowIfFalse(version.TryParse(out var newVersion)); + + return csharpOptions.WithLanguageVersion(newVersion); + } + } +} diff --git a/src/Workspaces/CSharp/Portable/LanguageServices/CSharpSyntaxFactsService.cs b/src/Workspaces/CSharp/Portable/LanguageServices/CSharpSyntaxFactsService.cs index c193cd0063fce..99aa4fea77d5f 100644 --- a/src/Workspaces/CSharp/Portable/LanguageServices/CSharpSyntaxFactsService.cs +++ b/src/Workspaces/CSharp/Portable/LanguageServices/CSharpSyntaxFactsService.cs @@ -1029,9 +1029,7 @@ private string GetTypeName(TypeSyntax type) } private static string GetSimpleTypeName(SimpleNameSyntax name) - { - return name.Identifier.ValueText; - } + => name.Identifier.ValueText; private static string ExpandExplicitInterfaceName(string identifier, ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifier) { @@ -1832,12 +1830,24 @@ public SyntaxNode GetOperandOfPrefixUnaryExpression(SyntaxNode node) public SyntaxNode GetNextExecutableStatement(SyntaxNode statement) => ((StatementSyntax)statement).GetNextStatement(); - public bool IsWhitespaceTrivia(SyntaxTrivia trivia) + public override bool IsWhitespaceTrivia(SyntaxTrivia trivia) => trivia.IsWhitespace(); - public bool IsEndOfLineTrivia(SyntaxTrivia trivia) + public override bool IsEndOfLineTrivia(SyntaxTrivia trivia) => trivia.IsEndOfLine(); + public override bool IsSingleLineCommentTrivia(SyntaxTrivia trivia) + => trivia.IsSingleLineComment(); + + public override bool IsMultiLineCommentTrivia(SyntaxTrivia trivia) + => trivia.IsMultiLineComment(); + + public override bool IsShebangDirectiveTrivia(SyntaxTrivia trivia) + => trivia.IsShebangDirective(); + + public override bool IsPreprocessorDirective(SyntaxTrivia trivia) + => SyntaxFacts.IsPreprocessorDirective(trivia.Kind()); + private class AddFirstMissingCloseBaceRewriter: CSharpSyntaxRewriter { private readonly SyntaxNode _contextNode; @@ -1979,8 +1989,5 @@ public bool IsBetweenTypeMembers(SourceText sourceText, SyntaxNode root, int pos public ImmutableArray GetSelectedMembers(SyntaxNode root, TextSpan textSpan) => ImmutableArray.CastUp(root.GetMembersInSpan(textSpan)); - - public ImmutableArray GetFileBanner(SyntaxNode root) - => root.GetFileBanner(); } } \ No newline at end of file diff --git a/src/Workspaces/CSharp/Portable/Utilities/CompilationOptionsConversion.cs b/src/Workspaces/CSharp/Portable/Utilities/CompilationOptionsConversion.cs deleted file mode 100644 index ae4e53da04813..0000000000000 --- a/src/Workspaces/CSharp/Portable/Utilities/CompilationOptionsConversion.cs +++ /dev/null @@ -1,35 +0,0 @@ -// 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.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Microsoft.CodeAnalysis.CSharp.Utilities -{ - internal static class CompilationOptionsConversion - { - internal static LanguageVersion? GetLanguageVersion(string projectLanguageVersion) - { - switch ((projectLanguageVersion ?? string.Empty).ToLowerInvariant()) - { - case "iso-1": - return LanguageVersion.CSharp1; - case "iso-2": - return LanguageVersion.CSharp2; - default: - if (!string.IsNullOrEmpty(projectLanguageVersion)) - { - if (int.TryParse(projectLanguageVersion, out var version)) - { - return (LanguageVersion)version; - } - } - - // use default; - return null; - } - } - } -} diff --git a/src/Workspaces/Core/Desktop/SymbolSearch/SymbolSearchUpdateEngineFactory.cs b/src/Workspaces/Core/Desktop/SymbolSearch/SymbolSearchUpdateEngineFactory.cs index ccf5e3382f5d0..dbf289fb1a9af 100644 --- a/src/Workspaces/Core/Desktop/SymbolSearch/SymbolSearchUpdateEngineFactory.cs +++ b/src/Workspaces/Core/Desktop/SymbolSearch/SymbolSearchUpdateEngineFactory.cs @@ -22,7 +22,7 @@ public static async Task CreateEngineAsync( var outOfProcessAllowed = workspace.Options.GetOption(SymbolSearchOptions.OutOfProcessAllowed); if (outOfProcessAllowed) { - var client = await workspace.GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); + var client = await workspace.TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); if (client != null) { return new RemoteUpdateEngine(workspace, client, logService, cancellationToken); @@ -75,8 +75,12 @@ private async void OnConnectionChanged(object sender, bool connected) _sessionDoNotAccessDirectly?.Dispose(); _sessionDoNotAccessDirectly = null; - _client = await _workspace.GetRemoteHostClientAsync(CancellationToken.None).ConfigureAwait(false); - _client.ConnectionChanged += OnConnectionChanged; + _client = await _workspace.TryGetRemoteHostClientAsync(CancellationToken.None).ConfigureAwait(false); + if (_client != null) + { + // client can be null if host is shutting down + _client.ConnectionChanged += OnConnectionChanged; + } } } @@ -89,6 +93,12 @@ private async void OnConnectionChanged(object sender, bool connected) return _sessionDoNotAccessDirectly; } + if (_client == null) + { + // client can be null if host is shutting down + return null; + } + // We create a single session and use it for the entire lifetime of this process. // That single session will be used to do all communication with the remote process. // This is because each session will cause a new instance of the RemoteSymbolSearchUpdateEngine diff --git a/src/Workspaces/Core/Portable/Diagnostics/AbstractDiagnosticPropertiesService.cs b/src/Workspaces/Core/Portable/Diagnostics/AbstractDiagnosticPropertiesService.cs index 0a4ae2b9a1287..3a357955d4821 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/AbstractDiagnosticPropertiesService.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/AbstractDiagnosticPropertiesService.cs @@ -17,15 +17,28 @@ private ImmutableDictionary GetAdditionalProperties( Compilation compilation) { var assemblyIds = compilation.GetUnreferencedAssemblyIdentities(diagnostic); - if (assemblyIds.IsDefaultOrEmpty) + var requiredVersion = Compilation.GetRequiredLanguageVersion(diagnostic); + if (assemblyIds.IsDefaultOrEmpty && requiredVersion == null) { return null; } - var result = ImmutableDictionary.Empty; - return result.Add( - DiagnosticPropertyConstants.UnreferencedAssemblyIdentity, - assemblyIds[0].GetDisplayName()); + var result = ImmutableDictionary.CreateBuilder(); + if (!assemblyIds.IsDefaultOrEmpty) + { + result.Add( + DiagnosticPropertyConstants.UnreferencedAssemblyIdentity, + assemblyIds[0].GetDisplayName()); + } + + if (requiredVersion != null) + { + result.Add( + DiagnosticPropertyConstants.RequiredLanguageVersion, + requiredVersion); + } + + return result.ToImmutable(); } } } \ No newline at end of file diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticPropertyConstants.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticPropertyConstants.cs index 8fe11116bdced..18e2af34aaf73 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticPropertyConstants.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticPropertyConstants.cs @@ -7,5 +7,6 @@ namespace Microsoft.CodeAnalysis.Diagnostics internal static class DiagnosticPropertyConstants { public const string UnreferencedAssemblyIdentity = nameof(UnreferencedAssemblyIdentity); + public const string RequiredLanguageVersion = nameof(RequiredLanguageVersion); } } \ No newline at end of file diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Remote.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Remote.cs index 44e09e272e586..3af943376df79 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Remote.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Remote.cs @@ -80,7 +80,7 @@ private static async Task FindReferencesInServiceProcessAsync( IImmutableSet documents, CancellationToken cancellationToken) { - var client = await solution.Workspace.GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); + var client = await solution.Workspace.TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); if (client == null) { await FindReferencesInCurrentProcessAsync( @@ -133,7 +133,7 @@ private static async Task TryFindLiteralReferencesInServiceProcessAsync( IStreamingFindLiteralReferencesProgress progress, CancellationToken cancellationToken) { - var client = await solution.Workspace.GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); + var client = await solution.Workspace.TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); if (client == null) { return false; diff --git a/src/Workspaces/Core/Portable/LanguageServices/SyntaxFactsService/AbstractSyntaxFactsService.cs b/src/Workspaces/Core/Portable/LanguageServices/SyntaxFactsService/AbstractSyntaxFactsService.cs index 12fb8258c100b..648c96df0c501 100644 --- a/src/Workspaces/Core/Portable/LanguageServices/SyntaxFactsService/AbstractSyntaxFactsService.cs +++ b/src/Workspaces/Core/Portable/LanguageServices/SyntaxFactsService/AbstractSyntaxFactsService.cs @@ -1,5 +1,9 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServices @@ -17,6 +21,53 @@ internal abstract class AbstractSyntaxFactsService private readonly static ObjectPool> s_aliasMapPool = new ObjectPool>(() => new Dictionary(StringComparer.OrdinalIgnoreCase)); + // Matches the following: + // + // (whitespace* newline)+ + private readonly Matcher _oneOrMoreBlankLines; + + // Matches the following: + // + // (whitespace* (single-comment|multi-comment) whitespace* newline)+ OneOrMoreBlankLines + private readonly Matcher _bannerMatcher; + + // Used to match the following: + // + // (whitespace* (single-comment|multi-comment) whitespace* newline)+ blankLine* + private readonly Matcher _fileBannerMatcher; + + protected AbstractSyntaxFactsService() + { + var whitespace = Matcher.Repeat( + Matcher.Single(IsWhitespaceTrivia, "\\b")); + var endOfLine = Matcher.Single(IsEndOfLineTrivia, "\\n"); + var singleBlankLine = Matcher.Sequence(whitespace, endOfLine); + + var shebangComment = Matcher.Single(IsShebangDirectiveTrivia, "#!"); + var singleLineComment = Matcher.Single(IsSingleLineCommentTrivia, "//"); + var multiLineComment = Matcher.Single(IsMultiLineCommentTrivia, "/**/"); + var anyCommentMatcher = Matcher.Choice(shebangComment, singleLineComment, multiLineComment); + + var commentLine = Matcher.Sequence(whitespace, anyCommentMatcher, whitespace, endOfLine); + + _oneOrMoreBlankLines = Matcher.OneOrMore(singleBlankLine); + _bannerMatcher = + Matcher.Sequence( + Matcher.OneOrMore(commentLine), + _oneOrMoreBlankLines); + _fileBannerMatcher = + Matcher.Sequence( + Matcher.OneOrMore(commentLine), + Matcher.Repeat(singleBlankLine)); + } + + public abstract bool IsWhitespaceTrivia(SyntaxTrivia trivia); + public abstract bool IsEndOfLineTrivia(SyntaxTrivia trivia); + public abstract bool IsSingleLineCommentTrivia(SyntaxTrivia trivia); + public abstract bool IsMultiLineCommentTrivia(SyntaxTrivia trivia); + public abstract bool IsShebangDirectiveTrivia(SyntaxTrivia trivia); + public abstract bool IsPreprocessorDirective(SyntaxTrivia trivia); + protected static List> AllocateAliasMapList() { return s_aliasMapListPool.Allocate(); @@ -47,5 +98,113 @@ protected static Dictionary AllocateAliasMap() { return s_aliasMapPool.Allocate(); } + + public ImmutableArray GetLeadingBlankLines(TSyntaxNode node) + where TSyntaxNode : SyntaxNode + { + GetNodeWithoutLeadingBlankLines(node, out var blankLines); + return blankLines; + } + + public TSyntaxNode GetNodeWithoutLeadingBlankLines(TSyntaxNode node) + where TSyntaxNode : SyntaxNode + { + return GetNodeWithoutLeadingBlankLines(node, out var blankLines); + } + + public TSyntaxNode GetNodeWithoutLeadingBlankLines( + TSyntaxNode node, out ImmutableArray strippedTrivia) + where TSyntaxNode : SyntaxNode + { + var leadingTriviaToKeep = new List(node.GetLeadingTrivia()); + + var index = 0; + _oneOrMoreBlankLines.TryMatch(leadingTriviaToKeep, ref index); + + strippedTrivia = leadingTriviaToKeep.Take(index).ToImmutableArray(); + + return node.WithLeadingTrivia(leadingTriviaToKeep.Skip(index)); + } + + public ImmutableArray GetLeadingBannerAndPreprocessorDirectives(TSyntaxNode node) + where TSyntaxNode : SyntaxNode + { + GetNodeWithoutLeadingBannerAndPreprocessorDirectives(node, out var leadingTrivia); + return leadingTrivia; + } + + public TSyntaxNode GetNodeWithoutLeadingBannerAndPreprocessorDirectives( + TSyntaxNode node) + where TSyntaxNode : SyntaxNode + { + return GetNodeWithoutLeadingBannerAndPreprocessorDirectives(node, out var strippedTrivia); + } + + public TSyntaxNode GetNodeWithoutLeadingBannerAndPreprocessorDirectives( + TSyntaxNode node, out ImmutableArray strippedTrivia) + where TSyntaxNode : SyntaxNode + { + var leadingTrivia = node.GetLeadingTrivia(); + + // Rules for stripping trivia: + // 1) If there is a pp directive, then it (and all preceding trivia) *must* be stripped. + // This rule supersedes all other rules. + // 2) If there is a doc comment, it cannot be stripped. Even if there is a doc comment, + // followed by 5 new lines, then the doc comment still must stay with the node. This + // rule does *not* supersede rule 1. + // 3) Single line comments in a group (i.e. with no blank lines between them) belong to + // the node *iff* there is no blank line between it and the following trivia. + + List leadingTriviaToStrip, leadingTriviaToKeep; + + var ppIndex = -1; + for (var i = leadingTrivia.Count - 1; i >= 0; i--) + { + if (this.IsPreprocessorDirective(leadingTrivia[i])) + { + ppIndex = i; + break; + } + } + + if (ppIndex != -1) + { + // We have a pp directive. it (and all previous trivia) must be stripped. + leadingTriviaToStrip = new List(leadingTrivia.Take(ppIndex + 1)); + leadingTriviaToKeep = new List(leadingTrivia.Skip(ppIndex + 1)); + } + else + { + leadingTriviaToKeep = new List(leadingTrivia); + leadingTriviaToStrip = new List(); + } + + // Now, consume as many banners as we can. s_fileBannerMatcher will only be matched at + // the start of the file. + var index = 0; + + while ( + _oneOrMoreBlankLines.TryMatch(leadingTriviaToKeep, ref index) || + _bannerMatcher.TryMatch(leadingTriviaToKeep, ref index) || + (node.FullSpan.Start == 0 && _fileBannerMatcher.TryMatch(leadingTriviaToKeep, ref index))) + { + } + + leadingTriviaToStrip.AddRange(leadingTriviaToKeep.Take(index)); + + strippedTrivia = leadingTriviaToStrip.ToImmutableArray(); + return node.WithLeadingTrivia(leadingTriviaToKeep.Skip(index)); + } + + public ImmutableArray GetFileBanner(SyntaxNode root) + { + Debug.Assert(root.FullSpan.Start == 0); + + var leadingTrivia = root.GetLeadingTrivia(); + var index = 0; + _fileBannerMatcher.TryMatch(leadingTrivia.ToList(), ref index); + + return ImmutableArray.CreateRange(leadingTrivia.Take(index)); + } } -} +} \ No newline at end of file diff --git a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt index c77a5aa04cda4..a54e978ac9479 100644 --- a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt @@ -50,6 +50,7 @@ Microsoft.CodeAnalysis.XmlDocumentationProvider.XmlDocumentationProvider() -> vo abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.AddEventHandler(Microsoft.CodeAnalysis.SyntaxNode event, Microsoft.CodeAnalysis.SyntaxNode handler) -> Microsoft.CodeAnalysis.SyntaxNode abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.GetSwitchSections(Microsoft.CodeAnalysis.SyntaxNode switchStatement) -> System.Collections.Generic.IReadOnlyList abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.InsertSwitchSections(Microsoft.CodeAnalysis.SyntaxNode switchStatement, int index, System.Collections.Generic.IEnumerable switchSections) -> Microsoft.CodeAnalysis.SyntaxNode +abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.LockStatement(Microsoft.CodeAnalysis.SyntaxNode expression, System.Collections.Generic.IEnumerable statements) -> Microsoft.CodeAnalysis.SyntaxNode abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.RemoveEventHandler(Microsoft.CodeAnalysis.SyntaxNode event, Microsoft.CodeAnalysis.SyntaxNode handler) -> Microsoft.CodeAnalysis.SyntaxNode abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.ThrowExpression(Microsoft.CodeAnalysis.SyntaxNode expression) -> Microsoft.CodeAnalysis.SyntaxNode abstract Microsoft.CodeAnalysis.XmlDocumentationProvider.GetSourceStream(System.Threading.CancellationToken cancellationToken) -> System.IO.Stream @@ -95,7 +96,6 @@ override Microsoft.CodeAnalysis.Options.DocumentOptionSet.WithChangedOption(Micr override Microsoft.CodeAnalysis.XmlDocumentationProvider.GetDocumentationForSymbol(string documentationMemberID, System.Globalization.CultureInfo preferredCulture, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> string static Microsoft.CodeAnalysis.CodeStyle.CodeStyleOption.Default.get -> Microsoft.CodeAnalysis.CodeStyle.CodeStyleOption static Microsoft.CodeAnalysis.CodeStyle.CodeStyleOption.FromXElement(System.Xml.Linq.XElement element) -> Microsoft.CodeAnalysis.CodeStyle.CodeStyleOption -abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.LockStatement(Microsoft.CodeAnalysis.SyntaxNode expression, System.Collections.Generic.IEnumerable statements) -> Microsoft.CodeAnalysis.SyntaxNode static Microsoft.CodeAnalysis.Simplification.SimplificationOptions.QualifyEventAccess.get -> Microsoft.CodeAnalysis.Options.PerLanguageOption static Microsoft.CodeAnalysis.Simplification.SimplificationOptions.QualifyFieldAccess.get -> Microsoft.CodeAnalysis.Options.PerLanguageOption static Microsoft.CodeAnalysis.Simplification.SimplificationOptions.QualifyMethodAccess.get -> Microsoft.CodeAnalysis.Options.PerLanguageOption @@ -115,4 +115,5 @@ static readonly Microsoft.CodeAnalysis.CodeStyle.NotificationOption.Suggestion - static readonly Microsoft.CodeAnalysis.CodeStyle.NotificationOption.Warning -> Microsoft.CodeAnalysis.CodeStyle.NotificationOption virtual Microsoft.CodeAnalysis.CodeActions.CodeAction.Tags.get -> System.Collections.Immutable.ImmutableArray virtual Microsoft.CodeAnalysis.Editing.SyntaxGenerator.MemberAccessExpression(Microsoft.CodeAnalysis.SyntaxNode expression, Microsoft.CodeAnalysis.SyntaxNode memberName) -> Microsoft.CodeAnalysis.SyntaxNode -virtual Microsoft.CodeAnalysis.Workspace.ApplyDocumentInfoChanged(Microsoft.CodeAnalysis.DocumentId id, Microsoft.CodeAnalysis.DocumentInfo info) -> void \ No newline at end of file +virtual Microsoft.CodeAnalysis.Workspace.ApplyDocumentInfoChanged(Microsoft.CodeAnalysis.DocumentId id, Microsoft.CodeAnalysis.DocumentInfo info) -> void +virtual Microsoft.CodeAnalysis.Workspace.CanApplyParseOptionChange(Microsoft.CodeAnalysis.ParseOptions oldOptions, Microsoft.CodeAnalysis.ParseOptions newOptions, Microsoft.CodeAnalysis.Project project) -> bool \ No newline at end of file diff --git a/src/Workspaces/Core/Portable/Remote/DefaultRemoteHostClientServiceFactory.RemoteHostClientService.cs b/src/Workspaces/Core/Portable/Remote/DefaultRemoteHostClientServiceFactory.RemoteHostClientService.cs index 0e3e62fc5a27f..efb4b7b2fbfc1 100644 --- a/src/Workspaces/Core/Portable/Remote/DefaultRemoteHostClientServiceFactory.RemoteHostClientService.cs +++ b/src/Workspaces/Core/Portable/Remote/DefaultRemoteHostClientServiceFactory.RemoteHostClientService.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.Threading; using System.Threading.Tasks; using Roslyn.Utilities; @@ -25,6 +26,11 @@ public RemoteHostClientService(Workspace workspace) } public Task GetRemoteHostClientAsync(CancellationToken cancellationToken) + { + return TryGetRemoteHostClientAsync(cancellationToken); + } + + public Task TryGetRemoteHostClientAsync(CancellationToken cancellationToken) { if (_lazyInstance == null) { diff --git a/src/Workspaces/Core/Portable/Remote/IRemoteHostClientService.cs b/src/Workspaces/Core/Portable/Remote/IRemoteHostClientService.cs index e65a6fa4c6070..3162e83453b95 100644 --- a/src/Workspaces/Core/Portable/Remote/IRemoteHostClientService.cs +++ b/src/Workspaces/Core/Portable/Remote/IRemoteHostClientService.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.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; @@ -11,6 +12,8 @@ namespace Microsoft.CodeAnalysis.Remote /// internal interface IRemoteHostClientService : IWorkspaceService { + [Obsolete("use TryGetRemoteHostClientAsync instead")] Task GetRemoteHostClientAsync(CancellationToken cancellationToken); + Task TryGetRemoteHostClientAsync(CancellationToken cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/Remote/RemoteHostClientExtensions.cs b/src/Workspaces/Core/Portable/Remote/RemoteHostClientExtensions.cs index e39f74d89d26c..b7089c9e5f3dc 100644 --- a/src/Workspaces/Core/Portable/Remote/RemoteHostClientExtensions.cs +++ b/src/Workspaces/Core/Portable/Remote/RemoteHostClientExtensions.cs @@ -37,10 +37,10 @@ internal static class RemoteHostClientExtensions WellKnownServiceHubServices.CodeAnalysisService, solution, callbackTarget, cancellationToken); } - public static Task GetRemoteHostClientAsync(this Workspace workspace, CancellationToken cancellationToken) + public static Task TryGetRemoteHostClientAsync(this Workspace workspace, CancellationToken cancellationToken) { var clientService = workspace.Services.GetService(); - return clientService?.GetRemoteHostClientAsync(cancellationToken); + return clientService?.TryGetRemoteHostClientAsync(cancellationToken); } public static Task RunOnRemoteHostAsync( diff --git a/src/Workspaces/Core/Portable/Workspace/Host/ParseOptions/IParseOptionsService.cs b/src/Workspaces/Core/Portable/Workspace/Host/ParseOptions/IParseOptionsService.cs new file mode 100644 index 0000000000000..7050105b191eb --- /dev/null +++ b/src/Workspaces/Core/Portable/Workspace/Host/ParseOptions/IParseOptionsService.cs @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.CodeAnalysis.Host +{ + internal interface IParseOptionsService : ILanguageService + { + string GetLanguageVersion(ParseOptions options); + ParseOptions WithLanguageVersion(ParseOptions old, string version); + } +} diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace.cs b/src/Workspaces/Core/Portable/Workspace/Workspace.cs index db4bf8235197b..110ac003a3424 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace.cs @@ -1113,7 +1113,9 @@ private void CheckAllowedProjectChanges(ProjectChanges projectChanges) } if (projectChanges.OldProject.ParseOptions != projectChanges.NewProject.ParseOptions - && !this.CanApplyChange(ApplyChangesKind.ChangeParseOptions)) + && !this.CanApplyChange(ApplyChangesKind.ChangeParseOptions) + && !this.CanApplyParseOptionChange( + projectChanges.OldProject.ParseOptions, projectChanges.NewProject.ParseOptions, projectChanges.NewProject)) { throw new NotSupportedException(WorkspacesResources.Changing_parse_options_is_not_supported); } @@ -1186,6 +1188,9 @@ private void CheckAllowedProjectChanges(ProjectChanges projectChanges) } } + protected virtual bool CanApplyParseOptionChange(ParseOptions oldOptions, ParseOptions newOptions, Project project) + => false; + /// /// This method is called during for each project /// that has been added, removed or changed. diff --git a/src/Workspaces/Core/Portable/Workspaces.csproj b/src/Workspaces/Core/Portable/Workspaces.csproj index 0d56a26fb2b1f..6493b4da176b0 100644 --- a/src/Workspaces/Core/Portable/Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Workspaces.csproj @@ -406,6 +406,7 @@ + diff --git a/src/Workspaces/CoreTest/WorkspaceTests/MSBuildWorkspaceTestBase.cs b/src/Workspaces/CoreTest/WorkspaceTests/MSBuildWorkspaceTestBase.cs index efbb90a9f885b..966594f4ac91a 100644 --- a/src/Workspaces/CoreTest/WorkspaceTests/MSBuildWorkspaceTestBase.cs +++ b/src/Workspaces/CoreTest/WorkspaceTests/MSBuildWorkspaceTestBase.cs @@ -14,25 +14,25 @@ public class MSBuildWorkspaceTestBase : WorkspaceTestBase { protected const string MSBuildNamespace = "http://schemas.microsoft.com/developer/msbuild/2003"; - protected void AssertOptions(T expected, Func actual) + protected void AssertCSCompilationOptions(T expected, Func actual) { var options = LoadCSharpCompilationOptions(); Assert.Equal(expected, actual(options)); } - protected void AssertOptions(T expected, Func actual) + protected void AssertCSParseOptions(T expected, Func actual) { var options = LoadCSharpParseOptions(); Assert.Equal(expected, actual(options)); } - protected void AssertVBOptions(T expected, Func actual) + protected void AssertVBCompilationOptions(T expected, Func actual) { var options = LoadVisualBasicCompilationOptions(); Assert.Equal(expected, actual(options)); } - protected void AssertVBOptions(T expected, Func actual) + protected void AssertVBParseOptions(T expected, Func actual) { var options = LoadVisualBasicParseOptions(); Assert.Equal(expected, actual(options)); diff --git a/src/Workspaces/CoreTest/WorkspaceTests/MSBuildWorkspaceTests.cs b/src/Workspaces/CoreTest/WorkspaceTests/MSBuildWorkspaceTests.cs index 6deabadfffcd0..cef5a03d23ca3 100644 --- a/src/Workspaces/CoreTest/WorkspaceTests/MSBuildWorkspaceTests.cs +++ b/src/Workspaces/CoreTest/WorkspaceTests/MSBuildWorkspaceTests.cs @@ -1373,175 +1373,175 @@ public void TestOpenProject_AddVBDefaultReferences() public void TTestCompilationOptions_CSharp_DebugType_Full() { CreateCSharpFilesWith("DebugType", "full"); - AssertOptions(0, options => options.Errors.Length); + AssertCSParseOptions(0, options => options.Errors.Length); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_CSharp_DebugType_None() { CreateCSharpFilesWith("DebugType", "none"); - AssertOptions(0, options => options.Errors.Length); + AssertCSParseOptions(0, options => options.Errors.Length); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_CSharp_DebugType_PDBOnly() { CreateCSharpFilesWith("DebugType", "pdbonly"); - AssertOptions(0, options => options.Errors.Length); + AssertCSParseOptions(0, options => options.Errors.Length); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_CSharp_DebugType_Portable() { CreateCSharpFilesWith("DebugType", "portable"); - AssertOptions(0, options => options.Errors.Length); + AssertCSParseOptions(0, options => options.Errors.Length); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_CSharp_DebugType_Embedded() { CreateCSharpFilesWith("DebugType", "embedded"); - AssertOptions(0, options => options.Errors.Length); + AssertCSParseOptions(0, options => options.Errors.Length); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_CSharp_OutputKind_DynamicallyLinkedLibrary() { CreateCSharpFilesWith("OutputType", "Library"); - AssertOptions(OutputKind.DynamicallyLinkedLibrary, options => options.OutputKind); + AssertCSCompilationOptions(OutputKind.DynamicallyLinkedLibrary, options => options.OutputKind); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_CSharp_OutputKind_ConsoleApplication() { CreateCSharpFilesWith("OutputType", "Exe"); - AssertOptions(OutputKind.ConsoleApplication, options => options.OutputKind); + AssertCSCompilationOptions(OutputKind.ConsoleApplication, options => options.OutputKind); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_CSharp_OutputKind_WindowsApplication() { CreateCSharpFilesWith("OutputType", "WinExe"); - AssertOptions(OutputKind.WindowsApplication, options => options.OutputKind); + AssertCSCompilationOptions(OutputKind.WindowsApplication, options => options.OutputKind); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_CSharp_OutputKind_NetModule() { CreateCSharpFilesWith("OutputType", "Module"); - AssertOptions(OutputKind.NetModule, options => options.OutputKind); + AssertCSCompilationOptions(OutputKind.NetModule, options => options.OutputKind); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_CSharp_OptimizationLevel_Release() { CreateCSharpFilesWith("Optimize", "True"); - AssertOptions(OptimizationLevel.Release, options => options.OptimizationLevel); + AssertCSCompilationOptions(OptimizationLevel.Release, options => options.OptimizationLevel); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_CSharp_OptimizationLevel_Debug() { CreateCSharpFilesWith("Optimize", "False"); - AssertOptions(OptimizationLevel.Debug, options => options.OptimizationLevel); + AssertCSCompilationOptions(OptimizationLevel.Debug, options => options.OptimizationLevel); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_CSharp_MainFileName() { CreateCSharpFilesWith("StartupObject", "Foo"); - AssertOptions("Foo", options => options.MainTypeName); + AssertCSCompilationOptions("Foo", options => options.MainTypeName); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_CSharp_AssemblyOriginatorKeyFile_SignAssembly_Missing() { CreateCSharpFiles(); - AssertOptions(null, options => options.CryptoKeyFile); + AssertCSCompilationOptions(null, options => options.CryptoKeyFile); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_CSharp_AssemblyOriginatorKeyFile_SignAssembly_False() { CreateCSharpFilesWith("SignAssembly", "false"); - AssertOptions(null, options => options.CryptoKeyFile); + AssertCSCompilationOptions(null, options => options.CryptoKeyFile); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_CSharp_AssemblyOriginatorKeyFile_SignAssembly_True() { CreateCSharpFilesWith("SignAssembly", "true"); - AssertOptions("snKey.snk", options => Path.GetFileName(options.CryptoKeyFile)); + AssertCSCompilationOptions("snKey.snk", options => Path.GetFileName(options.CryptoKeyFile)); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_CSharp_AssemblyOriginatorKeyFile_DelaySign_False() { CreateCSharpFilesWith("DelaySign", "false"); - AssertOptions(null, options => options.DelaySign); + AssertCSCompilationOptions(null, options => options.DelaySign); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_CSharp_AssemblyOriginatorKeyFile_DelaySign_True() { CreateCSharpFilesWith("DelaySign", "true"); - AssertOptions(true, options => options.DelaySign); + AssertCSCompilationOptions(true, options => options.DelaySign); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_CSharp_CheckOverflow_True() { CreateCSharpFilesWith("CheckForOverflowUnderflow", "true"); - AssertOptions(true, options => options.CheckOverflow); + AssertCSCompilationOptions(true, options => options.CheckOverflow); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_CSharp_CheckOverflow_False() { CreateCSharpFilesWith("CheckForOverflowUnderflow", "false"); - AssertOptions(false, options => options.CheckOverflow); + AssertCSCompilationOptions(false, options => options.CheckOverflow); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestParseOptions_CSharp_Compatibility_ECMA1() { CreateCSharpFilesWith("LangVersion", "ISO-1"); - AssertOptions(Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp1, options => options.LanguageVersion); + AssertCSParseOptions(Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp1, options => options.LanguageVersion); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestParseOptions_CSharp_Compatibility_ECMA2() { CreateCSharpFilesWith("LangVersion", "ISO-2"); - AssertOptions(Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp2, options => options.LanguageVersion); + AssertCSParseOptions(Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp2, options => options.LanguageVersion); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestParseOptions_CSharp_Compatibility_None() { CreateCSharpFilesWith("LangVersion", "3"); - AssertOptions(Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp3, options => options.LanguageVersion); + AssertCSParseOptions(Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp3, options => options.LanguageVersion); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestParseOptions_CSharp_LanguageVersion_Latest() { CreateCSharpFiles(); - AssertOptions(Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp7, options => options.LanguageVersion); + AssertCSParseOptions(Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp7, options => options.LanguageVersion); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestParseOptions_CSharp_PreprocessorSymbols() { CreateCSharpFilesWith("DefineConstants", "DEBUG;TRACE;X;Y"); - AssertOptions("DEBUG,TRACE,X,Y", options => string.Join(",", options.PreprocessorSymbolNames)); + AssertCSParseOptions("DEBUG,TRACE,X,Y", options => string.Join(",", options.PreprocessorSymbolNames)); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestConfigurationDebug() { CreateCSharpFiles(); - AssertOptions("DEBUG,TRACE", options => string.Join(",", options.PreprocessorSymbolNames)); + AssertCSParseOptions("DEBUG,TRACE", options => string.Join(",", options.PreprocessorSymbolNames)); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] @@ -1562,35 +1562,35 @@ public void TestConfigurationRelease() public void TestCompilationOptions_VisualBasic_DebugType_Full() { CreateVBFilesWith("DebugType", "full"); - AssertVBOptions(0, options => options.Errors.Length); + AssertVBParseOptions(0, options => options.Errors.Length); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_VisualBasic_DebugType_None() { CreateVBFilesWith("DebugType", "none"); - AssertVBOptions(0, options => options.Errors.Length); + AssertVBParseOptions(0, options => options.Errors.Length); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_VisualBasic_DebugType_PDBOnly() { CreateVBFilesWith("DebugType", "pdbonly"); - AssertVBOptions(0, options => options.Errors.Length); + AssertVBParseOptions(0, options => options.Errors.Length); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_VisualBasic_DebugType_Portable() { CreateVBFilesWith("DebugType", "portable"); - AssertVBOptions(0, options => options.Errors.Length); + AssertVBParseOptions(0, options => options.Errors.Length); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_VisualBasic_DebugType_Embedded() { CreateVBFilesWith("DebugType", "embedded"); - AssertVBOptions(0, options => options.Errors.Length); + AssertVBParseOptions(0, options => options.Errors.Length); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] @@ -1598,42 +1598,42 @@ public void TestCompilationOptions_VisualBasic_VBRuntime_Embed() { CreateFiles(GetMultiProjectSolutionFiles() .WithFile(@"VisualBasicProject\VisualBasicProject.vbproj", GetResourceText("VisualBasicProject_VisualBasicProject_Embed.vbproj"))); - AssertVBOptions(true, options => options.EmbedVbCoreRuntime); + AssertVBCompilationOptions(true, options => options.EmbedVbCoreRuntime); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_VisualBasic_OutputKind_DynamicallyLinkedLibrary() { CreateVBFilesWith("OutputType", "Library"); - AssertVBOptions(OutputKind.DynamicallyLinkedLibrary, options => options.OutputKind); + AssertVBCompilationOptions(OutputKind.DynamicallyLinkedLibrary, options => options.OutputKind); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_VisualBasic_OutputKind_ConsoleApplication() { CreateVBFilesWith("OutputType", "Exe"); - AssertVBOptions(OutputKind.ConsoleApplication, options => options.OutputKind); + AssertVBCompilationOptions(OutputKind.ConsoleApplication, options => options.OutputKind); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_VisualBasic_OutputKind_WindowsApplication() { CreateVBFilesWith("OutputType", "WinExe"); - AssertVBOptions(OutputKind.WindowsApplication, options => options.OutputKind); + AssertVBCompilationOptions(OutputKind.WindowsApplication, options => options.OutputKind); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_VisualBasic_OutputKind_NetModule() { CreateVBFilesWith("OutputType", "Module"); - AssertVBOptions(OutputKind.NetModule, options => options.OutputKind); + AssertVBCompilationOptions(OutputKind.NetModule, options => options.OutputKind); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_VisualBasic_RootNamespace() { CreateVBFilesWith("RootNamespace", "Foo.Bar"); - AssertVBOptions("Foo.Bar", options => options.RootNamespace); + AssertVBCompilationOptions("Foo.Bar", options => options.RootNamespace); } [Fact(Skip = "https://github.com/dotnet/roslyn/issues/16301")] @@ -1641,84 +1641,84 @@ public void TestCompilationOptions_VisualBasic_RootNamespace() public void TestCompilationOptions_VisualBasic_OptionStrict_On() { CreateVBFilesWith("OptionStrict", "On"); - AssertVBOptions(Microsoft.CodeAnalysis.VisualBasic.OptionStrict.On, options => options.OptionStrict); + AssertVBCompilationOptions(Microsoft.CodeAnalysis.VisualBasic.OptionStrict.On, options => options.OptionStrict); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_VisualBasic_OptionStrict_Off() { CreateVBFilesWith("OptionStrict", "Off"); - AssertVBOptions(Microsoft.CodeAnalysis.VisualBasic.OptionStrict.Off, options => options.OptionStrict); + AssertVBCompilationOptions(Microsoft.CodeAnalysis.VisualBasic.OptionStrict.Off, options => options.OptionStrict); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_VisualBasic_OptionStrict_Custom() { CreateVBFilesWith("OptionStrict", "Custom"); - AssertVBOptions(Microsoft.CodeAnalysis.VisualBasic.OptionStrict.Custom, options => options.OptionStrict); + AssertVBCompilationOptions(Microsoft.CodeAnalysis.VisualBasic.OptionStrict.Custom, options => options.OptionStrict); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_VisualBasic_OptionInfer_True() { CreateVBFilesWith("OptionInfer", "On"); - AssertVBOptions(true, options => options.OptionInfer); + AssertVBCompilationOptions(true, options => options.OptionInfer); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_VisualBasic_OptionInfer_False() { CreateVBFilesWith("OptionInfer", "Off"); - AssertVBOptions(false, options => options.OptionInfer); + AssertVBCompilationOptions(false, options => options.OptionInfer); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_VisualBasic_OptionExplicit_True() { CreateVBFilesWith("OptionExplicit", "On"); - AssertVBOptions(true, options => options.OptionExplicit); + AssertVBCompilationOptions(true, options => options.OptionExplicit); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_VisualBasic_OptionExplicit_False() { CreateVBFilesWith("OptionExplicit", "Off"); - AssertVBOptions(false, options => options.OptionExplicit); + AssertVBCompilationOptions(false, options => options.OptionExplicit); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_VisualBasic_OptionCompareText_True() { CreateVBFilesWith("OptionCompare", "Text"); - AssertVBOptions(true, options => options.OptionCompareText); + AssertVBCompilationOptions(true, options => options.OptionCompareText); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_VisualBasic_OptionCompareText_False() { CreateVBFilesWith("OptionCompare", "Binary"); - AssertVBOptions(false, options => options.OptionCompareText); + AssertVBCompilationOptions(false, options => options.OptionCompareText); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_VisualBasic_OptionRemoveIntegerOverflowChecks_True() { CreateVBFilesWith("RemoveIntegerChecks", "true"); - AssertVBOptions(false, options => options.CheckOverflow); + AssertVBCompilationOptions(false, options => options.CheckOverflow); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_VisualBasic_OptionRemoveIntegerOverflowChecks_False() { CreateVBFilesWith("RemoveIntegerChecks", "false"); - AssertVBOptions(true, options => options.CheckOverflow); + AssertVBCompilationOptions(true, options => options.CheckOverflow); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestCompilationOptions_VisualBasic_OptionAssemblyOriginatorKeyFile_SignAssemblyFalse() { CreateVBFilesWith("SignAssembly", "false"); - AssertVBOptions(null, options => options.CryptoKeyFile); + AssertVBCompilationOptions(null, options => options.CryptoKeyFile); } [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] diff --git a/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicCodeGenerationService.vb b/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicCodeGenerationService.vb index 5f34913909f7e..045d7e60f80b4 100644 --- a/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicCodeGenerationService.vb +++ b/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicCodeGenerationService.vb @@ -483,8 +483,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Dim statementArray = statements.OfType(Of StatementSyntax).ToArray() Dim newBlock As SyntaxNode If options.BeforeThisLocation IsNot Nothing Then - Dim strippedTrivia As IEnumerable(Of SyntaxTrivia) = Nothing - Dim newStatement = oldStatement.GetNodeWithoutLeadingBannerAndPreprocessorDirectives(strippedTrivia) + Dim strippedTrivia As ImmutableArray(Of SyntaxTrivia) = Nothing + Dim newStatement = VisualBasicSyntaxFactsService.Instance.GetNodeWithoutLeadingBannerAndPreprocessorDirectives( + oldStatement, strippedTrivia) statementArray(0) = statementArray(0).WithLeadingTrivia(strippedTrivia) diff --git a/src/Workspaces/VisualBasic/Portable/Extensions/SyntaxNodeExtensions.vb b/src/Workspaces/VisualBasic/Portable/Extensions/SyntaxNodeExtensions.vb index 50b068e3457c0..5c4ff14cd5ef3 100644 --- a/src/Workspaces/VisualBasic/Portable/Extensions/SyntaxNodeExtensions.vb +++ b/src/Workspaces/VisualBasic/Portable/Extensions/SyntaxNodeExtensions.vb @@ -188,44 +188,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions Return Contract.FailWithReturn(Of SyntaxList(Of StatementSyntax))("unknown statements container!") End Function - ' Matches the following: - ' - ' (whitespace* newline)+ - Private ReadOnly s_oneOrMoreBlankLines As Matcher(Of SyntaxTrivia) - - ' Matches the following: - ' - ' (whitespace* comment whitespace* newline)+ OneOrMoreBlankLines - Private ReadOnly s_bannerMatcher As Matcher(Of SyntaxTrivia) - - ' Used to match the following: - ' - ' (whitespace* comment whitespace* newline)+ blankLine* - Private ReadOnly s_fileBannerMatcher As Matcher(Of SyntaxTrivia) - - Sub New() - Dim whitespace = Matcher.Repeat(Match(SyntaxKind.WhitespaceTrivia, "\\b")) - Dim endOfLine = Match(SyntaxKind.EndOfLineTrivia, "\\n") - Dim singleBlankLine = Matcher.Sequence(whitespace, endOfLine) - - Dim comment = Match(SyntaxKind.CommentTrivia, "'") - Dim commentLine = Matcher.Sequence(whitespace, comment, whitespace, endOfLine) - - s_oneOrMoreBlankLines = Matcher.OneOrMore(singleBlankLine) - s_bannerMatcher = - Matcher.Sequence( - Matcher.OneOrMore(commentLine), - s_oneOrMoreBlankLines) - s_fileBannerMatcher = - Matcher.Sequence( - Matcher.OneOrMore(commentLine), - Matcher.Repeat(singleBlankLine)) - End Sub - - Private Function Match(kind As SyntaxKind, description As String) As Matcher(Of SyntaxTrivia) - Return Matcher.Single(Of SyntaxTrivia)(Function(t) t.Kind = kind, description) - End Function - Friend Function IsMultiLineLambda(node As SyntaxNode) As Boolean Return SyntaxFacts.IsMultiLineLambdaExpression(node.Kind()) @@ -448,106 +410,34 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions Return False End Function - - Public Function GetLeadingBlankLines(Of TSyntaxNode As SyntaxNode)(node As TSyntaxNode) As IEnumerable(Of SyntaxTrivia) - Dim blankLines As IEnumerable(Of SyntaxTrivia) = Nothing - node.GetNodeWithoutLeadingBlankLines(blankLines) - Return blankLines + Public Function GetLeadingBlankLines(Of TSyntaxNode As SyntaxNode)(node As TSyntaxNode) As ImmutableArray(Of SyntaxTrivia) + Return VisualBasicSyntaxFactsService.Instance.GetLeadingBlankLines(node) End Function Public Function GetNodeWithoutLeadingBlankLines(Of TSyntaxNode As SyntaxNode)(node As TSyntaxNode) As TSyntaxNode - Dim blankLines As IEnumerable(Of SyntaxTrivia) = Nothing - Return node.GetNodeWithoutLeadingBlankLines(blankLines) + Return VisualBasicSyntaxFactsService.Instance.GetNodeWithoutLeadingBlankLines(node) End Function - Public Function GetNodeWithoutLeadingBlankLines(Of TSyntaxNode As SyntaxNode)( - node As TSyntaxNode, ByRef strippedTrivia As IEnumerable(Of SyntaxTrivia)) As TSyntaxNode - - Dim leadingTriviaToKeep = New List(Of SyntaxTrivia)(node.GetLeadingTrivia()) - - Dim index = 0 - s_oneOrMoreBlankLines.TryMatch(leadingTriviaToKeep, index) - - strippedTrivia = New List(Of SyntaxTrivia)(leadingTriviaToKeep.Take(index)) - - Return DirectCast(node.WithLeadingTrivia(leadingTriviaToKeep.Skip(index)), TSyntaxNode) + Public Function GetNodeWithoutLeadingBlankLines(Of TSyntaxNode As SyntaxNode)(node As TSyntaxNode, ByRef strippedTrivia As ImmutableArray(Of SyntaxTrivia)) As TSyntaxNode + Return VisualBasicSyntaxFactsService.Instance.GetNodeWithoutLeadingBlankLines(node, strippedTrivia) End Function - Public Function GetLeadingBannerAndPreprocessorDirectives(Of TSyntaxNode As SyntaxNode)(node As TSyntaxNode) As IEnumerable(Of SyntaxTrivia) - Dim leadingTrivia As IEnumerable(Of SyntaxTrivia) = Nothing - node.GetNodeWithoutLeadingBannerAndPreprocessorDirectives(leadingTrivia) - Return leadingTrivia + Public Function GetLeadingBannerAndPreprocessorDirectives(Of TSyntaxNode As SyntaxNode)(node As TSyntaxNode) As ImmutableArray(Of SyntaxTrivia) + Return VisualBasicSyntaxFactsService.Instance.GetLeadingBannerAndPreprocessorDirectives(node) End Function Public Function GetNodeWithoutLeadingBannerAndPreprocessorDirectives(Of TSyntaxNode As SyntaxNode)(node As TSyntaxNode) As TSyntaxNode - Dim strippedTrivia As IEnumerable(Of SyntaxTrivia) = Nothing - Return node.GetNodeWithoutLeadingBannerAndPreprocessorDirectives(strippedTrivia) + Return VisualBasicSyntaxFactsService.Instance.GetNodeWithoutLeadingBannerAndPreprocessorDirectives(node) End Function - Public Function GetNodeWithoutLeadingBannerAndPreprocessorDirectives(Of TSyntaxNode As SyntaxNode)( - node As TSyntaxNode, - ByRef strippedTrivia As IEnumerable(Of SyntaxTrivia)) As TSyntaxNode - - Dim leadingTrivia = node.GetLeadingTrivia() - - ' Rules for stripping trivia: - ' 1) If there is a pp directive, then it (and all preceding trivia) *must* be stripped. - ' This rule supersedes all other rules. - ' 2) If there is a doc comment, it cannot be stripped. Even if there is a doc comment, - ' followed by 5 new lines, then the doc comment still must stay with the node. This - ' rule does *not* supersede rule 1. - ' 3) Single line comments in a group (i.e. with no blank lines between them) belong to - ' the node *iff* there is no blank line between it and the following trivia. - - Dim leadingTriviaToStrip, leadingTriviaToKeep As List(Of SyntaxTrivia) - - Dim ppIndex = -1 - For i = leadingTrivia.Count - 1 To 0 Step -1 - If SyntaxFacts.IsPreprocessorDirective(leadingTrivia(i).Kind) Then - ppIndex = i - Exit For - End If - Next - - If ppIndex <> -1 Then - ' We have a pp directive. it (and all previous trivia) must be stripped. - leadingTriviaToStrip = New List(Of SyntaxTrivia)(leadingTrivia.Take(ppIndex + 1)) - leadingTriviaToKeep = New List(Of SyntaxTrivia)(leadingTrivia.Skip(ppIndex + 1)) - Else - leadingTriviaToKeep = New List(Of SyntaxTrivia)(leadingTrivia) - leadingTriviaToStrip = New List(Of SyntaxTrivia)() - End If - - ' Now, consume as many banners as we can. s_fileBannerMatcher will only be matched at - ' the start of the file. - Dim index = 0 - While ( - s_oneOrMoreBlankLines.TryMatch(leadingTriviaToKeep, index) OrElse - s_bannerMatcher.TryMatch(leadingTriviaToKeep, index) OrElse - (node.FullSpan.Start = 0 AndAlso s_fileBannerMatcher.TryMatch(leadingTriviaToKeep, index))) - End While - - leadingTriviaToStrip.AddRange(leadingTriviaToKeep.Take(index)) - - strippedTrivia = leadingTriviaToStrip - Return DirectCast(node.WithLeadingTrivia(leadingTriviaToKeep.Skip(index)), TSyntaxNode) - End Function - - - Public Function GetFileBanner(root As SyntaxNode) As ImmutableArray(Of SyntaxTrivia) - Debug.Assert(root.FullSpan.Start = 0) - - Dim leadingTrivia = root.GetLeadingTrivia() - Dim index = 0 - s_fileBannerMatcher.TryMatch(leadingTrivia.ToList(), index) - - Return ImmutableArray.CreateRange(leadingTrivia.Take(index)) + Public Function GetNodeWithoutLeadingBannerAndPreprocessorDirectives(Of TSyntaxNode As SyntaxNode)(node As TSyntaxNode, ByRef strippedTrivia As ImmutableArray(Of SyntaxTrivia)) As TSyntaxNode + Return VisualBasicSyntaxFactsService.Instance.GetNodeWithoutLeadingBannerAndPreprocessorDirectives(node, strippedTrivia) End Function ''' diff --git a/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSyntaxFactsService.vb b/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSyntaxFactsService.vb index 6f1a06a575501..c430075ec9983 100644 --- a/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSyntaxFactsService.vb +++ b/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSyntaxFactsService.vb @@ -1659,14 +1659,32 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return DirectCast(statement, StatementSyntax).GetNextStatement()?.FirstAncestorOrSelf(Of ExecutableStatementSyntax) End Function - Public Function IsWhitespaceTrivia(trivia As SyntaxTrivia) As Boolean Implements ISyntaxFactsService.IsWhitespaceTrivia + Public Overrides Function IsWhitespaceTrivia(trivia As SyntaxTrivia) As Boolean Implements ISyntaxFactsService.IsWhitespaceTrivia Return trivia.IsWhitespace() End Function - Public Function IsEndOfLineTrivia(trivia As SyntaxTrivia) As Boolean Implements ISyntaxFactsService.IsEndOfLineTrivia + Public Overrides Function IsEndOfLineTrivia(trivia As SyntaxTrivia) As Boolean Implements ISyntaxFactsService.IsEndOfLineTrivia Return trivia.IsEndOfLine() End Function + Public Overrides Function IsSingleLineCommentTrivia(trivia As SyntaxTrivia) As Boolean + Return trivia.Kind = SyntaxKind.CommentTrivia + End Function + + Public Overrides Function IsMultiLineCommentTrivia(trivia As SyntaxTrivia) As Boolean + ' VB does not have multi-line comments. + Return False + End Function + + Public Overrides Function IsShebangDirectiveTrivia(trivia As SyntaxTrivia) As Boolean + ' VB does not have shebang directives. + Return False + End Function + + Public Overrides Function IsPreprocessorDirective(trivia As SyntaxTrivia) As Boolean + Return SyntaxFacts.IsPreprocessorDirective(trivia.Kind()) + End Function + Public Function IsRegularComment(trivia As SyntaxTrivia) As Boolean Implements ISyntaxFactsService.IsRegularComment Return trivia.Kind = SyntaxKind.CommentTrivia End Function @@ -1734,8 +1752,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return ImmutableArray(Of SyntaxNode).CastUp(root.GetMembersInSpan(textSpan)) End Function - Public Function GetFileBanner(root As SyntaxNode) As ImmutableArray(Of SyntaxTrivia) Implements ISyntaxFactsService.GetFileBanner - Return root.GetFileBanner() + Private Function ISyntaxFactsService_GetFileBanner(root As SyntaxNode) As ImmutableArray(Of SyntaxTrivia) Implements ISyntaxFactsService.GetFileBanner + Return GetFileBanner(root) End Function End Class End Namespace \ No newline at end of file diff --git a/src/Workspaces/VisualBasic/Portable/Utilities/ImportsOrganizer.vb b/src/Workspaces/VisualBasic/Portable/Utilities/ImportsOrganizer.vb index cac8d4bc8c651..b90cb0182b1e9 100644 --- a/src/Workspaces/VisualBasic/Portable/Utilities/ImportsOrganizer.vb +++ b/src/Workspaces/VisualBasic/Portable/Utilities/ImportsOrganizer.vb @@ -2,6 +2,7 @@ Imports System Imports System.Collections.Generic +Imports System.Collections.Immutable Imports System.Globalization Imports System.Linq Imports System.Text @@ -16,7 +17,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports Microsoft.CodeAnalysis.VisualBasic.Utilities Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities - Friend Partial Class ImportsOrganizer + Partial Friend Class ImportsOrganizer Public Shared Function Organize([imports] As SyntaxList(Of ImportsStatementSyntax), placeSystemNamespaceFirst As Boolean) As SyntaxList(Of ImportsStatementSyntax) If [imports].Count > 1 Then @@ -24,7 +25,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities If Not [imports].SpansPreprocessorDirective() Then ' If there is a banner comment that precedes the nodes, ' then remove it and store it for later. - Dim leadingTrivia As IEnumerable(Of SyntaxTrivia) = Nothing + Dim leadingTrivia As ImmutableArray(Of SyntaxTrivia) = Nothing initialList(0) = initialList(0).GetNodeWithoutLeadingBannerAndPreprocessorDirectives(leadingTrivia) Dim comparer = If(placeSystemNamespaceFirst, ImportsStatementComparer.SystemFirstInstance, ImportsStatementComparer.NormalInstance)