public NotNullMethodInfo VisitGetterMethod(SemanticModel model, IPropertySymbol propertySymbol, AccessorDeclarationSyntax getter) { var symbol = model.GetDeclaredSymbol(getter); if (symbol != null) { if (propertySymbol.HasNotNull() || symbol.HasNotNull()) { if (symbol.ReturnsVoid || IsValueType(symbol.ReturnType)) { context.ReportDiagnostic(MainAnalyzer.CreateBadAttributeUsageError(getter.GetLocation(), true)); return(null); } if (getter.Body != null) { return(new NotNullMethodInfo(getter.GetLocation(), symbol, getter.Body)); } } else if (FindInheritedReference(model, symbol).HasNotNull()) { context.ReportDiagnostic(MainAnalyzer.CreateMissingAttribute(getter.GetLocation(), symbol.ToString())); } } return(null); }
// This is kinda duplicated in CodeBlockAnalyzer.GetAssignmentStatus private void CheckExpressionForNull(ISymbol symbol, ExpressionSyntax expression, MethodFlowAnalysis analysis = null) { var expressionValueType = expression.GetTypeOfValue(context.SemanticModel); if (expressionValueType == ValueType.NotNull) { // Argument cannot be null, so move to the next return; } if (expressionValueType == ValueType.Null) { context.ReportDiagnostic(MainAnalyzer.CreateReturnNull(expression.GetLocation(), symbol.ToString())); return; } if (analysis == null) { context.ReportDiagnostic(MainAnalyzer.CreateReturnNull(expression.GetLocation(), symbol.ToString())); return; } ExpressionStatus status = analysis.IsAlwaysAssigned(expression, expression); if (!status.IsAssigned()) { context.ReportDiagnostic(MainAnalyzer.CreateReturnNull(expression.GetLocation(), symbol.ToString())); } if (status == ExpressionStatus.AssignedWithUnneededConstraint) { context.ReportDiagnostic(MainAnalyzer.CreateUnneededConstraint(expression.GetLocation(), symbol.ToString())); } }
public NotNullMethodInfo VisitMethod(SemanticModel model, MethodDeclarationSyntax method) { var symbol = model.GetDeclaredSymbol(method); if (symbol != null) { if (symbol.HasNotNull()) { if (symbol.ReturnsVoid || IsValueType(symbol.ReturnType)) { context.ReportDiagnostic(MainAnalyzer.CreateBadAttributeUsageError(method.GetLocation(), true)); return(null); } if (method.Body != null) { return(new NotNullMethodInfo(method.GetLocation(), symbol, method.Body)); } } else if (FindInheritedReference(model, symbol).HasNotNull()) { context.ReportDiagnostic(MainAnalyzer.CreateMissingAttribute(method.GetLocation(), symbol.ToString())); } } return(null); }
public void Analyze(SemanticModel model) { var constructorFlowAnalyzer = new CtorFlowAnalyzer(model); var root = model.SyntaxTree.GetRoot(); var classes = root.DescendantNodes().OfType <ClassDeclarationSyntax>(); foreach (var @class in classes) { var classSymbol = model.GetDeclaredSymbol(@class); if (classSymbol != null && classSymbol.HasNotNull()) { context.ReportDiagnostic(MainAnalyzer.CreateBadAttributeUsageError(@class.GetLocation(), false)); } var members = GetTrackedMembers(model, @class); if (members.Item1.Count > 0) { FlagUninitializedFields(@class, constructorFlowAnalyzer, members.Item1); } if (members.Item2.Count > 0) { VerifyMethods(members.Item2); } if (members.Item3.Count > 0) { VerifyExpressionBodies(members.Item3); } } }
private void FlagUninitializedFields( ClassDeclarationSyntax @class, CtorFlowAnalyzer constructorFlowAnalyzer, Dictionary <ISymbol, NotNullFieldInfo> fields) { var flowAnalysis = constructorFlowAnalyzer.AnalyzeDataFlow(@class, fields); foreach (var member in fields.Values) { var isInitialized = member.Initializer != null && member.Initializer.Value.GetTypeOfValue(context.SemanticModel) == ValueType.NotNull; if (!isInitialized) { if (flowAnalysis.Count == 0) { context.ReportDiagnostic(MainAnalyzer.CreateMemberNotInitialized(member.Location, member.Symbol)); continue; } foreach (var flow in flowAnalysis) { if (flow.UnassignedMembers.Contains(member)) { context.ReportDiagnostic(MainAnalyzer.CreateMemberNotInitialized(flow.Constructor.GetLocation(), member.Symbol)); } } } } }
private void ReportIfIsNotNullSymbol(ExpressionSyntax expression) { var target = expression.FindUnderlyingMember(); if (target == null) { return; } if (target is ConditionalExpressionSyntax conditional) { ReportIfIsNotNullSymbol(conditional.WhenTrue); ReportIfIsNotNullSymbol(conditional.WhenFalse); return; } // Make sure the expression is always Not Null. This catches the case: ([NotNull]x as SomeType) which can be null. var expressionValue = expression.GetTypeOfValue(context.SemanticModel); switch (expressionValue) { case ValueType.NotNull: { var symbol = context.SemanticModel.GetSymbolInfo(target).Symbol; if (symbol.HasNotNullOrCheckNull()) { if (symbol is IParameterSymbol && expression.Parent.Parent is IfStatementSyntax ifStatement) { if (ifStatement.Statement is ThrowStatementSyntax) { return; } if (ifStatement.Statement is BlockSyntax block && block.ChildNodes().FirstOrDefault() is ThrowStatementSyntax) { return; } } context.ReportDiagnostic(MainAnalyzer.CreateUnneededNullCheckError(expression.GetLocation(), symbol)); } break; } case ValueType.MaybeNull: { var status = GetAssignmentStatus(expression, expression, ValueType.MaybeNull, AnalysisMode.Strict); if (status.IsAssigned()) { var symbol = context.SemanticModel.GetSymbolInfo(target).Symbol; context.ReportDiagnostic(MainAnalyzer.CreateUnneededNullCheckError(expression.GetLocation(), symbol)); } break; } } }
public void Analyze(SyntaxNode node) { needsConstraintChecking.Clear(); this.Visit(node); foreach (var method in needsConstraintChecking.Values) { foreach (var violation in method.GetAssignmentsAfterConstraints()) { context.ReportDiagnostic( MainAnalyzer.CreateAssignmentAfterConstraint(violation.Expression.GetLocation(), violation.Expression.ToString())); } } }
private void ReportIssue(ExpressionStatus status, Location location, string errorContext) { switch (status) { case ExpressionStatus.NotAssigned: context.ReportDiagnostic(MainAnalyzer.CreateNullAssignmentError(location, errorContext)); break; case ExpressionStatus.ReassignedAfterCondition: context.ReportDiagnostic(MainAnalyzer.CreateAssignmentAfterCondition(location, errorContext)); break; case ExpressionStatus.AssignedWithUnneededConstraint: context.ReportDiagnostic(MainAnalyzer.CreateUnneededConstraint(location, errorContext)); break; } }
public NotNullExpressionBodyInfo VisitExpressionBody(SemanticModel model, IPropertySymbol propertySymbol, ExpressionSyntax expression, Location location) { if (propertySymbol.HasNotNull()) { if (!propertySymbol.Type.IsReferenceType) { context.ReportDiagnostic(MainAnalyzer.CreateBadAttributeUsageError(location, true)); return(null); } return(new NotNullExpressionBodyInfo(location, propertySymbol, expression)); } else if (FindInheritedReference(model, propertySymbol).HasNotNull()) { context.ReportDiagnostic(MainAnalyzer.CreateMissingAttribute(location, propertySymbol.ToString())); } return(null); }
public override void VisitAssignmentExpression(AssignmentExpressionSyntax node) { base.VisitAssignmentExpression(node); var valueType = node.Right.GetTypeOfValue(context.SemanticModel); if (valueType == ValueType.NotNull) { return; } var target = node.Left.FindUnderlyingMember(); if (target == null) { return; } var symbol = context.SemanticModel.GetSymbolInfo(target).Symbol; if (!symbol.HasNotNullOrCheckNull()) { return; } switch (GetAssignmentStatus(node.Right, node, valueType)) { case ExpressionStatus.NotAssigned: context.ReportDiagnostic(MainAnalyzer.CreateNullAssignmentError(node.GetLocation(), symbol)); break; case ExpressionStatus.ReassignedAfterCondition: context.ReportDiagnostic(MainAnalyzer.CreateAssignmentAfterCondition(node.GetLocation(), node.ToString())); break; case ExpressionStatus.AssignedWithUnneededConstraint: context.ReportDiagnostic(MainAnalyzer.CreateUnneededConstraint(node.GetLocation(), node.ToString())); break; } }
public override void VisitConstructorInitializer(ConstructorInitializerSyntax node) { base.VisitConstructorInitializer(node); var ctor = context.SemanticModel.GetSymbolInfo(node).Symbol as IMethodSymbol; if (ctor == null) { return; } CheckMethodInvocation( node, ctor, node.ArgumentList, (status, l, s) => { if (status == ExpressionStatus.NotAssigned) { context.ReportDiagnostic(MainAnalyzer.CreatePropagateNotNullInCtors(l, s)); } }); }
public override void VisitInvocationExpression(InvocationExpressionSyntax node) { base.VisitInvocationExpression(node); var methodDefinition = context.SemanticModel.GetSymbolInfo(node).Symbol as IMethodSymbol; if (methodDefinition == null) { return; } if (node.IsCheckAgainstNull(methodDefinition, out var target, out var valueType)) { ReportIfIsNotNullSymbol(target); } if (methodDefinition.IsConstraintMethod()) { if (node.IsConstraint(context.SemanticModel, out var expression)) { var symbol = context.SemanticModel.GetSymbolInfo(expression).Symbol; if (symbol.HasNotNullOrCheckNull()) { context.ReportDiagnostic(MainAnalyzer.CreateUnneededConstraint(node.GetLocation(), symbol.ToString())); } } else { context.ReportDiagnostic(MainAnalyzer.CreateInvalidConstraintError(node.GetLocation())); } } CheckMethodInvocation( node, methodDefinition, node.ArgumentList, (status, location, error) => ReportIssue(status, location, error)); }
private void CheckMethodInvocation( SyntaxNode node, IMethodSymbol methodDefinition, ArgumentListSyntax argumentList, Action <ExpressionStatus, Location, string> reportAction) { try { if (argumentList == null) { // Is object initializer return; } ImmutableArray <IParameterSymbol> parameters; List <ExpressionSyntax> arguments; // Extension method if (methodDefinition.ReducedFrom != null) { var invocation = ((InvocationExpressionSyntax)node).Expression; ExpressionSyntax firstArg = null; // TODO: handle extension methods on other things like MemberBindingExpressionSyntax if (invocation is MemberAccessExpressionSyntax access) { firstArg = access.Expression; } if (firstArg != null) { parameters = methodDefinition.ReducedFrom.Parameters; arguments = new[] { firstArg }.Concat(argumentList.Arguments.Select(i => i.Expression)).ToList(); } else { parameters = methodDefinition.Parameters; arguments = argumentList.Arguments.Select(i => i.Expression).ToList(); } } else { parameters = methodDefinition.Parameters; arguments = argumentList.Arguments.Select(i => i.Expression).ToList(); } if (parameters.Length != arguments.Count) { // Compiler error return; } var parameterEnumerator = parameters.GetEnumerator(); foreach (var arg in arguments) { parameterEnumerator.MoveNext(); if (parameterEnumerator.Current.IsParams) { // Ignore for 'params' parameter return; } if (parameterEnumerator.Current.RefKind == RefKind.Ref) { var argSymbol = context.SemanticModel.GetSymbolInfo(arg).Symbol; if (argSymbol.HasNotNullOrCheckNull()) { context.ReportDiagnostic(MainAnalyzer.CreateNotNullAsRefParameter(arg.GetLocation(), arg.ToString())); continue; } } if (!parameterEnumerator.Current.HasNotNullOrCheckNull()) { // Only check [NotNull] parameters continue; } var status = GetAssignmentStatus(arg, node, arg.GetTypeOfValue(context.SemanticModel)); reportAction(status, arg.GetLocation(), $"{methodDefinition.Name}({arg})"); } } catch (Exception ex) { throw new ParseFailedException(node.GetLocation(), $"{ex.Message} --> Parse failed on: {methodDefinition.GetFullName()}", ex); } }
GetTrackedMembers(SemanticModel model, ClassDeclarationSyntax type) { var trackedFields = new Dictionary <ISymbol, NotNullFieldInfo>(); var trackedMethods = new Dictionary <IMethodSymbol, NotNullMethodInfo>(); var trackedExpressionBodies = new Dictionary <ISymbol, NotNullExpressionBodyInfo>(); foreach (var member in type.ChildNodes() .Where(i => i is PropertyDeclarationSyntax || i is FieldDeclarationSyntax || i is MethodDeclarationSyntax)) { var property = member as PropertyDeclarationSyntax; if (property != null) { var symbol = model.GetDeclaredSymbol(property); if (symbol == null) { throw new ParseFailedException(member.GetLocation(), "Parse failed on: " + property); } if (!property.IsAutoProperty()) { if (property.ExpressionBody?.Expression != null) { var info = VisitExpressionBody(model, symbol, property.ExpressionBody.Expression, property.GetLocation()); if (info != null) { trackedExpressionBodies.Add(info.Symbol, info); } } else { var getter = property.AccessorList?.Accessors.FirstOrDefault(i => i.Kind() == SyntaxKind.GetAccessorDeclaration); if (getter != null) { var info = VisitGetterMethod(model, symbol, getter); if (info != null) { trackedMethods.Add(info.Symbol, info); } } } // Is computed property, so we don't need to worry about the ctor setting it. continue; } if (symbol.HasNotNull()) { if (symbol.SetMethod != null || IsValueType(symbol.Type)) { context.ReportDiagnostic(MainAnalyzer.CreateBadAttributeUsageError(member.GetLocation(), IsValueType(symbol.Type))); continue; } trackedFields.Add(symbol, new NotNullFieldInfo(member.GetLocation(), symbol, property.Initializer)); } else if (FindInheritedReference(model, symbol).HasNotNull()) { context.ReportDiagnostic(MainAnalyzer.CreateMissingAttribute(member.GetLocation(), symbol.ToString())); } } var field = member as FieldDeclarationSyntax; if (field != null) { var declaration = field.Declaration.Variables.First(); var symbol = model.GetDeclaredSymbol(declaration) as IFieldSymbol; if (symbol == null) { throw new ParseFailedException(member.GetLocation(), "Parse failed on: " + declaration); } if (symbol.HasNotNull()) { if (!symbol.IsReadOnlyOrConst() || IsValueType(symbol.Type)) { context.ReportDiagnostic(MainAnalyzer.CreateBadAttributeUsageError(member.GetLocation(), IsValueType(symbol.Type))); continue; } trackedFields.Add(symbol, new NotNullFieldInfo(member.GetLocation(), symbol, declaration.Initializer)); } } if (member is MethodDeclarationSyntax method) { var info = VisitMethod(model, method); if (info != null) { trackedMethods.Add(info.Symbol, info); } } } #if PORTABLE return(new Tuple <Dictionary <ISymbol, NotNullFieldInfo>, Dictionary <IMethodSymbol, NotNullMethodInfo>, Dictionary <ISymbol, NotNullExpressionBodyInfo> >(trackedFields, trackedMethods, trackedExpressionBodies)); #else return(trackedFields, trackedMethods, trackedExpressionBodies); #endif }