Пример #1
0
 public static void RegisterSyntaxNodeActionCatchable <TLanguageKindEnum>(this AnalysisContext context,
                                                                          Action <SyntaxNodeAnalysisContext> action, params TLanguageKindEnum[] syntaxKinds)
     where TLanguageKindEnum : struct
 {
     context.RegisterSyntaxNodeAction((c) =>
     {
         try
         {
             action(c);
         }
         catch (Exception e)
         {
             CommonAnalyzer.CommonReport(e.ToString());
         }
     }, syntaxKinds);
 }
Пример #2
0
        /// <summary>
        ///     Search recursively a given method to find whether it or any child call of it meets the specified predication
        /// </summary>
        public bool SearchMethod(SyntaxNodeAnalysisContext context, MethodDeclarationSyntax declare,
                                 Stack <SyntaxNode> callHierarchy, MarkInfo parent = null)
        {
            // =========================================
            // 1. some previous work before checking
            // =========================================

            // retrieve the symbol for the method declaration
            var method = RolsynExtensions.SemanticModelFor(context.SemanticModel, declare)?.GetDeclaredSymbol(declare);

            if (method == null)
            {
                return(false);
            }

            // if parent equals null, this is a starting node of a searching chain, so we reset the cached infomation about the searching stack
            if (parent == null)
            {
                _depth = 1;
                _searchStack.Clear();
                _searchStack.Push(method);
            }
            else
            {
                _depth++;
                _searchStack.Push(method);

                // we do this just in purpose of avoiding the potential probability of endless searching nesting in case of any accidential error occurring,
                // which could throw a StackOverflowException and shut down the application as a severe result.
                if (_depth > 32)
                {
                    Pop();
                    CommonAnalyzer.CommonReport("[UnityEngineAnalyzer] The Stack is ignored for its heavy depth: \n" +
                                                _searchStack.SymbolStackToString());
                    return(false);
                    //throw new Exception("[UnityEngineAnalyzer] The Stack is ignored for its heavy depth: \n" + _searchStack.SymbolStackToString());
                }
            }

            // check declaration exclusion
            if (declare.IsExcluded())
            {
                Pop();
                return(false);
            }

            // make sure the predication is not null
            if (predicate == null)
            {
                Pop();
                return(false);
            }

            // get the hashcode of this method (and check method validation)
            int methodCode = GetMethodHashCode(method);

            if (methodCode == 0)
            {
                Pop();
                return(false);
            }

            // make sure the call hierarchy is not null
            if (callHierarchy == null)
            {
                callHierarchy = new Stack <SyntaxNode>();
            }

            // ==========================================================
            // 2. check the cache dictionary to find out
            //    whether this method is ever checked
            //    and whether the marked infomation is still validate
            // ==========================================================

            // if we have already marked this method, check its syntaxTree to find whether the stored data is out-of-date
            // if not, just return the recorded value
            if (markDict.ContainsKey(methodCode))
            {
                var markInfo = markDict[methodCode];

                if (parent != null && !markInfo.parents.Contains(parent))
                {
                    markInfo.parents.Add(parent);
                }

                // if the stored SyntaxTree doesn't equal to the actual SyntaxTree,
                // we should update the stored data with the newest, setting all its parents as dirty
                if (!MethodHasSameSyntaxTree(method, markInfo.method))
                {
                    //CommonAnalyzer.CommonReport("update: " + method.ToString() + ", " + markInfo.method.GetHashCode() + " -> " + method.GetHashCode());
                    markInfo.Update(method);
                }
                // if the stored data is dirty, which means some of its children has been rebuilt,
                // we should not use the stored data as result directly and should reset this node
                // ( but not update it since it itself is still validate.
                //   if we still update it and set all its parents dirty, we may get stuck in a loop )
                else if (markInfo.dirty)
                {
                    //CommonAnalyzer.CommonReport("dirty: " + method.ToString());
                    markInfo.ResetMark();
                }
                // otherwise we use the stored data as result
                else
                {
                    if (markInfo.mark_value)
                    {
                        foreach (var call in markInfo.callstack)
                        {
                            callHierarchy.Push(call);
                        }
                    }

                    Pop();
                    return(markInfo.mark_value);
                }
            }
            else
            {
                // otherwise we mark this method with a default "false" value, since we might refer back to this method in codes below
                markDict[methodCode] = new MarkInfo(method);
                if (parent != null)
                {
                    markDict[methodCode].parents.Add(parent);
                }
            }


            // ==========================================================
            // 3. if the method is not marked in the cache dictionary,
            //    we check over the expressions within the declaration
            // ==========================================================

            // flag indicating whether we found any qualified node within this method
            SyntaxNode hitNode = null;

            // if the declaration meets the predication, we record it
            var declareSM = RolsynExtensions.SemanticModelFor(context.SemanticModel, declare);

            if (declareSM != null)
            {
                hitNode = predicate(declareSM, declare);
            }


            // ============================================================
            // 4. The last step is to check the declarations recursively
            //    of all the child invocations
            // ============================================================

            if (hitNode == null)
            {
                // if the iteration doesn't break above, meaning this declaration itself doesn't match the predication,
                // so we will check its child invocations.
                var childcalls = declare.DescendantNodes().OfType <InvocationExpressionSyntax>();
                if (childcalls != null)
                {
                    foreach (var childcall in childcalls)
                    {
                        if (childcall.IsExcluded())
                        {
                            continue;
                        }

                        // make sure the invocation is a valid method
                        var childMethodSymbol = declareSM?.GetSymbolInfo(childcall).Symbol as IMethodSymbol;
                        if (childMethodSymbol == null)
                        {
                            continue;
                        }

                        var childDeclare = childMethodSymbol.GetMethodDeclaration();
                        if (childDeclare == null)
                        {
                            continue;
                        }

                        // check the state this child invocation is marked as
                        var ret = SearchMethod(context, childDeclare, callHierarchy, markDict[methodCode]);

                        // if this child invocation is marked as "true", then record it and break.
                        if (ret)
                        {
                            hitNode = childcall;
                            break;
                        }
                    }
                }
            }

            // if this method is qualified, update the hierarchy stack and return
            if (hitNode != null)
            {
                callHierarchy.Push(hitNode);
                markDict[methodCode].Mark(callHierarchy);
            }

            Pop();
            return(hitNode != null);
        }