private static List <AddressedType> FlattenSymbol(ErrorReport errorReport, ref bool success, IParameterSymbol root, ITypeSymbol item, NullableAnnotation nullableAnnotation, int depth, string prefix = "") { List <AddressedType> result = new List <AddressedType>(); if (depth > 4) { errorReport.ReportError(root, "Log method parameter type contained too much nesting and exceeded maximum recursion when flattening. Flattened chain was: {0}", prefix); success = false; return(result); } if (item.SpecialType == SpecialType.System_Enum) { result.Add(new AddressedType() { Type = item, Address = prefix, NullableAnnotation = nullableAnnotation }); } if (item.ToDisplayString() == "System.Guid") { result.Add(new AddressedType() { Type = item, Address = prefix, NullableAnnotation = nullableAnnotation }); } else if (IsNonSpecialClassLike(item)) { if (item.BaseType != null && IsNonSpecialClassLike(item.BaseType)) { result.AddRange(FlattenSymbol(errorReport, ref success, root, item.BaseType, nullableAnnotation, depth + 1, prefix)); } foreach (var member in item.GetMembers().OfType <IPropertySymbol>().Where(FilterMembersToFlatten)) { result.AddRange(FlattenSymbol(errorReport, ref success, root, member.Type, member.NullableAnnotation, depth + 1, prefix + '.' + member.Name)); } foreach (var member in item.GetMembers().OfType <IFieldSymbol>().Where(FilterMembersToFlatten)) { result.AddRange(FlattenSymbol(errorReport, ref success, root, member.Type, member.NullableAnnotation, depth + 1, prefix + '.' + member.Name)); } } else { result.Add(new AddressedType() { Type = item, Address = prefix, NullableAnnotation = nullableAnnotation }); } Contract.Assume(depth > 0 || result.Count > 0, "FlattenSymbol skipped or didn't know how to handle a symbol of type " + item.TypeKind.ToString()); return(result); }
private string GetAccessibilityString(Accessibility accessibility) { switch (accessibility) { case Accessibility.Private: return("private"); case Accessibility.Protected: return("protected"); case Accessibility.Internal: return("internal"); case Accessibility.Public: return("public"); default: m_errorReport.ReportError("Unsupported accessibility type: {0}", accessibility); return(null); } }
public bool DiscoverLoggingSites(out List <LoggingSite> loggingSites) { loggingSites = new List <LoggingSite>(); // First create a compilation to act upon values and run codegen var syntaxTrees = new ConcurrentBag <SyntaxTree>(); CSharpParseOptions opts = new CSharpParseOptions(preprocessorSymbols: m_configuration.PreprocessorDefines.ToArray()); Parallel.ForEach( m_configuration.SourceFiles.Distinct(StringComparer.OrdinalIgnoreCase), file => { if (File.Exists(file)) { string text = File.ReadAllText(file); syntaxTrees.Add(CSharpSyntaxTree.ParseText(text, path: file, options: opts)); } }); var metadataFileReferences = new ConcurrentBag <MetadataReference>(); Parallel.ForEach( m_configuration.References.Distinct(StringComparer.OrdinalIgnoreCase), reference => { if (File.Exists(reference)) { metadataFileReferences.Add(MetadataReference.CreateFromFile(reference)); } }); if (m_errorReport.Errors != 0) { return(false); } Compilation compilation = CSharpCompilation.Create( "temp", syntaxTrees, metadataFileReferences, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); // Hold on to all of the errors. Most are probably ok but some may be relating to event definitions and // should cause errors MultiValueDictionary <string, Diagnostic> errorsByFile = new MultiValueDictionary <string, Diagnostic>(StringComparer.OrdinalIgnoreCase); foreach (Diagnostic d in compilation.GetDiagnostics()) { if (d.Location == null || d.Location.SourceTree == null) { continue; // TODO } if (d.Severity == DiagnosticSeverity.Error) { errorsByFile.Add(d.Location.SourceTree.FilePath, d); } } List <INamedTypeSymbol> symbols = new List <INamedTypeSymbol>(); FindTypesInNamespace(compilation.Assembly.GlobalNamespace, (symbol) => true, symbols, true); foreach (var symbol in symbols) { foreach (var member in symbol.GetMembers()) { IMethodSymbol method = member as IMethodSymbol; if (method != null) { AttributeData attributeData; if (GetGeneratedEventAttribute(method, out attributeData)) { // Check the errors we stashed off to see if any pretain to this event. If so, display them // as they may cause us to generate the event incorrectly bool eventHasErrors = false; IReadOnlyList <Diagnostic> diagnostics; if (errorsByFile.TryGetValue(method.Locations[0].SourceTree.FilePath, out diagnostics)) { foreach (Diagnostic d in diagnostics) { if (d.Location.SourceSpan.OverlapsWith(method.Locations[0].SourceSpan) || d.Location.SourceSpan.OverlapsWith(attributeData.ApplicationSyntaxReference.Span)) { m_errorReport.ReportError(d.ToString()); eventHasErrors = true; } } } if (!eventHasErrors) { LoggingSite site; if (!ParseAndValidateLogSite(method, attributeData, out site)) { return(false); } loggingSites.Add(site); } } } } } return(true); }
public bool DiscoverLoggingSites(out List <LoggingClass> loggingClasses) { loggingClasses = new List <LoggingClass>(); // First create a compilation to act upon values and run codegen var syntaxTrees = new ConcurrentBag <SyntaxTree>(); CSharpParseOptions opts = new CSharpParseOptions( preprocessorSymbols: m_configuration.PreprocessorDefines.ToArray(), languageVersion: LanguageVersion.Latest); Parallel.ForEach( m_configuration.SourceFiles.Distinct(StringComparer.OrdinalIgnoreCase), file => { if (File.Exists(file)) { string text = File.ReadAllText(file); syntaxTrees.Add(CSharpSyntaxTree.ParseText(text, path: file, options: opts)); } }); var metadataFileReferences = new ConcurrentBag <MetadataReference>(); Parallel.ForEach( m_configuration.References.Distinct(StringComparer.OrdinalIgnoreCase), reference => { if (File.Exists(reference)) { metadataFileReferences.Add(MetadataReference.CreateFromFile(reference)); } }); if (m_errorReport.Errors != 0) { return(false); } Compilation compilation = CSharpCompilation.Create( "temp", syntaxTrees, metadataFileReferences, new CSharpCompilationOptions( OutputKind.DynamicallyLinkedLibrary, deterministic: true ) ); // Hold on to all of the errors. Most are probably ok but some may be relating to event definitions and // should cause errors MultiValueDictionary <string, Diagnostic> errorsByFile = new MultiValueDictionary <string, Diagnostic>(StringComparer.OrdinalIgnoreCase); foreach (Diagnostic d in compilation.GetDiagnostics()) { if (d.Location == null || d.Location.SourceTree == null) { continue; // TODO } if (d.Severity == DiagnosticSeverity.Error) { Console.WriteLine(d.ToString()); errorsByFile.Add(d.Location.SourceTree.FilePath, d); } } List <INamedTypeSymbol> symbols = new List <INamedTypeSymbol>(); FindTypesInNamespace(compilation.Assembly.GlobalNamespace, (symbol) => true, symbols, true); foreach (var symbol in symbols) { if (GetAttribute(symbol, errorsByFile, nameof(LoggingDetailsAttribute), out var loggingDetailsData)) { if (!TryParseLoggingDetailsAttribute(symbol, loggingDetailsData, out var loggingDetails)) { return(false); } var loggingClass = new LoggingClass(loggingDetails, symbol); foreach (var member in symbol.GetMembers()) { if (member is IMethodSymbol method) { if (!method.IsAbstract && method.MethodKind != MethodKind.Constructor && method.MethodKind != MethodKind.StaticConstructor && // okay to have constructores method.MethodKind != MethodKind.PropertyGet && method.MethodKind != MethodKind.PropertySet) // okay to have properties (for now). { m_errorReport.ReportError(member, $"All methods must be abstract. Invalid method: {method.Name}"); } if (GetAttribute(method, errorsByFile, nameof(GeneratedEventAttribute), out var generatedEventData)) { if (!ParseAndValidateLogSite(method, generatedEventData, out var site)) { return(false); } loggingClass.Sites.Add(site); } } } loggingClasses.Add(loggingClass); } } return(true); }