Пример #1
0
        /// <summary>
        /// Check if a previously analyzed instance of the shader can be used
        /// </summary>
        /// <param name="mixinInfo">the ModuleMixinInfo</param>
        private void CheckMixinForReplacement(ModuleMixinInfo mixinInfo)
        {
            // TODO: infinite loop when cross reference (composition & =stage for example)
            // TODO: change ReplacementChecked to enum None/InProgress/Done
            if (mixinInfo.ReplacementChecked)
            {
                return;
            }

            // Check parents and dependencies
            mixinInfo.MinimalContext.Where(x => x != mixinInfo).ForEach(CheckMixinForReplacement);

            foreach (var replaceCandidateMixinInfo in MixinInfos.Where(x => x != mixinInfo && x.ShaderSource.Equals(mixinInfo.ShaderSource) && x.HashPreprocessSource == mixinInfo.HashPreprocessSource))
            {
                if (replaceCandidateMixinInfo.Mixin.DependenciesStatus != AnalysisStatus.None)
                {
                    if (replaceCandidateMixinInfo.Mixin.MinimalContext != null)
                    {
                        var noNeedToReplaced = replaceCandidateMixinInfo.Mixin.MinimalContext
                                               .Where(dep => dep != replaceCandidateMixinInfo.Mixin)
                                               .All(dep => mixinInfo.MinimalContext.FirstOrDefault(x => x.Mixin == dep) != null);
                        if (noNeedToReplaced)
                        {
                            mixinInfo.Mixin            = replaceCandidateMixinInfo.Mixin;
                            mixinInfo.MixinAst         = replaceCandidateMixinInfo.MixinAst;
                            mixinInfo.MixinGenericName = replaceCandidateMixinInfo.MixinGenericName;
                            break;
                        }
                    }
                }
            }

            mixinInfo.ReplacementChecked = true;
        }
Пример #2
0
        /// <summary>
        /// Build the virtual table for the specified mixin
        /// </summary>
        /// <param name="mixinInfo">the mixin</param>
        private void BuildVirtualTables(ModuleMixinInfo mixinInfo)
        {
            var mixin = mixinInfo.Mixin;

            if (mixin.VirtualTableStatus == AnalysisStatus.Error || mixin.VirtualTableStatus == AnalysisStatus.Cyclic || mixin.VirtualTableStatus == AnalysisStatus.Complete)
            {
                return;
            }

            if (!mixinInfo.Instanciated)
            {
                return;
            }

            foreach (var dep in mixin.InheritanceList)
            {
                var depInfo = MixinInfos.FirstOrDefault(x => x.Mixin == dep);
                BuildVirtualTables(depInfo);
            }

            // merge the virtual tables
            foreach (var dep in mixin.InheritanceList)
            {
                mixin.VirtualTable.MergeWithLocalVirtualTable(dep.LocalVirtualTable, mixin.MixinName, ErrorWarningLog);
            }

            mixin.VirtualTable.MergeWithLocalVirtualTable(mixin.LocalVirtualTable, mixin.MixinName, ErrorWarningLog);

            foreach (var dep in mixin.InheritanceList)
            {
                mixin.VirtualTable.AddVirtualTable(dep.VirtualTable, dep.MixinName, ErrorWarningLog);
            }
            mixin.VirtualTable.AddFinalDeclarations(mixin.LocalVirtualTable.Methods.Select(x => x.Method).ToList(), mixin.MixinName, ErrorWarningLog);

            foreach (var variable in mixin.VirtualTable.Variables)
            {
                // Compare local against local and inherited against inherited
                var local = variable.Shader == mixin.Shader;

                if (mixin.VirtualTable.Variables.Any(x => (x.Shader == mixin.Shader) == local && x.Variable != variable.Variable && x.Variable.Name.Text == variable.Variable.Name.Text))
                {
                    mixin.PotentialConflictingVariables.Add(variable.Variable);
                }
            }
            foreach (var method in mixin.VirtualTable.Methods)
            {
                // Compare local against local and inherited against inherited
                var local = method.Shader == mixin.Shader;

                if (mixin.VirtualTable.Methods.Any(x => (x.Shader == mixin.Shader) == local && x.Method != method.Method && x.Method.IsSameSignature(method.Method)))
                {
                    mixin.PotentialConflictingMethods.Add(method.Method);
                }
            }

            CheckStageClass(mixin);

            mixin.VirtualTableStatus = AnalysisStatus.Complete;
        }
