Skip to content
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- Fix analyzer [RCS1187](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1187) to report a field consistently when the containing type has a static constructor that does not assign the field
- Fix analyzer [RCS1060](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1060) to not report a file that contains only multiple partial declarations of the same type ([PR](https://github.com/dotnet/roslynator/pull/1798))
- Fix analyzer [RCS1231](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1231) to not suggest `in` for `ref struct` parameters ([#1725](https://github.com/dotnet/roslynator/issues/1725)) ([PR](https://github.com/dotnet/roslynator/pull/1807))
- Fix analyzer [RCS1260](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1260) false positive for `omit_when_single_line` on multi-line object/collection initializers ([#1439](https://github.com/dotnet/roslynator/issues/1439)) ([PR](https://github.com/dotnet/roslynator/pull/1808))
Expand Down
96 changes: 28 additions & 68 deletions src/Analyzers/CSharp/Analysis/InlineLocalVariableAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,29 +154,19 @@ private static void AnalyzeLocalDeclarationStatement(SyntaxNodeAnalysisContext c
if (localSymbol?.IsErrorType() != false)
return;

ContainsLocalOrParameterReferenceWalker walker = null;
var walker = new ContainsLocalOrParameterReferenceWalker(localSymbol, context.SemanticModel, context.CancellationToken);

try
{
walker = ContainsLocalOrParameterReferenceWalker.GetInstance(localSymbol, context.SemanticModel, context.CancellationToken);

walker.Visit(forEachStatement.Statement);
walker.Visit(forEachStatement.Statement);

if (!walker.Result
&& index < statements.Count - 2)
{
walker.VisitList(statements, index + 2);
}

if (!walker.Result)
ReportDiagnostic(context, localDeclarationInfo, forEachStatement.Expression);
}
finally
if (!walker.Result
&& index < statements.Count - 2)
{
if (walker is not null)
ContainsLocalOrParameterReferenceWalker.Free(walker);
walker.VisitList(statements, index + 2);
}

if (!walker.Result)
ReportDiagnostic(context, localDeclarationInfo, forEachStatement.Expression);

break;
}
case SyntaxKind.SwitchStatement:
Expand All @@ -194,29 +184,19 @@ private static void AnalyzeLocalDeclarationStatement(SyntaxNodeAnalysisContext c
if (localSymbol?.IsErrorType() != false)
return;

ContainsLocalOrParameterReferenceWalker walker = null;
var walker = new ContainsLocalOrParameterReferenceWalker(localSymbol, context.SemanticModel, context.CancellationToken);

try
{
walker = ContainsLocalOrParameterReferenceWalker.GetInstance(localSymbol, context.SemanticModel, context.CancellationToken);

walker.VisitList(switchStatement.Sections);
walker.VisitList(switchStatement.Sections);

if (!walker.Result
&& index < statements.Count - 2)
{
walker.VisitList(statements, index + 2);
}

if (!walker.Result)
ReportDiagnostic(context, localDeclarationInfo, switchStatement.Expression);
}
finally
if (!walker.Result
&& index < statements.Count - 2)
{
if (walker is not null)
ContainsLocalOrParameterReferenceWalker.Free(walker);
walker.VisitList(statements, index + 2);
}

if (!walker.Result)
ReportDiagnostic(context, localDeclarationInfo, switchStatement.Expression);

break;
}
}
Expand Down Expand Up @@ -250,28 +230,18 @@ private static void Analyze(
if (localSymbol?.IsErrorType() != false)
return;

ContainsLocalOrParameterReferenceWalker walker = null;
var walker = new ContainsLocalOrParameterReferenceWalker(localSymbol, context.SemanticModel, context.CancellationToken);

try
{
walker = ContainsLocalOrParameterReferenceWalker.GetInstance(localSymbol, context.SemanticModel, context.CancellationToken);

walker.Visit(assignment.Left);
walker.Visit(assignment.Left);

if (!walker.Result
&& index < statements.Count - 2)
{
walker.VisitList(statements, index + 2);
}

if (!walker.Result)
ReportDiagnostic(context, localDeclarationInfo, identifierName);
}
finally
if (!walker.Result
&& index < statements.Count - 2)
{
if (walker is not null)
ContainsLocalOrParameterReferenceWalker.Free(walker);
walker.VisitList(statements, index + 2);
}

if (!walker.Result)
ReportDiagnostic(context, localDeclarationInfo, identifierName);
}

private static void Analyze(
Expand Down Expand Up @@ -306,22 +276,12 @@ private static void Analyze(

if (index < statements.Count - 2)
{
ContainsLocalOrParameterReferenceWalker walker = null;
var walker = new ContainsLocalOrParameterReferenceWalker(localSymbol, context.SemanticModel, context.CancellationToken);

try
{
walker = ContainsLocalOrParameterReferenceWalker.GetInstance(localSymbol, context.SemanticModel, context.CancellationToken);

walker.VisitList(statements, index + 2);
walker.VisitList(statements, index + 2);

if (walker.Result)
return;
}
finally
{
if (walker is not null)
ContainsLocalOrParameterReferenceWalker.Free(walker);
}
if (walker.Result)
return;
}

ReportDiagnostic(context, localDeclarationInfo, identifierName);
Expand Down
69 changes: 16 additions & 53 deletions src/Analyzers/CSharp/Analysis/MakeClassStaticAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,29 +68,11 @@ private static void AnalyzeClassDeclaration(SyntaxNodeAnalysisContext context)
if (!AnalyzeMembers(members))
return;

bool canBeMadeStatic;
MakeClassStaticWalker walker = null;
var walker = new MakeClassStaticWalker(symbol, context.SemanticModel, context.CancellationToken);

try
{
walker = MakeClassStaticWalker.GetInstance();

walker.CanBeMadeStatic = true;
walker.Symbol = symbol;
walker.SemanticModel = context.SemanticModel;
walker.CancellationToken = context.CancellationToken;

walker.Visit(classDeclaration);
walker.Visit(classDeclaration);

canBeMadeStatic = walker.CanBeMadeStatic;
}
finally
{
if (walker is not null)
MakeClassStaticWalker.Free(walker);
}

if (canBeMadeStatic)
if (walker.CanBeMadeStatic)
DiagnosticHelpers.ReportDiagnostic(context, DiagnosticRules.MakeClassStatic, classDeclaration.Identifier);
}

Expand Down Expand Up @@ -173,16 +155,23 @@ public static bool AnalyzeMembers(ImmutableArray<ISymbol> members)

private class MakeClassStaticWalker : TypeSyntaxWalker
{
[ThreadStatic]
private static MakeClassStaticWalker _cachedInstance;
public MakeClassStaticWalker(
INamedTypeSymbol symbol,
SemanticModel semanticModel,
CancellationToken cancellationToken)
{
Symbol = symbol;
SemanticModel = semanticModel;
CancellationToken = cancellationToken;
}

public bool CanBeMadeStatic { get; set; }
public bool CanBeMadeStatic { get; private set; } = true;

public INamedTypeSymbol Symbol { get; set; }
public INamedTypeSymbol Symbol { get; }

public SemanticModel SemanticModel { get; set; }
public SemanticModel SemanticModel { get; }

public CancellationToken CancellationToken { get; set; }
public CancellationToken CancellationToken { get; }

protected override bool ShouldVisit => CanBeMadeStatic;

Expand Down Expand Up @@ -210,31 +199,5 @@ protected override void VisitType(TypeSyntax node)
}
}
}

public static MakeClassStaticWalker GetInstance()
{
MakeClassStaticWalker walker = _cachedInstance;

if (walker is not null)
{
Debug.Assert(walker.Symbol is null);
Debug.Assert(walker.SemanticModel is null);
Debug.Assert(walker.CancellationToken == default);

_cachedInstance = null;
return walker;
}

return new MakeClassStaticWalker();
}

public static void Free(MakeClassStaticWalker walker)
{
walker.Symbol = null;
walker.SemanticModel = null;
walker.CancellationToken = default;

_cachedInstance = walker;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,22 +58,9 @@ private static void AnalyzeTypeDeclaration(SyntaxNodeAnalysisContext context)
if (typeDeclaration.Modifiers.Contains(SyntaxKind.PartialKeyword))
return;

MakeMemberReadOnlyWalker walker = null;
var walker = new MakeMemberReadOnlyWalker(context.SemanticModel, context.CancellationToken);

try
{
walker = MakeMemberReadOnlyWalker.GetInstance();

walker.SemanticModel = context.SemanticModel;
walker.CancellationToken = context.CancellationToken;

AnalyzeTypeDeclaration(context, typeDeclaration, walker);
}
finally
{
if (walker is not null)
MakeMemberReadOnlyWalker.Free(walker);
}
AnalyzeTypeDeclaration(context, typeDeclaration, walker);
}

private static void AnalyzeTypeDeclaration(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Copyright (c) .NET Foundation and Contributors. 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.Diagnostics;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
Expand All @@ -19,49 +17,17 @@ internal class MakeMemberReadOnlyWalker : AssignedExpressionWalker
private bool _isInInstanceConstructor;
private bool _isInStaticConstructor;

[ThreadStatic]
private static MakeMemberReadOnlyWalker _cachedInstance;

public SemanticModel SemanticModel { get; set; }

public CancellationToken CancellationToken { get; set; }

public Dictionary<string, (SyntaxNode, ISymbol)> Symbols { get; } = [];

public static MakeMemberReadOnlyWalker GetInstance()
public MakeMemberReadOnlyWalker(SemanticModel semanticModel, CancellationToken cancellationToken)
{
MakeMemberReadOnlyWalker walker = _cachedInstance;

if (walker is not null)
{
Debug.Assert(walker.Symbols.Count == 0);
Debug.Assert(walker.SemanticModel is null);
Debug.Assert(walker.CancellationToken == default);

_cachedInstance = null;
return walker;
}

return new MakeMemberReadOnlyWalker();
SemanticModel = semanticModel;
CancellationToken = cancellationToken;
}

public static void Free(MakeMemberReadOnlyWalker walker)
{
walker.Reset();
_cachedInstance = walker;
}
public SemanticModel SemanticModel { get; }

private void Reset()
{
Symbols.Clear();
SemanticModel = null;
CancellationToken = default;
_classOrStructDepth = 0;
_localFunctionDepth = 0;
_anonymousFunctionDepth = 0;
_isInInstanceConstructor = false;
_isInStaticConstructor = false;
}
public CancellationToken CancellationToken { get; }

public Dictionary<string, (SyntaxNode, ISymbol)> Symbols { get; } = [];

public override void VisitAssignedExpression(ExpressionSyntax expression)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,35 +100,22 @@ private static bool CanBeMarkedAsConst(
SyntaxList<StatementSyntax> statements,
int startIndex)
{
MarkLocalVariableAsConstWalker walker = null;
var walker = new MarkLocalVariableAsConstWalker(context.SemanticModel, context.CancellationToken);

try
foreach (VariableDeclaratorSyntax variable in variables)
{
walker = MarkLocalVariableAsConstWalker.GetInstance();
var symbol = context.SemanticModel.GetDeclaredSymbol(variable, context.CancellationToken) as ILocalSymbol;

walker.SemanticModel = context.SemanticModel;
walker.CancellationToken = context.CancellationToken;

foreach (VariableDeclaratorSyntax variable in variables)
{
var symbol = context.SemanticModel.GetDeclaredSymbol(variable, context.CancellationToken) as ILocalSymbol;

if (symbol is not null)
walker.Identifiers[variable.Identifier.ValueText] = symbol;
}

for (int i = startIndex; i < statements.Count; i++)
{
walker.Visit(statements[i]);

if (walker.Result)
return false;
}
if (symbol is not null)
walker.Identifiers[variable.Identifier.ValueText] = symbol;
}
finally

for (int i = startIndex; i < statements.Count; i++)
{
if (walker is not null)
MarkLocalVariableAsConstWalker.Free(walker);
walker.Visit(statements[i]);

if (walker.Result)
return false;
}

return true;
Expand Down
Loading
Loading