Example #1
0
        private bool CheckField(DiagnosticSink diagnosticSink, IFieldSymbol field)
        {
            if (field.IsImplicitlyDeclared)
            {
                // These correspond to auto-properties. That case gets handled
                // in CheckProperty instead.
                return(true);
            }

            var decl = field.DeclaringSyntaxReferences.Single()
                       .GetSyntax() as VariableDeclaratorSyntax;

            var type = decl.FirstAncestorOrSelf <VariableDeclarationSyntax>().Type;

            // Get all possible assignments of the field
            var assignments = GetAssignments(field, decl.Initializer?.Value);

            return(CheckFieldOrProperty(
                       diagnosticSink,
                       member: field,
                       type: field.Type,
                       isReadOnly: field.IsReadOnly,
                       typeSyntax: type,
                       nameSyntax: decl.Identifier,
                       assignments
                       ));
        }
Example #2
0
 public ImmutableDefinitionChecker(
     Compilation compilation,
     DiagnosticSink diagnosticSink,
     ImmutabilityContext context,
     AnnotationsContext annotationsContext
     )
 {
     m_compilation        = compilation;
     m_diagnosticSink     = diagnosticSink;
     m_context            = context;
     m_annotationsContext = annotationsContext;
 }
Example #3
0
 public ImmutableAttributeConsistencyChecker(
     Compilation compilation,
     DiagnosticSink diagnosticSink,
     ImmutabilityContext context,
     AnnotationsContext annotationsContext
     )
 {
     m_compilation        = compilation;
     m_diagnosticSink     = diagnosticSink;
     m_context            = context;
     m_annotationsContext = annotationsContext;
 }
        OrderTestClasses(IEnumerable <IGrouping <ITestClass, IXunitTestCase> > testCaseGroups)
        {
            int lastOrder = 0;

            //nasty check if we are in class without collection defined
            if (testCaseGroups.First().Key.TestCollection.CollectionDefinition != null)
            {
                foreach (var g in
                         testCaseGroups
                         .GroupBy(g => GetClassOrder(g.Key))
                         .OrderBy(g => g.Key))
                {
                    int count = g.Count();

                    if (count > 1)
                    {
                        DiagnosticSink.OnMessage(
                            new DiagnosticMessage(
                                g.Key == 0
                                                                        ? "Found {0} test classes with unassigned or order 0. '{2}'"
                                                                        : "Found {0} duplicates of order '{1}' on test collection '{2}'",
                                count,
                                g.Key,
                                string.Join("', '", g.Select(tc => tc.Key.Class.Name))));
                    }

                    if (lastOrder < g.Key - 1)
                    {
                        int lower = lastOrder + 1;
                        int upper = g.Key - 1;

                        DiagnosticSink.OnMessage(
                            new DiagnosticMessage(
                                lower == upper
                                                                        ? "Missing test classes order '{0}' for collection '{2}'."
                                                                        : "Missing test classes order sequence from '{0}' to '{1}' for collection '{2}'.",
                                lower,
                                upper,
                                GetCollectionName(g.First().Key.TestCollection)));
                    }

                    lastOrder = g.Key;
                }
            }

            return(testCaseGroups.OrderBy(g => GetClassOrder(g.Key)));
        }