Пример #3
0
        public ModuleMixinInfo Copy(Xenko.Core.Shaders.Parser.ShaderMacro[] macros)
        {
            var mixinInfo = new ModuleMixinInfo();

            mixinInfo.ShaderSource         = ShaderSource;
            mixinInfo.MixinAst             = MixinAst;
            mixinInfo.MixinGenericName     = MixinGenericName;
            mixinInfo.Mixin                = Mixin;
            mixinInfo.Instanciated         = Instanciated;
            mixinInfo.HashPreprocessSource = HashPreprocessSource;
            mixinInfo.Macros               = macros;

            return(mixinInfo);
        }
Пример #4
0
        /// <summary>
        /// Build the ModuleMixinInfo class
        /// </summary>
        /// <param name="shaderSource">the ShaderSource to load</param>
        /// <param name="macros">the macros applied on the source</param>
        /// <returns>the ModuleMixinInfo</returns>
        private ModuleMixinInfo BuildMixinInfo(ShaderSource shaderSource, Xenko.Core.Shaders.Parser.ShaderMacro[] macros)
        {
            ModuleMixinInfo mixinInfo = null;

            if (shaderSource is ShaderClassCode)
            {
                var shaderClassSource = shaderSource as ShaderClassCode;
                mixinInfo = new ModuleMixinInfo {
                    ShaderSource = shaderClassSource, Macros = macros
                };
                LoadMixinFromClassSource(mixinInfo);
            }
            else if (shaderSource is ShaderMixinSource)
            {
                var shaderMixinSource = shaderSource as ShaderMixinSource;

                var shaderName = "Mix" + lastMixIndex;
                ++lastMixIndex;
                var fakeAst = new ShaderClassType(shaderName);
                foreach (var classSource in shaderMixinSource.Mixins)
                {
                    Identifier name;
                    if (classSource.GenericArguments != null && classSource.GenericArguments.Length > 0)
                    {
                        name = new IdentifierGeneric(classSource.ClassName, classSource.GenericArguments.Select(x => new Identifier(x.ToString())).ToArray());
                    }
                    else
                    {
                        name = new Identifier(classSource.ClassName);
                    }

                    fakeAst.BaseClasses.Add(new TypeName(name));
                }

                mixinInfo = new ModuleMixinInfo
                {
                    MixinGenericName = shaderName,
                    Macros           = macros,
                    MixinAst         = fakeAst,
                    ShaderSource     = shaderSource,
                    SourceHash       = ObjectId.FromBytes(Encoding.UTF8.GetBytes(shaderName)),
                    Instanciated     = true
                };
            }

            return(mixinInfo);
        }
Пример #5
0
        /// <summary>
        /// Loads the mixin based on its ShaderSource
        /// </summary>
        /// <param name="mixinInfo">the ModuleMixinInfo</param>
        private void LoadMixinFromClassSource(ModuleMixinInfo mixinInfo)
        {
            var classSource = (ShaderClassCode)mixinInfo.ShaderSource;

            // If we allow to parse non instantiated generics, put empty generic arguments to let the ShaderLoader correctly expand the class
            var shaderClass = ShaderLoader.LoadClassSource(classSource, mixinInfo.Macros, mixinInfo.Log, AllowNonInstantiatedGenerics);

            // If result is null, there was some errors while parsing.
            if (shaderClass == null)
            {
                return;
            }

            var shaderType = shaderClass.Type.DeepClone();

            if (shaderType.ShaderGenerics.Count > 0)
            {
                mixinInfo.Instanciated = false;
            }

            mixinInfo.HashPreprocessSource = shaderClass.PreprocessedSourceHash;
            mixinInfo.SourceHash           = shaderClass.SourceHash;

            if (!SourceHashes.ContainsKey(classSource.ClassName))
            {
                SourceHashes.Add(classSource.ClassName, shaderClass.SourceHash);
            }

            // check if it was a generic class and find out if the instanciation was correct
            if (shaderType.GenericParameters.Count > 0)
            {
                if (classSource.GenericArguments == null || classSource.GenericArguments.Length == 0 || shaderType.GenericParameters.Count > classSource.GenericArguments.Length)
                {
                    mixinInfo.Instanciated = false;
                    mixinInfo.Log.Error(XenkoMessageCode.ErrorClassSourceNotInstantiated, shaderType.Span, classSource.ClassName);
                }
                else
                {
                    ModuleMixinInfo.CleanIdentifiers(shaderType.GenericParameters.Select(x => x.Name).ToList());
                }
            }

            mixinInfo.MixinAst         = shaderType;
            mixinInfo.MixinGenericName = classSource.ClassName;
        }
