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 )); }
public ImmutableDefinitionChecker( Compilation compilation, DiagnosticSink diagnosticSink, ImmutabilityContext context, AnnotationsContext annotationsContext ) { m_compilation = compilation; m_diagnosticSink = diagnosticSink; m_context = context; m_annotationsContext = annotationsContext; }
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))); }
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))); }
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); }
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); }
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); }