private bool IsContractInvocation( InvocationExpressionSyntax invocationExpression, ContractMethodNames allowedMethodNames, INamedTypeSymbol contractTypeSymbol) { MemberAccessExpressionSyntax?memberAccess = invocationExpression .Expression.As(x => x as MemberAccessExpressionSyntax); if ((ParseContractMethodName(memberAccess?.Name.Identifier.Text) & allowedMethodNames) == ContractMethodNames.None) { return(false); } var memberSymbol = memberAccess ?.As(x => _semanticModel.GetSymbolInfo(x).Symbol as IMethodSymbol); // TODO: ToMetadataFullName() on every call is probably somewhat expensive if (memberSymbol == null || !memberSymbol.ContainingType.Equals(contractTypeSymbol)) { // This is not Contract. return(false); } return(true); }
private static string GetCheckMethod(ContractMethodNames contractMethod) { return(contractMethod switch { RequiresDebug => FluentContractNames.CheckDebugMethodName, AssertDebug => FluentContractNames.CheckDebugMethodName, _ => FluentContractNames.CheckMethodName, });
private static string GetTargetMethod(ContractMethodNames contractMethod) { if (contractMethod.IsPrecondition()) { return(FluentContractNames.Requires); } return(FluentContractNames.Assert); }
public bool GetContractInvocation(IMethodSymbol invokedMethod, out ContractMethodNames contract) { contract = default; if (!invokedMethod.ContainingType.Equals(_runtimeContractTypeSymbol)) { return(false); } contract = ParseContractMethodName(invokedMethod.Name); return(true); }
private bool IsContractInvocation( IMethodSymbol?memberSymbol, ContractMethodNames allowedMethodNames, INamedTypeSymbol contractTypeSymbol) { if ((ParseContractMethodName(memberSymbol?.Name) & allowedMethodNames) == ContractMethodNames.None) { return(false); } // TODO: ToMetadataFullName() on every call is probably somewhat expensive if (memberSymbol == null || !memberSymbol.ContainingType.Equals(contractTypeSymbol)) { // This is not Contract. return(false); } return(true); }
public bool IsStandardContractInvocation( InvocationExpressionSyntax invocationExpression, ContractMethodNames allowedMethodNames = ContractMethodNames.All) { return(IsContractInvocation(invocationExpression, allowedMethodNames, _standardContractTypeSymbol)); }
public static bool IsForAll(this ContractMethodNames contract) => (contract & RequiresForAll) != None;
public static bool IsNotNullOrWhiteSpace(this ContractMethodNames contract) => (contract & (RequiresNotNullOrWhiteSpace | AssertNotNullOrWhiteSpace)) != None;
private static (SyntaxNode source, SyntaxNode destination) GetFluentContractsReplacements( IInvocationOperation operation, ContractMethodNames contractMethod, SemanticModel semanticModel, CancellationToken token) { var invocationExpression = operation.Syntax; // Getting the original predicate. var predicateArgumentOperation = operation.Arguments[0]; var predicateArgument = (ArgumentSyntax)predicateArgumentOperation.Syntax; ArgumentSyntax?extraForAllArgument = null; int messageArgumentIndex = 1; // We need to mutate it for the cases like RequiresNotNull and RequiresNotNullOrEmpty if (contractMethod.IsNullCheck()) { predicateArgument = Argument( // Changing NotNull(x) to x != null BinaryExpression( SyntaxKind.NotEqualsExpression, predicateArgument.Expression, LiteralExpression(SyntaxKind.NullLiteralExpression))) .NormalizeWhitespace(); } else if (contractMethod.IsNotNullOrEmpty() || contractMethod.IsNotNullOrWhiteSpace()) { var stringMethodName = contractMethod.IsNotNullOrEmpty() ? nameof(string.IsNullOrEmpty) : nameof(string.IsNullOrWhiteSpace); // Targeting a full framework can cause an issue for null-ness analysis // because string.IsNullOrEmpty and string.IsNullOrWhiteSpace is not annotated with any attributes. // It means that the compiler can't recognize that the following code is correct and still emit the warning: // if (!string.IsNullOrEmpty(str)) return str.Length; predicateArgument = Argument( PrefixUnaryExpression( SyntaxKind.LogicalNotExpression, InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, PredefinedType(Token(SyntaxKind.StringKeyword)), IdentifierName(stringMethodName))) .WithArgumentList( ArgumentList(SingletonSeparatedList(predicateArgument)))) ).NormalizeWhitespace(); } else if (contractMethod.IsForAll()) { extraForAllArgument = (ArgumentSyntax)operation.Arguments[1].Syntax; messageArgumentIndex++; } // Detecting the following case: // if (predicate is false) {Contract.Assert(false, complicatedMessage);} var sourceNode = invocationExpression.Parent; if (predicateArgumentOperation.Value is ILiteralOperation lo && lo.ConstantValue.HasValue && lo.ConstantValue.Value.Equals(false)) { // this is Assert(false) case. if (operation.Parent?.Parent?.Parent is IConditionalOperation conditional && conditional.WhenFalse == null && conditional.WhenTrue.Children.Count() == 1) { // The contract is inside the if block with a single statement. sourceNode = conditional.Syntax; var negatedCondition = (ExpressionSyntax?)SyntaxGeneratorExtensions.Negate(conditional.Condition.Syntax, semanticModel, token); if (negatedCondition != null) { predicateArgument = Argument(negatedCondition); } } } var originalMessageArgument = operation.Arguments[messageArgumentIndex]; Func <string> nonDefaultArgument = () => contractMethod.IsForAll() ? operation.Arguments[1].Syntax.ToFullString() : predicateArgument.ToFullString(); // Using an original message if provided. // Otherwise using a predicate as the new message. var messageArgument = originalMessageArgument.IsImplicit == false ? (ArgumentSyntax)originalMessageArgument.Syntax : Argument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(nonDefaultArgument()))); var arguments = new SeparatedSyntaxList <ArgumentSyntax>() .Add(predicateArgument) .AddIfNotNull(extraForAllArgument); // Generating Contract.Check(predicate)?.Requires/Assert(message) var targetMethodName = GetTargetMethod(contractMethod); var checkMethodName = GetCheckMethod(contractMethod); var contractCall = ExpressionStatement( ConditionalAccessExpression( // Contract.Requires( InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName(FluentContractNames.ContractClassName), IdentifierName(checkMethodName))) // (predicate) .WithArgumentList( ArgumentList(arguments)), // ?.IsTrue(message) InvocationExpression( MemberBindingExpression( IdentifierName(targetMethodName))) .WithArgumentList( ArgumentList( SingletonSeparatedList(messageArgument))) ) ); var trivia = sourceNode.GetLeadingTrivia(); var finalNode = contractCall.WithLeadingTrivia(trivia); return(sourceNode, finalNode); }
public static bool IsNullCheck(this ContractMethodNames contract) => (contract & (RequiresNotNull | AssertNotNull)) != None;
public static bool IsPostcondition(this ContractMethodNames contract) => (contract & (Ensures | EnsuresOnThrow)) != None;
public static bool IsAssertFailure(this ContractMethodNames contract) => (contract & AssertFailure) != None;
public static bool IsPrecondition(this ContractMethodNames contract) => (contract & AllRequires) != None;
public bool IsFluentContractInvocation( InvocationExpressionSyntax invocationExpression, ContractMethodNames allowedMethodNames = ContractMethodNames.AllFluentContracts) { return(IsContractInvocation(invocationExpression, allowedMethodNames, _runtimeContractTypeSymbol)); }
public static bool IsNotNullOrEmpty(this ContractMethodNames contract) => (contract & (RequiresNotNullOrEmpty | AssertNotNullOrEmpty)) != None;
public bool IsFluentContractInvocation( IMethodSymbol method, ContractMethodNames allowedMethodNames = ContractMethodNames.AllFluentContracts) { return(IsContractInvocation(method, allowedMethodNames, _runtimeContractTypeSymbol)); }
private static (SyntaxNode source, SyntaxNode destination) GetFluentContractsReplacements( IInvocationOperation operation, ContractMethodNames contractMethod, SemanticModel semanticModel, CancellationToken token) { var invocationExpression = operation.Syntax; // Getting the original predicate. var predicateArgumentOperation = operation.Arguments[0]; var predicateArgument = (ArgumentSyntax)predicateArgumentOperation.Syntax; ArgumentSyntax?extraForAllArgument = null; int messageArgumentIndex = 1; // We need to mutate it for the cases like RequiresNotNull and RequiresNotNullOrEmpty if (contractMethod.IsNullCheck()) { predicateArgument = Argument( // Changing NotNull(x) to x != null BinaryExpression( SyntaxKind.NotEqualsExpression, predicateArgument.Expression, LiteralExpression(SyntaxKind.NullLiteralExpression))) .NormalizeWhitespace(); } else if (contractMethod.IsNotNullOrEmpty() || contractMethod.IsNotNullOrWhiteSpace()) { var stringMethodName = contractMethod.IsNotNullOrEmpty() ? nameof(string.IsNullOrEmpty) : nameof(string.IsNullOrWhiteSpace); // Targeting a full framework can cause an issue for null-ness analysis // because string.IsNullOrEmpty and string.IsNullOrWhiteSpace is not annotated with any attributes. // It means that the compiler can't recognize that the following code is correct and still emit the warning: // if (!string.IsNullOrEmpty(str)) return str.Length; predicateArgument = Argument( PrefixUnaryExpression( SyntaxKind.LogicalNotExpression, InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, PredefinedType(Token(SyntaxKind.StringKeyword)), IdentifierName(stringMethodName))) .WithArgumentList( ArgumentList(SingletonSeparatedList(predicateArgument)))) ).NormalizeWhitespace(); } // Detecting the following case: // if (predicate is false) {Contract.Assert(false, complicatedMessage);} var sourceNode = invocationExpression.Parent; var originalMessageArgument = operation.Arguments[messageArgumentIndex]; // Using an original message if provided. // Otherwise using a predicate as the new message. var messageArgument = originalMessageArgument.IsImplicit == false ? (ArgumentSyntax)originalMessageArgument.Syntax : null; var arguments = new SeparatedSyntaxList <ArgumentSyntax>() .Add(predicateArgument) .AddIfNotNull(extraForAllArgument) .AddIfNotNull(messageArgument); // Generating Contract.Check(predicate)?.Requires/Assert(message) var targetMethodName = GetTargetMethod(contractMethod); var contractCall = //ExpressionStatement( // Contract.Requires( InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName(FluentContractNames.ContractClassName), IdentifierName(targetMethodName))) // (predicate, message) .WithArgumentList(ArgumentList(arguments) //) ); var trivia = sourceNode.GetLeadingTrivia(); var finalNode = contractCall.WithLeadingTrivia(trivia); return(invocationExpression, finalNode); }