Пример #6
0
        /// <summary>
        /// Loads generic classes that may appear in the mixin
        /// </summary>
        /// <param name="mixinInfo">The mixin to investigate</param>
        /// <param name="macros">The macros.</param>
        /// <param name="macrosString">The macros string.</param>
        private void LoadNecessaryShaders(ModuleMixinInfo mixinInfo, Xenko.Core.Shaders.Parser.ShaderMacro[] macros, string macrosString)
        {
            if (!mixinInfo.Instanciated)
            {
                return;
            }

            // Look for all the generic calls
            var shaderDependencyVisitor = new ShaderDependencyVisitor(mixinInfo.Log, ShaderLoader.SourceManager);

            shaderDependencyVisitor.Run(mixinInfo.MixinAst);

            foreach (var foundClass in shaderDependencyVisitor.FoundClasses)
            {
                var classSource    = new ShaderClassSource(foundClass, null);
                var foundMixinInfo = GetModuleMixinInfo(classSource, macros, macrosString);
                mixinInfo.MinimalContext.UnionWith(foundMixinInfo.MinimalContext);
            }

            foreach (var id in shaderDependencyVisitor.FoundIdentifiers)
            {
                var genericClass = id.Item1;
                ModuleMixinInfo.CleanIdentifiers(genericClass.Identifiers);
                var genericParams = BuildShaderGenericParameters(genericClass);
                var classSource   = new ShaderClassSource(genericClass.Text, genericParams);

                var instanciatedClassInfo = GetModuleMixinInfo(classSource, macros, macrosString);
                mixinInfo.MinimalContext.UnionWith(instanciatedClassInfo.MinimalContext);

                var newId = new Identifier(instanciatedClassInfo.MixinName);
                if (id.Item2 is TypeName) // in the baseclass list or in a variable declaration
                {
                    (id.Item2 as TypeName).Name = newId;
                }
                else if (id.Item2 is VariableReferenceExpression)
                {
                    (id.Item2 as VariableReferenceExpression).Name = newId;
                }
                else if (id.Item2 is MemberReferenceExpression)
                {
                    (id.Item2 as MemberReferenceExpression).Member = newId;
                }
            }
        }
Пример #7
0
        /// <summary>
        /// Performs type analysis for each mixin
        /// </summary>
        /// <param name="mixinInfo">the ModuleMixinInfo</param>
        private void PerformTypeAnalysis(ModuleMixinInfo mixinInfo)
        {
            lock (mixinInfo)
            {
                if (mixinInfo.Mixin.TypeAnalysisStatus == AnalysisStatus.None)
                {
                    mixinInfo.Mixin.TypeAnalysisStatus = AnalysisStatus.InProgress;

                    // TODO: order + typedef
                    var typeAnalyzer = new XenkoTypeAnalysis(new ParsingResult());
                    typeAnalyzer.Run(mixinInfo.MixinAst);
                    mixinInfo.Mixin.TypeAnalysisStatus = AnalysisStatus.Complete;
                }
                else if (mixinInfo.Mixin.TypeAnalysisStatus != AnalysisStatus.Complete)
                {
                    throw new Exception("Type analysis failed for mixin " + mixinInfo.MixinName);
                }
            }
        }
Пример #8
0
 /// <summary>
 /// Specifically analyze a module
 /// </summary>
 /// <param name="mixinInfo">the ModuleMixinInfo</param>
 public void Analyze(ModuleMixinInfo mixinInfo)
 {
     ModuleSemanticAnalysisPerMixin(mixinInfo);
 }
