Analyzer to make Task`1.Result scary. (#3069)

This commit is contained in:
Pieter-Jan Briers
2022-07-23 12:33:55 +02:00
committed by GitHub
parent 0ec6995542
commit ec70abe928
8 changed files with 59 additions and 2 deletions

View File

@@ -8,6 +8,7 @@ public static class Diagnostics
public const string IdSerializable = "RA0001";
public const string IdAccess = "RA0002";
public const string IdExplicitVirtual = "RA0003";
public const string IdTaskResult = "RA0004";
public static SuppressionDescriptor MeansImplicitAssignment =>
new SuppressionDescriptor("RADC1000", "CS0649", "Marked as implicitly assigned.");

View File

@@ -0,0 +1,44 @@
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
namespace Robust.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class TaskResultAnalyzer : DiagnosticAnalyzer
{
[SuppressMessage("ReSharper", "RS2008")]
private static readonly DiagnosticDescriptor ResultRule = new DiagnosticDescriptor(
Diagnostics.IdTaskResult,
"Risk of deadlock from accessing Task<T>.Result",
"Accessing Task<T>.Result is dangerous and can cause deadlocks in some contexts. If you understand how this works and are certain that you aren't causing a deadlock here, mute this error with #pragma.",
"Usage",
DiagnosticSeverity.Error,
true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
ImmutableArray.Create(ResultRule);
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterOperationAction(Check, OperationKind.PropertyReference);
}
private static void Check(OperationAnalysisContext context)
{
var taskType = context.Compilation.GetTypeByMetadataName("System.Threading.Tasks.Task`1");
var operation = (IPropertyReferenceOperation) context.Operation;
var member = operation.Member;
if (member.Name == "Result" && taskType.Equals(member.ContainingType.ConstructedFrom, SymbolEqualityComparer.Default))
{
var diag = Diagnostic.Create(ResultRule, operation.Syntax.GetLocation());
context.ReportDiagnostic(diag);
}
}
}

View File

@@ -132,7 +132,9 @@ namespace Robust.Client.Graphics.Clyde
_cmdWriter.Complete();
// Drain command queue ignoring it until the window thread confirms completion.
#pragma warning disable RA0004
while (_eventReader.WaitToReadAsync().AsTask().Result)
#pragma warning restore RA0004
{
_eventReader.TryRead(out _);
}

View File

@@ -260,8 +260,9 @@ namespace Robust.Client.Graphics.Clyde
// Block the main thread (to avoid stuff like texture uploads being problematic).
WaitWindowCreate(task);
#pragma warning disable RA0004
var (reg, errorResult) = task.Result;
#pragma warning restore RA0004
if (reg != null)
{

View File

@@ -113,7 +113,9 @@ internal partial class Clyde
_cmdWriter.Complete();
// Drain command queue ignoring it until the window thread confirms completion.
#pragma warning disable RA0004
while (_eventReader.WaitToReadAsync().AsTask().Result)
#pragma warning restore RA0004
{
_eventReader.TryRead(out _);
}

View File

@@ -48,7 +48,10 @@ internal partial class Clyde
// Block the main thread (to avoid stuff like texture uploads being problematic).
WaitWindowCreate(task);
#pragma warning disable RA0004
// Block above ensured task is done, this is safe.
var (reg, error) = task.Result;
#pragma warning restore RA0004
if (reg != null)
{
reg.Owner = reg.Handle;

View File

@@ -170,7 +170,9 @@ namespace Robust.Shared.ContentPack
return true;
}
#pragma warning disable RA0004
var loadedConfig = _config.Result;
#pragma warning restore RA0004
// We still do explicit type reference scanning, even though the actual whitelists work with raw members.
// This is so that we can simplify handling of generic type specifications during member checking:
@@ -233,7 +235,9 @@ namespace Robust.Shared.ContentPack
}
});
#pragma warning disable RA0004
var loadedCfg = _config.Result;
#pragma warning restore RA0004
var verifyErrors = false;
foreach (var res in bag)

View File

@@ -373,7 +373,7 @@ namespace Robust.Shared.Network
Logger.DebugS("net", "First peer failed.");
firstPeer.Peer.Shutdown("You failed.");
_toCleanNetPeers.Add(firstPeer.Peer);
firstReason = firstPeerChanged.Result;
firstReason = await firstPeerChanged;
await secondPeerChanged;
winningPeer = secondPeer;
winningConnection = secondConnection;