static List <CGFDocument> ProcessDocuments(CGFParserReporter reporter, List <Microsoft.CodeAnalysis.Document> documents, Microsoft.CodeAnalysis.Project project) { using (reporter.CreateContextScope(CGFParserReporterContext.Type.Project, project.FilePath)) { List <CGFDocument> documentsToProcess = new List <CGFDocument>(); foreach (Microsoft.CodeAnalysis.Document document in documents) { using (reporter.CreateContextScope(CGFParserReporterContext.Type.File, document.FilePath)) { List <CGFTypeSymbol> typesToProcess = new List <CGFTypeSymbol>(); Microsoft.CodeAnalysis.SemanticModel semanticModel = document.GetSemanticModelAsync().Result; Microsoft.CodeAnalysis.SyntaxTree syntaxTree = document.GetSyntaxTreeAsync().Result; IEnumerable <Microsoft.CodeAnalysis.SyntaxNode> syntaxNodes = syntaxTree.GetRoot().DescendantNodes().Where(n => (n as ClassDeclarationSyntax) != null || (n as EnumDeclarationSyntax) != null); foreach (Microsoft.CodeAnalysis.SyntaxNode node in syntaxNodes) { ClassDeclarationSyntax classSyntax = node as ClassDeclarationSyntax; if (classSyntax != null) { Microsoft.CodeAnalysis.INamedTypeSymbol typeSymbol = semanticModel.GetDeclaredSymbol(classSyntax); using (reporter.CreateContextScope(CGFParserReporterContext.Type.Type, typeSymbol.Name)) { CGFTypeSymbol cgfTypeSymbol = CGFTypeSymbol.Parse(reporter, typeSymbol); typesToProcess.Add(cgfTypeSymbol); } } else { EnumDeclarationSyntax enumSyntax = node as EnumDeclarationSyntax; Microsoft.CodeAnalysis.INamedTypeSymbol typeSymbol = semanticModel.GetDeclaredSymbol(enumSyntax); using (reporter.CreateContextScope(CGFParserReporterContext.Type.Type, typeSymbol.Name)) { CGFTypeSymbol cgfTypeSymbol = CGFTypeSymbol.Parse(reporter, typeSymbol); typesToProcess.Add(cgfTypeSymbol); } } } if (typesToProcess.Count > 0) { CGFDocument cgfDocument = CGFDocument.Parse(reporter, document, typesToProcess); documentsToProcess.Add(cgfDocument); } } } return(documentsToProcess); } }
public static CGFAttributeData Parse(CGFParserReporter reporter, Microsoft.CodeAnalysis.AttributeData attributeData) { //TODO - Add way to have custom validation for ArrayAttribute to validate variable name actually matches an serialized variable Type attributeType = Type.GetType(attributeData.AttributeClass.ToString(), false); Type baseAttributeType = typeof(CGFAttribute); if (!attributeType.IsSubclassOf(baseAttributeType)) { reporter.LogInfo($"Unrecognized attribute type {attributeType.Name}. Attributes are expected to be subtypes of {baseAttributeType.Name}."); return(null); } Attribute attribute = null; using (reporter.CreateContextScope(CGFParserReporterContext.Type.Type, attributeType.Name)) { System.Reflection.ConstructorInfo[] constructors = attributeType.GetConstructors(); foreach (System.Reflection.ConstructorInfo constructorInfo in constructors) { System.Reflection.ParameterInfo[] parameters = constructorInfo.GetParameters(); if (attributeData.ConstructorArguments.Length != parameters.Length) { continue; } bool matchingParameters = true; for (int paramIndex = 0; paramIndex < parameters.Length; ++paramIndex) { Microsoft.CodeAnalysis.TypedConstant cgfTypedConstant = attributeData.ConstructorArguments[paramIndex]; System.Reflection.ParameterInfo parameterInfo = parameters[paramIndex]; Type cgfParameterType = null; if (string.IsNullOrEmpty(cgfTypedConstant.Type.Name)) { cgfParameterType = Type.GetType(cgfTypedConstant.Type.Name, false); } else { cgfParameterType = Type.GetType($"{cgfTypedConstant.Type.ContainingSymbol.Name}.{cgfTypedConstant.Type.Name}", false); } if (cgfParameterType != parameterInfo.ParameterType) { matchingParameters = false; break; } } if (matchingParameters) { if (parameters.Length > 0) { Object[] parameterObjects = new Object[parameters.Length]; for (int paramIndex = 0; paramIndex < parameters.Length; ++paramIndex) { Microsoft.CodeAnalysis.TypedConstant cgfTypedConstant = attributeData.ConstructorArguments[paramIndex]; System.Reflection.ParameterInfo parameterInfo = parameters[paramIndex]; Object convertedParam = null; if (parameterInfo.ParameterType.IsEnum) { convertedParam = Enum.ToObject(parameterInfo.ParameterType, cgfTypedConstant.Value); } else { convertedParam = System.Convert.ChangeType(cgfTypedConstant.Value, parameterInfo.ParameterType); } parameterObjects[paramIndex] = convertedParam; } attribute = Activator.CreateInstance(attributeType, parameterObjects) as Attribute; } else { attribute = Activator.CreateInstance(attributeType) as Attribute; } } } if (attribute == null) { //TODO - Add provided constructor param list to help finding the error. // List most likely constructors. reporter.LogError($"No matching constructor found for {attributeType.Name}."); return(null); } CGFAttributeData CGFAttributeData = new CGFAttributeData(attribute, attributeData); return(CGFAttributeData); } }
public static CGFParser ParseSolution(CGFParserReporter reporter, string solutionPath) { string fullSolutionPath = Path.GetFullPath(solutionPath); reporter.LogInfo($"CGFParser parsing {solutionPath} (FullPath: {fullSolutionPath})"); if (!File.Exists(fullSolutionPath)) { reporter.LogError($"{fullSolutionPath} does not exist."); return(null); } CGFParser cgfParser = new CGFParser(reporter); string[] documentsToExclude = { "AssemblyAttributes.cs" }; reporter.LogInfo($"Opening solution {solutionPath}"); IEnumerable <VisualStudioInstance> visualStudioInstances = MSBuildLocator.QueryVisualStudioInstances(new VisualStudioInstanceQueryOptions { DiscoveryTypes = DiscoveryType.VisualStudioSetup }); VisualStudioInstance visualStudioInstance = visualStudioInstances.OrderByDescending(i => i.Version) .Where(i => Directory.Exists(i.MSBuildPath)) .FirstOrDefault(); if (string.IsNullOrEmpty(visualStudioInstance.MSBuildPath)) { reporter.LogError($"Unable to find a proper VisualStudio instance. MSBuild workspace creation/solution parsing may fail"); } else { MSBuildLocator.RegisterInstance(visualStudioInstance); } MSBuildWorkspace workspace = MSBuildWorkspace.Create(); workspace.LoadMetadataForReferencedProjects = false; workspace.WorkspaceFailed += cgfParser.Workspace_WorkspaceFailed; workspace.SkipUnrecognizedProjects = true; Microsoft.CodeAnalysis.Solution solution = workspace.OpenSolutionAsync(solutionPath).Result; reporter.LogInfo($"Solution opened"); using (reporter.CreateContextScope(CGFParserReporterContext.Type.Solution, solution.FilePath)) { reporter.LogInfo($"Parsing"); List <CGFDocument> cgfDocuments = new List <CGFDocument>(); foreach (Microsoft.CodeAnalysis.Project project in solution.Projects) { List <Microsoft.CodeAnalysis.Document> documentsToProcess = new List <Microsoft.CodeAnalysis.Document>(); foreach (Microsoft.CodeAnalysis.Document document in project.Documents) { bool includeDocument = true; foreach (string documentToExclude in documentsToExclude) { if (document.FilePath.EndsWith(documentToExclude, StringComparison.CurrentCultureIgnoreCase)) { includeDocument = false; break; } } if (includeDocument) { documentsToProcess.Add(document); } } cgfDocuments.AddRange(ProcessDocuments(reporter, documentsToProcess, project)); } reporter.LogInfo($"Parsing complete"); Dictionary <string, string> typeNameToPathMap = new Dictionary <string, string>(); Dictionary <string, CGFTypeSymbol> typeNameToTypeMap = new Dictionary <string, CGFTypeSymbol>(); foreach (CGFDocument cgfDocument in cgfDocuments) { using (reporter.CreateContextScope(CGFParserReporterContext.Type.File, cgfDocument.DocumentFilePath)) { foreach (CGFTypeSymbol cgfType in cgfDocument.Types) { using (reporter.CreateContextScope(CGFParserReporterContext.Type.Type, cgfType.Name)) { if (typeNameToPathMap.ContainsKey(cgfType.Name)) { reporter.LogError($"Duplicate type {cgfType.Name} found. Other instance found in {typeNameToPathMap[cgfType.Name]}"); } else { typeNameToPathMap.Add(cgfType.Name, cgfDocument.DocumentFilePath); typeNameToTypeMap.Add(cgfType.Name, cgfType); } } } } } foreach (CGFTypeSymbol type in typeNameToTypeMap.Values) { foreach (CGFFieldSymbol field in type.Fields) { CGFTypeSymbol typeSymbol = null; if (typeNameToTypeMap.TryGetValue(field.TypeName, out typeSymbol)) { field.TypeSymbol = typeSymbol; } } } cgfParser.m_Documents = cgfDocuments; cgfParser.m_TypeNameToPathMap = typeNameToPathMap; return(cgfParser); } }