Пример #9
0
        /// <summary>
        /// Performs an semantic analysis of the mixin inside its own context
        /// </summary>
        /// <param name="mixinInfo">The mixin to analyze</param>
        private void ModuleSemanticAnalysisPerMixin(ModuleMixinInfo mixinInfo)
        {
            if (mixinInfo == null)
            {
                return;
            }

            var mixin = mixinInfo.Mixin;

            if (mixin.DependenciesStatus != AnalysisStatus.Complete || mixin.VirtualTableStatus != AnalysisStatus.Complete)
            {
                return;
            }

            if (mixin.SemanticAnalysisStatus == AnalysisStatus.Complete)
            {
                return;
            }
            if (mixin.SemanticAnalysisStatus == AnalysisStatus.InProgress)
            {
                ErrorWarningLog.Error(XenkoMessageCode.ErrorCyclicDependency, mixin.Shader.Span, mixin.Shader);
                return;
            }
            if (!mixinInfo.Instanciated)
            {
                return;
            }

            mixin.SemanticAnalysisStatus = AnalysisStatus.InProgress;

            // analyze the base mixins
            foreach (var baseClass in mixin.BaseMixins)
            {
                var baseClassInfo = MixinInfos.FirstOrDefault(x => x.Mixin == baseClass);
                ModuleSemanticAnalysisPerMixin(baseClassInfo);

                if (baseClassInfo.Mixin.SemanticAnalysisStatus == AnalysisStatus.Error || baseClassInfo.Mixin.SemanticAnalysisStatus == AnalysisStatus.Cyclic)
                {
                    mixin.SemanticAnalysisStatus = AnalysisStatus.Error;
                    return;
                }

                if (!baseClassInfo.Instanciated)
                {
                    mixinInfo.Instanciated       = false;
                    mixin.SemanticAnalysisStatus = AnalysisStatus.None;
                    return;
                }

                if (mixin.LocalVirtualTable.CheckNameConflict(baseClass.VirtualTable, ErrorWarningLog))
                {
                    mixin.SemanticAnalysisStatus = AnalysisStatus.Error;
                    return;
                }
            }

            // analyze the extern mixins
            foreach (var externMixin in mixin.VariableDependencies)
            {
                var externMixinInfo = MixinInfos.FirstOrDefault(x => x.Mixin == externMixin.Value);
                ModuleSemanticAnalysisPerMixin(externMixinInfo);
                if (externMixinInfo.Mixin.SemanticAnalysisStatus == AnalysisStatus.Error || externMixinInfo.Mixin.SemanticAnalysisStatus == AnalysisStatus.Cyclic)
                {
                    mixin.SemanticAnalysisStatus = AnalysisStatus.Error;
                    return;
                }
            }

            var compilationContext = MixinInfos.Select(x => x.Mixin).ToList();

            mixin.ParsingInfo = XenkoSemanticAnalysis.RunAnalysis(mixin, compilationContext);

            var staticStageMixins = new List <ModuleMixin>();

            staticStageMixins.AddRange(mixin.ParsingInfo.StaticReferences.VariablesReferences.Select(x => x.Key.GetTag(XenkoTags.ShaderScope) as ModuleMixin));
            staticStageMixins.AddRange(mixin.ParsingInfo.StaticReferences.MethodsReferences.Select(x => x.Key.GetTag(XenkoTags.ShaderScope) as ModuleMixin));
            staticStageMixins.AddRange(mixin.ParsingInfo.StageInitReferences.VariablesReferences.Select(x => x.Key.GetTag(XenkoTags.ShaderScope) as ModuleMixin));
            staticStageMixins.AddRange(mixin.ParsingInfo.StageInitReferences.MethodsReferences.Select(x => x.Key.GetTag(XenkoTags.ShaderScope) as ModuleMixin));
            staticStageMixins.RemoveAll(x => x == mixin);

            foreach (var dep in staticStageMixins)
            {
                var depInfo = MixinInfos.FirstOrDefault(x => x.Mixin == dep);
                ModuleSemanticAnalysisPerMixin(depInfo);
                if (dep.SemanticAnalysisStatus == AnalysisStatus.Error || dep.SemanticAnalysisStatus == AnalysisStatus.Cyclic)
                {
                    mixin.SemanticAnalysisStatus = AnalysisStatus.Error;
                    return;
                }
            }

            // check the extern stage references (but do not change the type inference)
            var externList = new List <ModuleMixin>();

            // NOTE: we cannot use the members .Values of .Keys because it internally modifies the dictionary, creating a Dictionary<K,T>.ValueCollection which cannot be cloned (no default constructor).
            // This results in a exception in the DeepClone code.
            mixin.InheritanceList.ForEach(dep => externList.AddRange(dep.VariableDependencies.Select(x => x.Value)));
            externList.AddRange(mixin.VariableDependencies.Select(x => x.Value));
            externList.ForEach(ext => CheckReferencesFromExternMixin(ext, mixin));

            mixin.ParsingInfo.ErrorsWarnings.CopyTo(ErrorWarningLog);

            mixin.SemanticAnalysisStatus = AnalysisStatus.Complete;
        }