Example #5
0
        private bool CheckProperty(DiagnosticSink diagnosticSink, IPropertySymbol prop)
        {
            if (prop.IsIndexer)
            {
                // Indexer properties are just glorified method syntax and
                // don't hold state.
                // https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/indexers/
                return(true);
            }

            if (prop.IsImplicitlyDeclared)
            {
                // records have implicitly declared properties like
                // EqualityContract which are OK but don't have a
                // PropertyDeclarationSyntax etc.
                return(true);
            }

            var propInfo = GetPropertyStuff(
                prop,
                prop.DeclaringSyntaxReferences.Single().GetSyntax()
                );

            if (!propInfo.IsAutoImplemented)
            {
                // Properties that are auto-implemented have an implicit
                // backing field that may be mutable. Otherwise, properties are
                // just sugar for getter/setter methods and don't themselves
                // contribute to mutability.
                return(true);
            }

            // Get all possible assignments of the property
            var assignments = GetAssignments(prop, propInfo.Initializer);

            return(CheckFieldOrProperty(
                       diagnosticSink,
                       member: prop,
                       type: prop.Type,
                       isReadOnly: propInfo.IsReadOnly,
                       typeSyntax: propInfo.TypeSyntax,
                       nameSyntax: propInfo.Identifier,
                       assignments
                       ));
        }
        public virtual IEnumerable <TTestCase> OrderTestCases <TTestCase>(IEnumerable <TTestCase> testCases)
            where TTestCase : ITestCase
        {
            int lastOrder = 0;

            foreach (var g in
                     testCases
                     .GroupBy(tc => GetCaseOrder(tc))
                     .OrderBy(g => g.Key))
            {
                int count = g.Count();

                if (count > 1)
                {
                    DiagnosticSink.OnMessage(
                        new DiagnosticMessage(
                            g.Key == 0
                                                                ? "Found {0} test cases with unassigned or order 0. '{2}'"
                                                                : "Found {0} duplicate order '{1}' for test cases '{2}'",
                            count,
                            g.Key,
                            string.Join("', '", g.Select(tc => $"{tc.TestMethod.TestClass.Class.Name}.{tc.TestMethod.Method.Name}"))));
                }

                if (lastOrder < g.Key - 1)
                {
                    int lower = lastOrder + 1;
                    int upper = g.Key - 1;

                    DiagnosticSink.OnMessage(
                        new DiagnosticMessage(
                            lower == upper
                                                                ? "Missing test case order '{0}' in test class '{2}'."
                                                                : "Missing test case order sequence from '{0}' to '{1}' in test class '{2}'.",
                            lower,
                            upper,
                            string.Join("], [", g.Select(tc => tc.TestMethod.TestClass.Class.Name))));
                }

                lastOrder = g.Key;
            }

            return(testCases.OrderBy(tc => GetCaseOrder(tc)));
        }
        public IEnumerable <ITestCollection> OrderTestCollections(IEnumerable <ITestCollection> testCollections)
        {
            int lastOrder = 0;

            foreach (var g in testCollections
                     .GroupBy(tc => GetOrder(tc))
                     .OrderBy(g => g.Key))
            {
                int count = g.Count();

                if (count > 1)
                {
                    DiagnosticSink.OnMessage(
                        new DiagnosticMessage(
                            g.Key == 0
                                                                ? "Found {0} test collections with unassigned or order 0. '{2}'"
                                                                : "Found {0} duplicate order '{1}' on test collections '{2}'",
                            count,
                            g.Key,
                            string.Join("', '", g.Select(tc => GetCollectionName(tc)))));
                }

                if (lastOrder < g.Key - 1)
                {
                    int lower = lastOrder + 1;
                    int upper = g.Key - 1;

                    DiagnosticSink.OnMessage(
                        new DiagnosticMessage(
                            lower == upper
                                                                ? "Missing test collection order '{0}'."
                                                                : "Missing test collection order sequence from '{0}' to '{1}'.",
                            lower,
                            upper));
                }

                lastOrder = g.Key;
            }

            return(testCollections.OrderBy(c => GetOrder(c)));
        }
