diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index b935d933f..c08307f6e 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,19 +3,13 @@ "isRoot": true, "tools": { "powershell": { - "version": "7.3.8", + "version": "7.4.0", "commands": [ "pwsh" ] }, - "dotnet-format": { - "version": "5.1.250801", - "commands": [ - "dotnet-format" - ] - }, "dotnet-coverage": { - "version": "17.9.3", + "version": "17.9.5", "commands": [ "dotnet-coverage" ] @@ -27,4 +21,4 @@ ] } } -} +} \ No newline at end of file diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index cef7ac6f7..b680b1dbc 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,5 +1,5 @@ # Refer to https://hub.docker.com/_/microsoft-dotnet-sdk for available versions -FROM mcr.microsoft.com/dotnet/sdk:7.0.401-jammy +FROM mcr.microsoft.com/dotnet/sdk:8.0.100-jammy # Installing mono makes `dotnet test` work without errors even for net472. # But installing it takes a long time, so it's excluded by default. diff --git a/.editorconfig b/.editorconfig index d3f8ae0d7..c31ac04b9 100644 --- a/.editorconfig +++ b/.editorconfig @@ -40,6 +40,7 @@ indent_size = 4 [*.{cs,vb}] # Sort using and Import directives with System.* appearing first dotnet_sort_system_directives_first = true +dotnet_separate_import_directive_groups = false dotnet_style_qualification_for_field = true:warning dotnet_style_qualification_for_property = true:warning dotnet_style_qualification_for_method = true:warning diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 14f655e9f..6cc61b83c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,7 +8,4 @@ updates: schedule: interval: weekly ignore: - # This package has unlisted versions on nuget.org that are not supported. Avoid them. - - dependency-name: dotnet-format - versions: ["6.x", "7.x", "8.x"] - dependency-name: Microsoft.CodeAnalysis* # We intentionally target older VS versions. diff --git a/.vscode/settings.json b/.vscode/settings.json index 92408eb68..a4836b956 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,5 +5,9 @@ "omnisharp.enableEditorConfigSupport": true, "omnisharp.enableRoslynAnalyzers": true, "dotnet.completion.showCompletionItemsFromUnimportedNamespaces": true, - "dotnet.defaultSolution": "Microsoft.VisualStudio.Threading.sln" + "dotnet.defaultSolution": "Microsoft.VisualStudio.Threading.sln", + "editor.formatOnSave": true, + "[xml]": { + "editor.wordWrap": "off" + } } diff --git a/Directory.Build.props b/Directory.Build.props index 8c4bf662d..deab0b903 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,3 +1,4 @@ + Debug @@ -5,7 +6,7 @@ $(RepoRootPath)obj\$([MSBuild]::MakeRelative($(RepoRootPath), $(MSBuildProjectDirectory)))\ $(RepoRootPath)bin\$(MSBuildProjectName)\ $(RepoRootPath)bin\Packages\$(Configuration)\NuGet\ - 11 + 12 enable disable latest diff --git a/Directory.Build.targets b/Directory.Build.targets index ea7b6e6f8..4549a848c 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,3 +1,4 @@ + @@ -8,4 +9,6 @@ + + diff --git a/Directory.Packages.props b/Directory.Packages.props index a3bfa007d..5ed054c50 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,3 +1,4 @@ + @@ -24,7 +25,7 @@ - + @@ -34,16 +35,16 @@ - + - + - - + + @@ -56,6 +57,6 @@ - + diff --git a/azure-pipelines/Install-NuGetPackage.ps1 b/azure-pipelines/Install-NuGetPackage.ps1 index cdac73085..9afde0550 100644 --- a/azure-pipelines/Install-NuGetPackage.ps1 +++ b/azure-pipelines/Install-NuGetPackage.ps1 @@ -45,7 +45,7 @@ try { if ($PSCmdlet.ShouldProcess($PackageId, 'nuget install')) { $p = Start-Process $nugetPath $nugetArgs -NoNewWindow -Wait -PassThru - if ($p.ExitCode -ne 0) { throw } + if ($null -ne $p.ExitCode -and $p.ExitCode -ne 0) { throw } } # Provide the path to the installed package directory to our caller. diff --git a/azure-pipelines/NuGetSbom.targets b/azure-pipelines/NuGetSbom.targets new file mode 100644 index 000000000..a2599e882 --- /dev/null +++ b/azure-pipelines/NuGetSbom.targets @@ -0,0 +1,12 @@ + + + true + $(TargetsForTfmSpecificBuildOutput);IncludeSbomInNupkg + + + + + + + + diff --git a/azure-pipelines/artifacts/_stage_all.ps1 b/azure-pipelines/artifacts/_stage_all.ps1 index d81d16d46..b077931cc 100644 --- a/azure-pipelines/artifacts/_stage_all.ps1 +++ b/azure-pipelines/artifacts/_stage_all.ps1 @@ -30,6 +30,12 @@ function Create-SymbolicLink { } else { cmd /c "mklink `"$Link`" `"$Target`"" | Out-Null } + + if ($LASTEXITCODE -ne 0) { + # Windows requires admin privileges to create symbolic links + # unless Developer Mode has been enabled. + throw "Failed to create symbolic link at $Link that points to $Target" + } } # Stage all artifacts diff --git a/azure-pipelines/build.yml b/azure-pipelines/build.yml index 246ca87f2..4ffbdc39a 100644 --- a/azure-pipelines/build.yml +++ b/azure-pipelines/build.yml @@ -68,6 +68,9 @@ jobs: - template: dotnet.yml parameters: RunTests: ${{ parameters.RunTests }} + - script: dotnet format --verify-no-changes --no-restore + displayName: 💅 Verify formatted code + enabled: false - job: macOS condition: and(${{ parameters.includeMacOS }}, ne(variables['OptProf'], 'true')) diff --git a/azure-pipelines/variables/TeamName.ps1 b/azure-pipelines/variables/TeamName.ps1 index 5f2822c50..ba9c82310 100644 --- a/azure-pipelines/variables/TeamName.ps1 +++ b/azure-pipelines/variables/TeamName.ps1 @@ -1,2 +1,2 @@ -# This value is used to craft a \\cpvsbuild\drops path for symbol archival. +# This value is used as an input to the MicroBuild Insert VS task. 'VS IDE' diff --git a/global.json b/global.json index 7b603b9ba..e21f20327 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.401", + "version": "8.0.100", "rollForward": "patch", "allowPrerelease": false }, diff --git a/init.ps1 b/init.ps1 old mode 100644 new mode 100755 diff --git a/settings.VisualStudio.json b/settings.VisualStudio.json new file mode 100644 index 000000000..7abb4a060 --- /dev/null +++ b/settings.VisualStudio.json @@ -0,0 +1,3 @@ +{ + "textEditor.codeCleanup.profile": "profile1" +} diff --git a/src/Directory.Build.props b/src/Directory.Build.props index c66ce7837..74a5dbf11 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,3 +1,4 @@ + diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index c1d929a5b..07f413461 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -1,3 +1,4 @@ + diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/CommonFixes.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/CommonFixes.cs index c4c7b1838..79015ac8d 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/CommonFixes.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/CommonFixes.cs @@ -47,9 +47,9 @@ internal static async Task> ReadAdditionalFilesAsync(IEnu } IEnumerable? docs = from doc in additionalFiles.OrderBy(x => x.FilePath, StringComparer.Ordinal) - let fileName = Path.GetFileName(doc.Name) - where fileNamePattern.IsMatch(fileName) - select doc; + let fileName = Path.GetFileName(doc.Name) + where fileNamePattern.IsMatch(fileName) + select doc; ImmutableArray.Builder? result = ImmutableArray.CreateBuilder(); foreach (TextDocument? doc in docs) { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD011UseAsyncLazyAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD011UseAsyncLazyAnalyzer.cs index 44a599998..7147399f3 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD011UseAsyncLazyAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD011UseAsyncLazyAnalyzer.cs @@ -71,9 +71,9 @@ private void AnalyzeNode(OperationAnalysisContext context) if (firstArgExpression is IDelegateCreationOperation { Target: IAnonymousFunctionOperation anonFunc }) { System.Collections.Generic.IEnumerable? problems = from invocation in anonFunc.Descendants().OfType() - let invokedSymbol = invocation.TargetMethod - where invokedSymbol is object && CommonInterest.SyncBlockingMethods.Any(m => m.Method.IsMatch(invokedSymbol)) - select invocation; + let invokedSymbol = invocation.TargetMethod + where invokedSymbol is object && CommonInterest.SyncBlockingMethods.Any(m => m.Method.IsMatch(invokedSymbol)) + select invocation; IInvocationOperation? firstProblem = problems.FirstOrDefault(); if (firstProblem is object) { diff --git a/src/Microsoft.VisualStudio.Threading/AsyncLazy`1.cs b/src/Microsoft.VisualStudio.Threading/AsyncLazy`1.cs index a809391a8..0b98fc7ad 100644 --- a/src/Microsoft.VisualStudio.Threading/AsyncLazy`1.cs +++ b/src/Microsoft.VisualStudio.Threading/AsyncLazy`1.cs @@ -36,14 +36,14 @@ public class AsyncLazy private readonly object syncObject = new object(); /// - /// The unique instance identifier. + /// An optional means to avoid deadlocks when synchronous APIs are called that must invoke async methods in user code. /// - private AsyncLocal? recursiveFactoryCheck; + private readonly JoinableTaskFactory? jobFactory; /// - /// An optional means to avoid deadlocks when synchronous APIs are called that must invoke async methods in user code. + /// The unique instance identifier. /// - private readonly JoinableTaskFactory? jobFactory; + private AsyncLocal? recursiveFactoryCheck; /// /// The function to invoke to produce the task. diff --git a/src/Microsoft.VisualStudio.Threading/TplExtensions.cs b/src/Microsoft.VisualStudio.Threading/TplExtensions.cs index 7fe2a473d..9fdb6aa76 100644 --- a/src/Microsoft.VisualStudio.Threading/TplExtensions.cs +++ b/src/Microsoft.VisualStudio.Threading/TplExtensions.cs @@ -121,7 +121,7 @@ public static void ApplyResultTo(this Task task, TaskCompletionSource t /// The task whose completion should be applied to another. /// The task that should receive the completion status. public static void ApplyResultTo(this Task task, TaskCompletionSource tcs) - //// where T : defaultable + //// where T : defaultable { Requires.NotNull(task, nameof(task)); Requires.NotNull(tcs, nameof(tcs)); diff --git a/test/Directory.Build.props b/test/Directory.Build.props index 5e3cd0260..e8b338a78 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -1,3 +1,4 @@ + diff --git a/test/Directory.Build.targets b/test/Directory.Build.targets index 052fe3ef0..a6e0f4ace 100644 --- a/test/Directory.Build.targets +++ b/test/Directory.Build.targets @@ -1,3 +1,4 @@ + diff --git a/test/IsolatedTestHost/App.config b/test/IsolatedTestHost/App.config index 2a58d341c..f899e7a15 100644 --- a/test/IsolatedTestHost/App.config +++ b/test/IsolatedTestHost/App.config @@ -10,13 +10,13 @@ - + - + diff --git a/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/MultiAnalyzerTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/MultiAnalyzerTests.cs index b052dbc59..9d4a61437 100644 --- a/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/MultiAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/MultiAnalyzerTests.cs @@ -330,8 +330,8 @@ public class Test : CSharpCodeFixVerifier GetDiagnosticAnalyzers() { IEnumerable? analyzers = from type in typeof(VSTHRD002UseJtfRunAnalyzer).Assembly.GetTypes() - where type.GetCustomAttributes(typeof(DiagnosticAnalyzerAttribute), true).Any() - select (DiagnosticAnalyzer?)Activator.CreateInstance(type) ?? throw Assumes.Fail("Unable to instantiate the analyzer"); + where type.GetCustomAttributes(typeof(DiagnosticAnalyzerAttribute), true).Any() + select (DiagnosticAnalyzer?)Activator.CreateInstance(type) ?? throw Assumes.Fail("Unable to instantiate the analyzer"); return analyzers.ToImmutableArray(); } } diff --git a/test/Microsoft.VisualStudio.Threading.Tests/AsyncAutoResetEventTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/AsyncAutoResetEventTests.cs index fffb45d83..5bfb23e95 100644 --- a/test/Microsoft.VisualStudio.Threading.Tests/AsyncAutoResetEventTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/AsyncAutoResetEventTests.cs @@ -74,9 +74,9 @@ public void SetReturnsBeforeInlinedContinuations() .ContinueWith( delegate { - // Arrange to synchronously block the continuation until Set() has returned, - // which would deadlock if Set does not return until inlined continuations complete. - Assert.True(setReturned.Wait(AsyncDelay)); + // Arrange to synchronously block the continuation until Set() has returned, + // which would deadlock if Set does not return until inlined continuations complete. + Assert.True(setReturned.Wait(AsyncDelay)); }, TaskContinuationOptions.ExecuteSynchronously); this.evt.Set(); diff --git a/test/Microsoft.VisualStudio.Threading.Tests/AsyncReaderWriterLockTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/AsyncReaderWriterLockTests.cs index 4f0a78ab5..40aa53f35 100644 --- a/test/Microsoft.VisualStudio.Threading.Tests/AsyncReaderWriterLockTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/AsyncReaderWriterLockTests.cs @@ -4512,7 +4512,7 @@ await Task.WhenAll( await secondLockInQueue.SetAsync(); }), secondLockObtained.Task); - }); + }); } private Task UncontestedTopLevelLocksAllocFreeHelperAsync(Func locker, bool yieldingLock) diff --git a/test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskContextTests.cs b/test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskContextTests.cs index d0b00c42f..e50d64078 100644 --- a/test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskContextTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Tests/JoinableTaskContextTests.cs @@ -410,8 +410,8 @@ public void GetHangReportProducesDgmlWithNamedJoinableCollections() this.Logger.WriteLine(report.Content); var dgml = XDocument.Parse(report.Content); IEnumerable? collectionLabels = from node in dgml.Root!.Element(XName.Get("Nodes", DgmlNamespace))!.Elements() - where node.Attribute(XName.Get("Category"))?.Value == "Collection" - select node.Attribute(XName.Get("Label"))?.Value; + where node.Attribute(XName.Get("Category"))?.Value == "Collection" + select node.Attribute(XName.Get("Label"))?.Value; Assert.Contains(collectionLabels, label => label == jtcName); return Task.CompletedTask; }); @@ -433,8 +433,8 @@ public void GetHangReportProducesDgmlWithMethodNameRequestingMainThread() this.Logger.WriteLine(report.Content); var dgml = XDocument.Parse(report.Content); IEnumerable? collectionLabels = from node in dgml.Root!.Element(XName.Get("Nodes", DgmlNamespace))!.Elements() - where node.Attribute(XName.Get("Category"))?.Value == "Task" - select node.Attribute(XName.Get("Label"))?.Value; + where node.Attribute(XName.Get("Category"))?.Value == "Task" + select node.Attribute(XName.Get("Label"))?.Value; Assert.Contains(collectionLabels, label => label.Contains(nameof(this.GetHangReportProducesDgmlWithMethodNameRequestingMainThread))); } @@ -456,8 +456,8 @@ public void GetHangReportProducesDgmlWithMethodNameYieldingOnMainThread() this.Logger.WriteLine(report.Content); var dgml = XDocument.Parse(report.Content); IEnumerable? collectionLabels = from node in dgml.Root!.Element(XName.Get("Nodes", DgmlNamespace))!.Elements() - where node.Attribute(XName.Get("Category"))?.Value == "Task" - select node.Attribute(XName.Get("Label"))?.Value; + where node.Attribute(XName.Get("Category"))?.Value == "Task" + select node.Attribute(XName.Get("Label"))?.Value; Assert.Contains(collectionLabels, label => label.Contains(nameof(this.YieldingMethodAsync))); }); }