public override void ExplicitVisit(UpdateStatement node)
        {
            TableNameIllegalCandidate candidate = CreateAndPushCandidate();

            base.ExplicitVisit(node);
            PopAndSetIllegalStatement(candidate);
        }
        private void PopAndSetIllegalStatement(TableNameIllegalCandidate candidate)
        {
            //本来はポップだけで問題ないが、スタック操作に失敗する気がするので、バリアを張っておく。
            if (candidate != candidateStack.Pop())
            {
                throw new InvalidOperationException("正しくスタックが動作していない。popのし忘れかpushのし忘れ");
            }
            //違反候補に上がっていて、Joinがないのなら規約違反確定。
            if (candidate.hasJoin == true)
            {
                //ZIP!
                IList <string> illegalTextList    = candidate.TextList;
                IList <string> illegalCommentList = candidate.CommentList;

                var illegalList = illegalTextList.Zip(illegalCommentList, (text, comment) => new { text = text, comment = comment });
                foreach (var illegal in illegalList)
                {
                    notificationList.Add(
                        NotificationLevel.Illegal,
                        DEFAULT_ILLEGAL_TYPE,
                        "JOINがあるのに [" + illegal.text + "] のテーブル宣言がない。\r\n" + illegal.comment,
                        illegal.text
                        );
                }
            }
        }
        //スタックに積む。詳しくはcandidateStack参照。
        private TableNameIllegalCandidate CreateAndPushCandidate()
        {
            TableNameIllegalCandidate candidate = new TableNameIllegalCandidate();

            candidateStack.Push(candidate);
            return(candidate);
        }
        //列を参照する場合に呼び出される。
        //MultiPartIdentifier.Countが1だった場合は、テーブルの宣言なしに列を指定しているということを意味するので違反候補に追加する。
        public override void ExplicitVisit(MultiPartIdentifier node)
        {
            if (isInsideColumnReference == true &&
                node.Count == 1 &&
                candidateStack.Count > 0 &&
                isLeftAssignmentSetClause == false)
            {
                TableNameIllegalCandidate candidate = candidateStack.Peek();
                candidate.AddText(node.Identifiers[0].Value);

                //commentには理解のために前後の文を追加する。

                IList <TSqlParserToken> tokenList = node.ScriptTokenStream;
                int    commentFirstIndex          = node.FirstTokenIndex - 10 < 0 ? 0 : node.FirstTokenIndex - 10;
                int    commentLastIndex           = node.FirstTokenIndex + 10 < tokenList.Count ? node.FirstTokenIndex + 10 : tokenList.Count;
                string comment = tokenList[node.FirstTokenIndex].Line.ToString() + "行目付近:";
                for (int i = commentFirstIndex; i < commentLastIndex; i++)
                {
                    comment = comment + tokenList[i].Text;
                }
                candidate.AddComment(comment);
            }

            base.ExplicitVisit(node);
            isLeftAssignmentSetClause = false;
        }
 //JOINが発生する場合に呼び出される。
 public override void ExplicitVisit(QualifiedJoin node)
 {
     if (candidateStack.Count > 0)
     {
         TableNameIllegalCandidate candidate = candidateStack.Peek();
         candidate.hasJoin = true;
     }
     base.ExplicitVisit(node);
 }