Example #8
0
        private bool CheckMember(DiagnosticSink diagnosticSink, ISymbol member)
        {
            switch (member.Kind)
            {
            case SymbolKind.Field:
                return(CheckField(diagnosticSink, member as IFieldSymbol));

            case SymbolKind.Property:
                return(CheckProperty(diagnosticSink, member as IPropertySymbol));

            // These member types never contribute to mutability:
            case SymbolKind.Method:
            case SymbolKind.NamedType:
                return(true);

            case SymbolKind.Event:
                diagnosticSink(
                    Diagnostic.Create(
                        Diagnostics.EventMemberMutable,
                        GetLocationOfMember(member)
                        )
                    );

                return(false);

            // By default raise an alarm (in case we missed something, or
            // if there are new unsupported language features.)
            default:
                m_diagnosticSink(
                    Diagnostic.Create(
                        Diagnostics.UnexpectedMemberKind,
                        GetLocationOfMember(member),
                        member.Name,
                        member.Kind
                        )
                    );

                return(false);
            }
        }
        public static bool CheckAudited(
            AnnotationsContext annotationsContext,
            ISymbol symbol,
            DiagnosticSink diagnosticSink,
            out Location location)
        {
            // Collect audit information
            var hasStaticAudited              = annotationsContext.Statics.Audited.IsDefined(symbol);
            var hasStaticUnaudited            = annotationsContext.Statics.Unaudited.IsDefined(symbol);
            var hasMutabilityAudited          = annotationsContext.Mutability.Audited.IsDefined(symbol);
            var hasMutabilityUnaudited        = annotationsContext.Mutability.Unaudited.IsDefined(symbol);
            var hasBothStaticsAttributes      = hasStaticAudited && hasStaticUnaudited;
            var hasBothMutabilityAttributes   = hasMutabilityAudited && hasMutabilityUnaudited;
            var hasEitherStaticsAttributes    = hasStaticAudited || hasStaticUnaudited;
            var hasEitherMutabilityAttributes = hasMutabilityAudited || hasMutabilityUnaudited;

            // If there are no audits, don't do anything
            if (!hasEitherStaticsAttributes && !hasEitherMutabilityAttributes)
            {
                location = null;
                return(false);
            }

            var syntaxLocation = symbol
                                 .DeclaringSyntaxReferences[0]
                                 .GetSyntax()
                                 .GetLastToken()
                                 .GetLocation();

            // Check if both static audits are applied
            if (hasBothStaticsAttributes)
            {
                var diagnostic = Diagnostic.Create(
                    Diagnostics.ConflictingImmutability,
                    syntaxLocation,
                    "Statics.Audited",
                    "Statics.Unaudited",
                    symbol.Kind.ToString().ToLower());
                diagnosticSink(diagnostic);
            }

            // Check if both mutability audits are applied
            if (hasBothMutabilityAttributes)
            {
                var diagnostic = Diagnostic.Create(
                    Diagnostics.ConflictingImmutability,
                    syntaxLocation,
                    "Mutability.Audited",
                    "Mutability.Unaudited",
                    symbol.Kind.ToString().ToLower());
                diagnosticSink(diagnostic);
            }

            AttributeData attr = null;

            if (symbol.IsStatic)
            {
                // Check if a static member is using mutability audits
                if (hasEitherMutabilityAttributes)
                {
                    var diagnostic = Diagnostic.Create(
                        Diagnostics.InvalidAuditType,
                        syntaxLocation,
                        "static",
                        symbol.Kind.ToString().ToLower(),
                        "Statics.*");
                    diagnosticSink(diagnostic);
                }

                attr = annotationsContext.Statics.Audited.GetAll(symbol).FirstOrDefault()
                       ?? annotationsContext.Statics.Unaudited.GetAll(symbol).FirstOrDefault();
            }
            else
            {
                // Check if a non-static member is using static audits
                if (hasEitherStaticsAttributes)
                {
                    var diagnostic = Diagnostic.Create(
                        Diagnostics.InvalidAuditType,
                        syntaxLocation,
                        "non-static",
                        symbol.Kind.ToString().ToLower(),
                        "Mutability.*");
                    diagnosticSink(diagnostic);
                }

                attr = annotationsContext.Mutability.Audited.GetAll(symbol).FirstOrDefault()
                       ?? annotationsContext.Mutability.Unaudited.GetAll(symbol).FirstOrDefault();
            }

            if (attr != null)
            {
                location = GetLocation(attr);
                return(true);
            }
            location = null;
            return(false);
        }
