Files
SQLLint/SQLLinter/Infrastructure/Rules/SemicolonTerminationRule.cs

74 lines
2.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Microsoft.SqlServer.TransactSql.ScriptDom;
using SQLLinter.Common;
using SQLLinter.Infrastructure.Rules.Common;
using System.Text.RegularExpressions;
namespace SQLLinter.Infrastructure.Rules;
public class SemicolonTerminationRule : BaseRuleVisitor, IRule
{
private readonly IList<TSqlFragment> waitForStatements = new List<TSqlFragment>();
private readonly IList<TSqlFragment> functionReturnTypeSelectStatements = new List<TSqlFragment>();
private static Regex WhiteSpaceRegex = new Regex(@"\s", RegexOptions.Compiled);
private static Regex AllWhiteSpaceRegex = new Regex(@"^\s$", RegexOptions.Compiled);
// не принудительно завершать эти операторы точкой с запятой
private readonly Type[] typesToSkip =
{
typeof(BeginEndBlockStatement),
typeof(GoToStatement),
typeof(IndexDefinition),
typeof(LabelStatement),
typeof(WhileStatement),
typeof(IfStatement),
typeof(CreateViewStatement)
};
public override string Text => "Оператор не заканчивается точкой с запятой";
public override void Visit(WaitForStatement node)
{
waitForStatements.Add(node.Statement);
}
public override void Visit(CreateFunctionStatement node)
{
if (node.ReturnType is SelectFunctionReturnType returnType)
{
functionReturnTypeSelectStatements.Add(returnType.SelectStatement);
}
}
public override void Visit(TSqlStatement node)
{
if (Array.IndexOf(typesToSkip, node.GetType()) > -1 ||
EndsWithSemicolon(node) ||
waitForStatements.Contains(node) ||
functionReturnTypeSelectStatements.Contains(node))
{
return;
}
var dynamicSqlColumnOffset = GetDynamicSqlColumnOffset(node);
var (lastToken, column) = GetLastTokenAndColumn(node);
AddViolation(Name, Text, GetLineNumber(lastToken), column + dynamicSqlColumnOffset);
}
private static (TSqlParserToken, int) GetLastTokenAndColumn(TSqlStatement node)
{
var lastToken = node.ScriptTokenStream[node.LastTokenIndex];
var tabsOnLine = ColumnNumberCalculator.CountTabsBeforeToken(lastToken.Line, node.LastTokenIndex, node.ScriptTokenStream);
var column = ColumnNumberCalculator.GetColumnNumberAfterToken(tabsOnLine, lastToken);
return (lastToken, column);
}
private static bool EndsWithSemicolon(TSqlFragment node)
{
return node.ScriptTokenStream[node.LastTokenIndex].TokenType == TSqlTokenType.Semicolon
|| node.ScriptTokenStream[node.LastTokenIndex + 1].TokenType == TSqlTokenType.Semicolon;
}
}