Example #1
0
        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);
        }
Example #2
0
        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);
            }
        }
Example #3
0
        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);
        }
Example #4
0
        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);
        }