Example #10
0
        private bool CheckFieldOrProperty(
            DiagnosticSink diagnosticSink,
            ISymbol member,
            ITypeSymbol type,
            bool isReadOnly,
            TypeSyntax typeSyntax,
            SyntaxToken nameSyntax,
            IEnumerable <AssignmentInfo> assignments
            )
        {
            if (!isReadOnly)
            {
                diagnosticSink(
                    Diagnostic.Create(
                        Diagnostics.MemberIsNotReadOnly,
                        nameSyntax.GetLocation(),
                        member.Kind,
                        member.Name,
                        member.ContainingType.Name
                        )
                    );

                // Note: we're going to go looking for other errors.
                // There shouldn't be any "return true" below, it should
                // always be conditional on isReadOnly.
            }

            // If our field or property is a type that is always immutable then
            // we can stop looking (all that matters is that we are readonly).
            if (m_context.IsImmutable(
                    new ImmutabilityQuery(
                        ImmutableTypeKind.Total,
                        type
                        ),
                    typeSyntax.GetLocation,
                    out var typeDiagnostic
                    ))
            {
                return(isReadOnly);
            }

            // Our field or property could hold mutable values, but it's safe
            // as long as we always assign immutable values to it.

            // Usually that would be difficult, but if we're readonly its a lot
            // easier: we only have to look in our constructors (this applies
            // even to protected readonly; these can't be set in derived
            // constructors.)

            // TODO: we don't really need this, but it has been our current
            // behaviour... if we have a readonly field with no writes that's
            // worth a diagnostic on its own (there are built in ones for that)
            // but technically it doesn't add mutability.
            // If we remove this we can rename typeDiagnostic to _ again.
            if (!assignments.Any())
            {
                diagnosticSink(typeDiagnostic);
                return(false);
            }

            var allAssignmentsAreOfImmutableValues = true;

            foreach (var assignment in assignments)
            {
                var kind = GetQueryForAssignment(assignment, out var query, out var diagnostic);

                switch (kind)
                {
                case AssignmentQueryKind.NothingToCheck:
                    continue;

                case AssignmentQueryKind.Hopeless:
                    diagnosticSink(diagnostic);
                    allAssignmentsAreOfImmutableValues = false;
                    continue;

                case AssignmentQueryKind.ImmutabilityQuery:
                    if (m_context.IsImmutable(
                            query,
                            () => assignment.Expression.GetLocation(),
                            out diagnostic
                            ))
                    {
                        continue;
                    }

                    diagnosticSink(diagnostic);

                    // We're going to keep looking at all writes to surface as many
                    // relevant diagnostics as possible.
                    allAssignmentsAreOfImmutableValues = false;
                    continue;
                }
            }

            return(isReadOnly && allAssignmentsAreOfImmutableValues);
        }
Example #11
0
            public static Options Parse(string[] args)
            {
                var diagnostics = new DiagnosticSink();
                var range       = new SourceRange("<command line>");

                Options result = new Options();

                int argCount = args.Length;
                int argIdx   = 0;

                while (argIdx < argCount)
                {
                    var argStr = args[argIdx++];
                    if (argStr.StartsWith("-"))
                    {
                        if (argStr.StartsWith("-o"))
                        {
                            var option = argStr.Substring(2);
                            if (string.IsNullOrWhiteSpace(option))
                            {
                                option = args[argIdx++];
                            }

                            result.outputPrefix = option;
                        }
                        else
                        {
                            diagnostics.Add(
                                Severity.Error,
                                range,
                                "Unknown option '{0}'",
                                argStr);
                        }
                    }
                    else
                    {
                        result.fileNames.Add(argStr);
                    }
                }

                int fileCount = result.fileNames.Count;

                if (fileCount == 1)
                {
                    if (result.outputPrefix == null)
                    {
                        result.outputPrefix = result.fileNames[0];
                    }
                }
                else if (fileCount == 0)
                {
                    diagnostics.Add(
                        Severity.Error,
                        range,
                        "No input files given");
                }
                else
                {
                    if (result.outputPrefix == null)
                    {
                        diagnostics.Add(
                            Severity.Error,
                            range,
                            "When multiple input files are given, an output prefix must be selected with -o");
                    }
                }

                int errorCount = diagnostics.Flush(System.Console.Error);

                if (errorCount != 0)
                {
                    System.Console.Error.WriteLine(
                        "Usage: sparkc [-o outputPrefix] file.spark file2.spark");
                    return(null);
                }

                return(result);
            }