Пример #10
0
        /// <summary>
        /// Get the list of dependencies for the mixin (base classes only)
        /// </summary>
        /// <param name="mixinInfo">the mixin info</param>
        /// <returns>A collection of class names</returns>
        private void BuildMixinDependencies(ModuleMixinInfo mixinInfo)
        {
            var mixin = mixinInfo.Mixin;

            if (mixin.DependenciesStatus == AnalysisStatus.Cyclic || mixin.DependenciesStatus == AnalysisStatus.Error || mixinInfo.Mixin.DependenciesStatus == AnalysisStatus.Complete)
            {
                return;
            }
            if (mixin.DependenciesStatus == AnalysisStatus.InProgress)
            {
                ErrorWarningLog.Error(XenkoMessageCode.ErrorCyclicDependency, mixin.Shader.Span, mixin.Shader);
                mixin.DependenciesStatus = AnalysisStatus.Cyclic;
                return;
            }
            if (mixin.DependenciesStatus == AnalysisStatus.None)
            {
                mixin.DependenciesStatus = AnalysisStatus.InProgress;

                foreach (var baseClass in mixin.Shader.BaseClasses)
                {
                    // search based on class name and macros. It is enough since a ShaderMixinSource only have ShaderClassSource as base mixin (and no ShaderMixinSource that may redefine the macros)
                    var bcInfo = MixinInfos.FirstOrDefault(x => x.MixinName == baseClass.Name.Text && AreMacrosEqual(x.Macros, mixinInfo.Macros));

                    if (bcInfo == null)
                    {
                        ErrorWarningLog.Error(XenkoMessageCode.ErrorDependencyNotInModule, baseClass.Span, baseClass, mixin.MixinName);
                        mixin.DependenciesStatus = AnalysisStatus.Error;
                        return;
                    }

                    var bc = bcInfo.Mixin;

                    BuildMixinDependencies(bcInfo);
                    if (bc.DependenciesStatus == AnalysisStatus.Error || bc.DependenciesStatus == AnalysisStatus.Cyclic)
                    {
                        mixin.DependenciesStatus = AnalysisStatus.Error;
                        return;
                    }

                    foreach (var dependency in bc.InheritanceList)
                    {
                        if (!mixin.InheritanceList.Contains(dependency))
                        {
                            mixin.InheritanceList.Add(dependency);
                        }
                    }

                    if (!mixin.InheritanceList.Contains(bc))
                    {
                        mixin.InheritanceList.Add(bc);
                    }

                    mixin.BaseMixins.Add(bc);

                    if (!bcInfo.Instanciated)
                    {
                        mixinInfo.Instanciated = false;
                    }
                }

                // do not look for extern keyword but for type name
                foreach (var variable in mixin.LocalVirtualTable.Variables)
                {
                    var variableTypeName = variable.Variable.Type.Name.Text;
                    if (variable.Variable.Type is ArrayType) // support for array of externs
                    {
                        variableTypeName = (variable.Variable.Type as ArrayType).Type.Name.Text;
                    }

                    var baseClassInfo = MixinInfos.FirstOrDefault(x => x.MixinName == variableTypeName);
                    if (baseClassInfo != null)
                    {
                        variable.Variable.Qualifiers |= Xenko.Core.Shaders.Ast.Hlsl.StorageQualifier.Extern; // add the extern keyword but simpler analysis in the future
                        if (variable.Variable.InitialValue is VariableReferenceExpression && (variable.Variable.InitialValue as VariableReferenceExpression).Name.Text == "stage")
                        {
                            mixin.StageInitVariableDependencies.Add(variable.Variable, baseClassInfo.Mixin);
                        }
                        else
                        {
                            mixin.VariableDependencies.Add(variable.Variable, baseClassInfo.Mixin);
                        }

                        if (variable.Variable.Type is ArrayType)
                        {
                            var typeArray = variable.Variable.Type as ArrayType;
                            typeArray.Type.TypeInference.Declaration = baseClassInfo.MixinAst;
                        }
                        else
                        {
                            variable.Variable.Type.TypeInference.Declaration = baseClassInfo.MixinAst;
                        }
                    }
                }

                mixin.DependenciesStatus = AnalysisStatus.Complete;
            }
        }
