Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
162 lines (127 sloc) 5.64 KB
// Copyright (c) Josef Pihrt. All rights reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Immutable;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Roslynator.CSharp.Syntax;
namespace Roslynator.CSharp.Analysis.MarkLocalVariableAsConst
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class LocalDeclarationStatementAnalyzer : BaseDiagnosticAnalyzer
{
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
{
get { return ImmutableArray.Create(DiagnosticDescriptors.MarkLocalVariableAsConst); }
}
public override void Initialize(AnalysisContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
base.Initialize(context);
context.RegisterSyntaxNodeAction(AnalyzeLocalDeclarationStatement, SyntaxKind.LocalDeclarationStatement);
}
public static void AnalyzeLocalDeclarationStatement(SyntaxNodeAnalysisContext context)
{
if (context.Node.ContainsDiagnostics)
return;
var localDeclaration = (LocalDeclarationStatementSyntax)context.Node;
if (localDeclaration.IsConst)
return;
StatementListInfo statementsInfo = SyntaxInfo.StatementListInfo(localDeclaration);
if (!statementsInfo.Success)
return;
SyntaxList<StatementSyntax> statements = statementsInfo.Statements;
if (statements.Count <= 1)
return;
int index = statements.IndexOf(localDeclaration);
if (index == statements.Count - 1)
return;
LocalDeclarationStatementInfo localInfo = SyntaxInfo.LocalDeclarationStatementInfo(localDeclaration);
if (!localInfo.Success)
return;
ITypeSymbol typeSymbol = context.SemanticModel.GetTypeSymbol(localInfo.Type, context.CancellationToken);
if (typeSymbol?.SupportsConstantValue() != true)
return;
if (localInfo.Type.IsVar && !typeSymbol.SupportsExplicitDeclaration())
return;
foreach (VariableDeclaratorSyntax declarator in localInfo.Variables)
{
if (!HasConstantValue(declarator.Initializer?.Value, typeSymbol, context.SemanticModel, context.CancellationToken))
return;
}
if (!CanBeMarkedAsConst(localInfo.Variables, statements, index + 1))
return;
DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.MarkLocalVariableAsConst, localInfo.Type);
}
private static bool CanBeMarkedAsConst(
SeparatedSyntaxList<VariableDeclaratorSyntax> variables,
SyntaxList<StatementSyntax> statements,
int startIndex)
{
MarkLocalVariableAsConstWalker walker = MarkLocalVariableAsConstWalkerCache.GetInstance();
foreach (VariableDeclaratorSyntax variable in variables)
walker.Identifiers.Add(variable.Identifier.ValueText);
for (int i = startIndex; i < statements.Count; i++)
{
walker.Visit(statements[i]);
if (walker.IsMatch)
{
MarkLocalVariableAsConstWalkerCache.Free(walker);
return false;
}
}
MarkLocalVariableAsConstWalkerCache.Free(walker);
return true;
}
private static bool HasConstantValue(
ExpressionSyntax expression,
ITypeSymbol typeSymbol,
SemanticModel semanticModel,
CancellationToken cancellationToken = default(CancellationToken))
{
if (expression?.IsMissing != false)
return false;
switch (typeSymbol.SpecialType)
{
case SpecialType.System_Boolean:
{
if (CSharpFacts.IsBooleanLiteralExpression(expression.Kind()))
return true;
break;
}
case SpecialType.System_Char:
{
if (expression.Kind() == SyntaxKind.CharacterLiteralExpression)
return true;
break;
}
case SpecialType.System_SByte:
case SpecialType.System_Byte:
case SpecialType.System_Int16:
case SpecialType.System_UInt16:
case SpecialType.System_Int32:
case SpecialType.System_UInt32:
case SpecialType.System_Int64:
case SpecialType.System_UInt64:
case SpecialType.System_Decimal:
case SpecialType.System_Single:
case SpecialType.System_Double:
{
if (expression.Kind() == SyntaxKind.NumericLiteralExpression)
return true;
break;
}
case SpecialType.System_String:
{
if (expression.Kind() == SyntaxKind.StringLiteralExpression)
return true;
break;
}
}
return semanticModel.HasConstantValue(expression, cancellationToken);
}
}
}
You can’t perform that action at this time.