Пример #11
0
        /// <summary>
        /// Get all the declarations in the mixin
        /// </summary>
        /// <param name="mixinInfo">The mixin info</param>
        private void BuildModuleMixin(ModuleMixinInfo mixinInfo)
        {
            lock (mixinInfo)
            {
                if (mixinInfo.Mixin.ModuleMixinBuildStatus == AnalysisStatus.Complete)
                {
                    return;
                }

                if (mixinInfo.Mixin.ModuleMixinBuildStatus != AnalysisStatus.None)
                {
                    throw new Exception("BuildModuleMixin failed for mixin " + mixinInfo.MixinName);
                }

                mixinInfo.Mixin.ModuleMixinBuildStatus = AnalysisStatus.InProgress;

                var mixinAst = mixinInfo.MixinAst;

                var moduleMixin = mixinInfo.Mixin;
                moduleMixin.SetShaderAst(mixinAst);
                moduleMixin.MixinGenericName = mixinInfo.MixinGenericName;

                if (mixinAst != null && mixinInfo.Instanciated)
                {
                    var vtindex = 0;
                    foreach (var member in mixinAst.Members)
                    {
                        if (member is MethodDeclaration)
                        {
                            moduleMixin.LocalVirtualTable.Methods.Add(new MethodDeclarationShaderCouple(member as MethodDeclaration, mixinAst));
                            member.SetTag(XenkoTags.VirtualTableReference, new VTableReference {
                                Shader = mixinInfo.MixinName, Slot = vtindex++
                            });
                        }
                        else if (member is Variable)
                        {
                            var variable = member as Variable;
                            moduleMixin.LocalVirtualTable.Variables.Add(new VariableShaderCouple(variable, mixinAst));
                            // remove null initial values
                            var initValue = variable.InitialValue as VariableReferenceExpression;
                            if (initValue != null && initValue.Name.Text == "null")
                            {
                                variable.InitialValue = null;
                            }
                        }
                        else if (member is Typedef)
                        {
                            moduleMixin.LocalVirtualTable.Typedefs.Add(member as Typedef);
                        }
                        else if (member is StructType)
                        {
                            moduleMixin.LocalVirtualTable.StructureTypes.Add(member as StructType);
                        }
                        else
                        {
                            moduleMixin.RemainingNodes.Add(member);
                        }

                        // set a tag on the members to easily recognize them from the local declarations/definitions
                        member.SetTag(XenkoTags.ShaderScope, moduleMixin);
                        member.SetTag(XenkoTags.BaseDeclarationMixin, mixinInfo.MixinName);
                    }

                    // Check name conflicts
                    foreach (var method in moduleMixin.LocalVirtualTable.Methods)
                    {
                        if (moduleMixin.LocalVirtualTable.Methods.Count(x => x.Method.IsSameSignature(method.Method)) > 1) // 1 because the function is in the list
                        {
                            ErrorWarningLog.Error(XenkoMessageCode.ErrorFunctionRedefined, method.Method.Span, method.Method, mixinInfo.MixinName);
                        }

                        if (moduleMixin.LocalVirtualTable.Variables.Any(x => x.Variable.Name.Text == method.Method.Name.Text))
                        {
                            ErrorWarningLog.Error(XenkoMessageCode.ErrorFunctionVariableNameConflict, method.Method.Span, method.Method, mixinInfo.MixinName);
                        }
                    }
                    foreach (var variable in moduleMixin.LocalVirtualTable.Variables)
                    {
                        if (moduleMixin.LocalVirtualTable.Variables.Count(x => x.Variable.Name.Text == variable.Variable.Name.Text) > 1) // 1 because the function is in the list
                        {
                            ErrorWarningLog.Error(XenkoMessageCode.ErrorFunctionVariableNameConflict, variable.Variable.Span, variable.Variable, mixinInfo.MixinName);
                        }

                        if (moduleMixin.LocalVirtualTable.Methods.Any(x => x.Method.Name.Text == variable.Variable.Name.Text))
                        {
                            ErrorWarningLog.Error(XenkoMessageCode.ErrorVariableNameConflict, variable.Variable.Span, variable.Variable, mixinInfo.MixinName);
                        }
                    }
                }

                moduleMixin.MinimalContext = new HashSet <ModuleMixin>(mixinInfo.MinimalContext.Select(x => x.Mixin));

                mixinInfo.Mixin.ModuleMixinBuildStatus = AnalysisStatus.Complete;
            }
        }