public ConstantBindingContainer( IReadOnlyList <ITypeSymbol> bindFromTypes, ITypeSymbol constTypeSymbol, ArgumentSyntax constantSyntax, BindScopeEnum scope, ArgumentSyntax?whenArgumentClause ) : base(bindFromTypes, constTypeSymbol, scope, whenArgumentClause, constantSyntax, null) { ConstructorArguments = new List <DetectedConstructorArgument>(); NotBindConstructorArgumentTypes = new HashSet <ITypeSymbol>(); }
public ConstantBindingContainer( BindingContainerTypes types, ArgumentSyntax constantSyntax, BindScopeEnum scope, ExpressionStatementSyntax expressionNode, ArgumentSyntax?whenArgumentClause, IReadOnlyList <IDefinedSetting> settings ) : base(types, scope, expressionNode, whenArgumentClause, constantSyntax, settings, false) { ConstructorArguments = new List <DetectedMethodArgument>(); }
protected BaseBindingContainer( BindingContainerTypes types, BindScopeEnum scope, ExpressionStatementSyntax expressionNode, ArgumentSyntax?whenArgumentClause, ArgumentSyntax?constantSyntax, IReadOnlyList <IDefinedSetting> settings, bool isConventional ) { if (types is null) { throw new ArgumentNullException(nameof(types)); } if (scope == BindScopeEnum.Constant && constantSyntax is null) { throw new DpdtException(DpdtExceptionTypeEnum.InternalError, $"Misconfiguration between scope and constant syntax"); } if (settings is null) { throw new ArgumentNullException(nameof(settings)); } if (scope != BindScopeEnum.Constant && !(constantSyntax is null)) { throw new DpdtException(DpdtExceptionTypeEnum.InternalError, $"Misconfiguration between scope and constant syntax"); } _types = types; Scope = scope; ExpressionNode = expressionNode; WhenArgumentClause = whenArgumentClause; ConstantSyntax = constantSyntax; _settings = settings; IsConventional = isConventional; UniqueUnstableIdentifier = Guid.NewGuid(); var uniqueKey0 = expressionNode.SyntaxTree.FilePath.GetStringSha256Hash().SafeSubstring(0, 8); var uniqueKey1 = expressionNode.Span.Start; var uniqueKey2 = expressionNode.Span.End; var uniqueKey3 = types.BindToType.ToGlobalDisplayString().GetStringSha256Hash().SafeSubstring(0, 8); _uniqueKey = $"u{uniqueKey0}_{uniqueKey1}_{uniqueKey2}_{uniqueKey3}"; }
public BindingContainerWithInstance( BindingContainerTypes types, IReadOnlyList <DetectedMethodArgument> constructorArguments, BindScopeEnum scope, ExpressionStatementSyntax expressionNode, ArgumentSyntax?whenArgumentClause, IReadOnlyList <IDefinedSetting> settings, bool isConventional ) : base(types, scope, expressionNode, whenArgumentClause, null, settings, isConventional) { if (constructorArguments is null) { throw new ArgumentNullException(nameof(constructorArguments)); } ConstructorArguments = constructorArguments; }
private void ProcessConstant( ExpressionStatementSyntax expressionNode, GenericNameSyntax bindGenericNode, ArgumentSyntax?whenArgumentClause ) { var genericNodes = expressionNode .DescendantNodes() .OfType <GenericNameSyntax>() .ToList(); var withScopeSyntax = expressionNode .DescendantNodes() .Where(s => s.GetText().ToString() == nameof(IToOrConstantBinding.WithConstScope)) .First(); var constTypeSymbol = (_semanticModel.GetSymbolInfo(withScopeSyntax).Symbol as IMethodSymbol) !.TypeArguments[0]; var constantClause = DetermineArgumentSubClause( expressionNode, nameof(IToOrConstantBinding.WithConstScope) ); if (constantClause is null) { throw new DpdtException(DpdtExceptionTypeEnum.InternalError, $"Cannot find constant clause"); } var bindFromTypeSemantics = GetBindFromTypes( bindGenericNode ); var bindingContainer = new ConstantBindingContainer( bindFromTypeSemantics, constTypeSymbol, constantClause, BindScopeEnum.Constant, whenArgumentClause ); _bindingContainers.Add(bindingContainer); }
public BindingContainerWithInstance( IReadOnlyList <ITypeSymbol> bindFromTypes, ITypeSymbol bindToType, IReadOnlyList <DetectedConstructorArgument> constructorArguments, BindScopeEnum scope, ArgumentSyntax?whenArgumentClause, ITypeSymbol?factoryPayloadType ) : base(bindFromTypes, bindToType, scope, whenArgumentClause, null, factoryPayloadType) { if (constructorArguments is null) { throw new ArgumentNullException(nameof(constructorArguments)); } ConstructorArguments = constructorArguments; NotBindConstructorArgumentTypes = new HashSet <ITypeSymbol>( constructorArguments .Where(ca => !ca.DefineInBindNode) .Select(ca => ca.Type !), new TypeSymbolEqualityComparer() ); }
private void ApppendSource(GeneratorExecutionContext context, StringBuilder sourceBuilder, GenerationDetails generateThis) { string propertyName = generateThis.MethodNameNode.Identifier.ValueText; string dpMemberName = propertyName + "Property"; string dpkMemberName = propertyName + "PropertyKey"; Accessibility dpAccess = generateThis.FieldSymbol.DeclaredAccessibility; Accessibility dpkAccess = generateThis.FieldSymbol.DeclaredAccessibility; // If this is a DependencyPropertyKey, then we may need to create the corresponding DependencyProperty field. // We do this because it's proper to always have a DependencyProperty field & because the DependencyProperty // field is required when using TemplateBindings in XAML. if (generateThis.IsDpk) { ISymbol?dpMemberSymbol = generateThis.FieldSymbol.ContainingType.GetMembers(dpMemberName).FirstOrDefault(); if (dpMemberSymbol != null) { dpAccess = dpMemberSymbol.DeclaredAccessibility; } else { dpAccess = Accessibility.Public; // Something like... // public static readonly DependencyProperty FooProperty = FooPropertyKey.DependencyProperty; sourceBuilder.Append($@" public static readonly DependencyProperty {dpMemberName} = {dpkMemberName}.DependencyProperty;" ); } } // Try to get the generic type argument (if it exists, this will be the type of the property). GeneratorOps.TryGetGenericTypeArgument(context, generateThis.MethodNameNode, out ITypeSymbol? genTypeArg); // We support 0, 1, or 2 arguments. Check for default value and/or flags arguments. // (A) Gen.Foo<T>() // (B) Gen.Foo(defaultValue) // (C) Gen.Foo<T>(flags) // (D) Gen.Foo(defaultValue, flags) // The first argument is either the default value or the flags. // Note: We do not support properties whose default value is `FrameworkPropertyMetadataOptions` because // it's a niche case that would add code complexity. ArgumentSyntax?defaultValueArgNode = null; ITypeSymbol? typeOfFirstArg = null; bool hasFlags = false; if (GeneratorOps.TryGetAncestor(generateThis.MethodNameNode, out InvocationExpressionSyntax? invocationExpressionNode)) { var args = invocationExpressionNode.ArgumentList.Arguments; if (args.Count > 0) { // If the first argument is the flags, then we generate (C); otherwise, we generate (B) or (D). typeOfFirstArg = GetArgumentType(context, args[0]) ?? this.objTypeSymbol; if (typeOfFirstArg.Equals(this.flagsTypeSymbol, SymbolEqualityComparer.Default)) { hasFlags = true; } else { defaultValueArgNode = args[0]; hasFlags = args.Count > 1; } } } bool hasDefaultValue = defaultValueArgNode != null; // Determine the type of the property. // If there is a generic type argument, then use that; otherwise, use the type of the default value argument. // As a safety precaution - ensure that the generated code is always valid by defaulting to use `object`. // But really, if we were unable to get the type, that means the user's code doesn't compile anyhow. generateThis.PropertyType = genTypeArg ?? (hasDefaultValue ? typeOfFirstArg : null) ?? this.objTypeSymbol; generateThis.PropertyTypeName = generateThis.PropertyType.ToDisplayString(); string genClassDecl; string?moreDox = null; if (generateThis.IsAttached) { string targetTypeName = "DependencyObject"; if (generateThis.MethodNameNode.Parent is MemberAccessExpressionSyntax memberAccessExpr && memberAccessExpr.Expression is GenericNameSyntax genClassNameNode) { genClassDecl = "GenAttached<__TTarget> where __TTarget : DependencyObject"; if (GeneratorOps.TryGetGenericTypeArgument(context, genClassNameNode, out ITypeSymbol? attachmentNarrowingType)) { generateThis.AttachmentNarrowingType = attachmentNarrowingType; targetTypeName = attachmentNarrowingType.ToDisplayString(); moreDox = $@"<br/>This attached property is only for use with objects of type <typeparamref name=""__TTarget""/>."; } } else { genClassDecl = "GenAttached"; } // Write the static get/set methods source code. string getterAccess = dpAccess.ToString().ToLower(); string setterAccess = generateThis.IsDpk ? dpkAccess.ToString().ToLower() : getterAccess; string setterArg0 = generateThis.IsDpk ? dpkMemberName : dpMemberName; // Something like... // public static int GetFoo(DependencyObject d) => (int)d.GetValue(FooProperty); // private static void SetFoo(DependencyObject d, int value) => d.SetValue(FooPropertyKey); sourceBuilder.Append($@" {getterAccess} static {generateThis.PropertyTypeName} Get{propertyName}({targetTypeName} d) => ({generateThis.PropertyTypeName})d.GetValue({dpMemberName}); {setterAccess} static void Set{propertyName}({targetTypeName} d, {generateThis.PropertyTypeName} value) => d.SetValue({setterArg0}, value);" ); }
internal static bool TryGetRegisteredType(FieldOrProperty fieldOrProperty, SemanticModel semanticModel, CancellationToken cancellationToken, [NotNullWhen(true)] out ArgumentSyntax?typeArg, [NotNullWhen(true)] out ITypeSymbol?result) { typeArg = null; result = null; if (fieldOrProperty.TryGetAssignedValue(cancellationToken, out var value) && value is InvocationExpressionSyntax invocation) { if (TryGetRegisterCall(invocation, semanticModel, cancellationToken, out _) && invocation.TryGetArgumentAtIndex(3, out typeArg)) { return(typeArg.TryGetTypeofValue(semanticModel, cancellationToken, out result)); } } return(false); }
/// <summary> /// Get the argument that matches <paramref name="parameter"/>. /// </summary> /// <param name="initializer">The <see cref="ConstructorInitializerSyntax"/>.</param> /// <param name="parameter">The <see cref="IParameterSymbol"/>.</param> /// <param name="argument">The <see cref="ArgumentSyntax"/>.</param> /// <returns>True if a match was found.</returns> public static bool TryFindArgument(this ConstructorInitializerSyntax initializer, IParameterSymbol parameter, [NotNullWhen(true)] out ArgumentSyntax?argument) { if (initializer is null) { throw new System.ArgumentNullException(nameof(initializer)); } if (parameter is null) { throw new System.ArgumentNullException(nameof(parameter)); } argument = null; return(initializer.ArgumentList is { } argumentList&& argumentList.TryFind(parameter, out argument)); }
private static bool TryGetArgs(SyntaxNodeAnalysisContext context, [NotNullWhen(true)] out IMethodSymbol?target, [NotNullWhen(true)] out ArgumentSyntax?propertyArg, [NotNullWhen(true)] out ArgumentSyntax?valueArg) { if (context.Node is InvocationExpressionSyntax invocation) { var propertyParameter = QualifiedParameter.Create(KnownSymbols.DependencyProperty); var valueParameter = QualifiedParameter.Create(KnownSymbols.Object); return(invocation.TryGetTarget(KnownSymbols.DependencyObject.SetValue, propertyParameter, valueParameter, context.SemanticModel, context.CancellationToken, out target, out propertyArg, out valueArg) || invocation.TryGetTarget(KnownSymbols.DependencyObject.SetValue, QualifiedParameter.Create(KnownSymbols.DependencyPropertyKey), valueParameter, context.SemanticModel, context.CancellationToken, out target, out propertyArg, out valueArg) || invocation.TryGetTarget(KnownSymbols.DependencyObject.SetCurrentValue, propertyParameter, valueParameter, context.SemanticModel, context.CancellationToken, out target, out propertyArg, out valueArg)); } target = null; propertyArg = null; valueArg = null; return(false); }
/// <summary> /// Get the argument that matches <paramref name="parameter"/>. /// </summary> /// <param name="argumentList">The <see cref="ArgumentListSyntax"/>.</param> /// <param name="parameter">The <see cref="IParameterSymbol"/>.</param> /// <param name="argument">The <see cref="ArgumentSyntax"/>.</param> /// <returns>True if a match was found.</returns> public static bool TryFind(this ArgumentListSyntax argumentList, IParameterSymbol parameter, [NotNullWhen(true)] out ArgumentSyntax?argument) { if (argumentList is null) { throw new ArgumentNullException(nameof(argumentList)); } if (parameter is null) { throw new ArgumentNullException(nameof(parameter)); } switch (argumentList) { case { Arguments: { Count : 0 } } :
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 ConstantParsedBindExpression( SemanticModelDecorator semanticModel, ExpressionStatementSyntax expressionNode, List <Tuple <InvocationExpressionSyntax, IMethodSymbol> > invocationSymbols ) : base(BindScopeEnum.Constant, invocationSymbols) { if (semanticModel is null) { throw new ArgumentNullException(nameof(semanticModel)); } if (expressionNode is null) { throw new ArgumentNullException(nameof(expressionNode)); } if (invocationSymbols is null) { throw new ArgumentNullException(nameof(invocationSymbols)); } _semanticModel = semanticModel; _expressionNode = expressionNode; _invocationSymbols = invocationSymbols; var constantClause = DetermineArgumentSubClause( invocationSymbols, typeof(IToOrConstantBinding).GetMethod(nameof(IToOrConstantBinding.WithConstScope), BindingFlags.Public | BindingFlags.Instance) ! ); if (constantClause is null) { throw new DpdtException( DpdtExceptionTypeEnum.InternalError, $"Cannot find constant clause" ); } _constantClause = constantClause; _from = invocationSymbols.First( s => s.Item2.ContainingType.ToGlobalDisplayString() == typeof(DefaultCluster).ToGlobalDisplayString() && s.Item2.Name == DefaultCluster.BindMethodName ); _fromTypes = _from.Item2.TypeArguments; if (_fromTypes.Any(t => t is IDynamicTypeSymbol)) { throw new DpdtException( DpdtExceptionTypeEnum.IncorrectBinding_IncorrectFrom, $"Dynamic cannot be used as bind from type" ); } _constScope = invocationSymbols.First( s => s.Item2.ContainingType.ToGlobalDisplayString() == typeof(IToOrConstantBinding).ToGlobalDisplayString() && s.Item2.Name == nameof(IToOrConstantBinding.WithConstScope) ); //euristric if constant is a dynamic actually if (_constScope.Item2.TypeArguments[0].TypeKind == TypeKind.TypeParameter) { throw new DpdtException( DpdtExceptionTypeEnum.IncorrectBinding_IncorrectFrom, $"Dynamic cannot be used as bind from type" ); } _whenArgumentClause = DetermineArgumentSubClause( invocationSymbols, typeof(IConditionalBinding).GetMethod(nameof(IConditionalBinding.When), BindingFlags.Public | BindingFlags.Instance) !, typeof(IConstantConditionalBinding).GetMethod(nameof(IConstantConditionalBinding.When), BindingFlags.Public | BindingFlags.Instance) ! ); }
internal static bool TryGetCoerceValueCallback(ObjectCreationExpressionSyntax objectCreation, SemanticModel semanticModel, CancellationToken cancellationToken, [NotNullWhen(true)] out ArgumentSyntax?callback) { return(TryGetCallback(objectCreation, KnownSymbols.CoerceValueCallback, semanticModel, cancellationToken, out callback)); }
internal static bool TryGetDefaultValue(ObjectCreationExpressionSyntax objectCreation, SemanticModel semanticModel, CancellationToken cancellationToken, [NotNullWhen(true)] out ArgumentSyntax?defaultValueArg) { defaultValueArg = null; if (objectCreation?.ArgumentList == null || objectCreation.ArgumentList.Arguments.Count == 0) { return(false); } return(TryGetConstructor(objectCreation, semanticModel, cancellationToken, out var constructor) && constructor.Parameters.TryFirst(out var parameter) && parameter.Type == KnownSymbols.Object && objectCreation.ArgumentList.Arguments.TryFirst(out defaultValueArg)); }
private static bool TryGetAddHandlerCall(InvocationExpressionSyntax invocation, SyntaxNodeAnalysisContext context, [NotNullWhen(true)] out ArgumentSyntax?eventArgument) { eventArgument = null; if (invocation.TryGetMethodName(out var name) && name != "AddHandler") { return(false); } if (invocation.ArgumentList == null || invocation.ArgumentList.Arguments.Count < 2 || invocation.ArgumentList.Arguments.Count > 3) { return(false); } return(invocation.TryGetArgumentAtIndex(0, out eventArgument) && context.SemanticModel.GetTypeInfoSafe(eventArgument.Expression, context.CancellationToken).Type == KnownSymbols.RoutedEvent); }
private static bool PreferIsDefined(InvocationExpressionSyntax invocation, IMethodSymbol target, ExpressionSyntax member, ArgumentAndValue <ITypeSymbol> attributeType, ArgumentSyntax?inherits, [NotNullWhen(true)] out Location?location, [NotNullWhen(true)] out string?invocationText) { switch (invocation.Parent) { case BinaryExpressionSyntax binary when binary.Right.IsKind(SyntaxKind.NullLiteralExpression): if (binary.IsKind(SyntaxKind.EqualsExpression)) { location = binary.GetLocation(); invocationText = "!" + GetText(); return(true); } if (binary.IsKind(SyntaxKind.NotEqualsExpression)) { location = binary.GetLocation(); invocationText = GetText(); return(true); } break; case IsPatternExpressionSyntax { Pattern: ConstantPatternSyntax constantPattern } isPattern when constantPattern.Expression.IsKind(SyntaxKind.NullLiteralExpression) : location = isPattern.GetLocation(); invocationText = "!" + GetText(); return(true); } location = null; invocationText = null; return(false); string GetText() { var inheritsText = inherits is null ? string.Empty : $", {inherits}"; return(target.IsExtensionMethod ? $"{member}.IsDefined({attributeType.Argument}{inheritsText})" : $"Attribute.IsDefined({member}, {attributeType.Argument}{inheritsText})"); } }
private static bool IsValidatedNotNull(string possibleNullReference, BlockSyntax parent, SyntaxNode node, [NotNullWhen(true)] out SyntaxNode?suppressionCause) { suppressionCause = default(SyntaxNode); StatementSyntax?statement = node?.AncestorsAndSelf().OfType <StatementSyntax>().FirstOrDefault(); var siblings = parent.ChildNodes().ToList(); // Look in earlier statements to see if the variable was previously checked for null. for (int nodeIndex = siblings.FindIndex(x => x == statement); --nodeIndex >= 0;) { SyntaxNode previous = siblings[nodeIndex]; suppressionCause = previous; if (previous is ExpressionStatementSyntax expressionStatement) { if (expressionStatement.Expression is AssignmentExpressionSyntax assignmentExpression) { // Is the offending symbol assigned here? if (InvalidatedBy(assignmentExpression.Left.ToString(), possibleNullReference)) { return(IsKnownToBeNotNull(assignmentExpression.Right)); } } // Check if this is Assert.NotNull or Assert.IsNotNull for the same symbol if (IsAssert(expressionStatement.Expression, out string member, out ArgumentListSyntax? argumentList)) { if (member == NunitFrameworkConstants.NameOfAssertNotNull || member == NunitFrameworkConstants.NameOfAssertIsNotNull || member == NunitFrameworkConstants.NameOfAssertThat) { if (member == NunitFrameworkConstants.NameOfAssertThat) { // We must check the 2nd argument for anything but "Is.Null" // E.g.: Is.Not.Null.And.Not.Empty. ArgumentSyntax?secondArgument = argumentList.Arguments.ElementAtOrDefault(1); if (secondArgument?.ToString() == "Is.Null") { continue; } } ArgumentSyntax firstArgument = argumentList.Arguments.First(); if (CoveredBy(firstArgument.Expression.ToString(), possibleNullReference)) { return(true); } } } } else if (previous is LocalDeclarationStatementSyntax localDeclarationStatement) { VariableDeclarationSyntax declaration = localDeclarationStatement.Declaration; foreach (var variable in declaration.Variables) { if (variable.Identifier.ToString() == possibleNullReference) { return(IsKnownToBeNotNull(variable.Initializer?.Value)); } } } } return(false); }
/// <summary> /// Get the argument that matches <paramref name="parameter"/>. /// </summary> /// <param name="initializer">The <see cref="ConstructorInitializerSyntax"/>.</param> /// <param name="parameter">The <see cref="IParameterSymbol"/>.</param> /// <param name="argument">The <see cref="ArgumentSyntax"/>.</param> /// <returns>True if a match was found.</returns> public static bool TryFindArgument(this ConstructorInitializerSyntax initializer, IParameterSymbol parameter, [NotNullWhen(true)] out ArgumentSyntax?argument) { argument = null; return(initializer?.ArgumentList is ArgumentListSyntax argumentList && argumentList.TryFind(parameter, out argument)); }
internal static bool TryGetArgumentAtIndex(this InvocationExpressionSyntax?invocation, int index, [NotNullWhen(true)] out ArgumentSyntax?result) { result = null; return(invocation?.ArgumentList?.Arguments.TryElementAt(index, out result) == true); }
private void ProcessCustom( ExpressionStatementSyntax expressionNode, GenericNameSyntax bindGenericNode, ArgumentSyntax?whenArgumentClause ) { var genericNodes = expressionNode .DescendantNodes() .OfType <GenericNameSyntax>() .ToList(); var toGenericNode = genericNodes[1]; var toMethodName = toGenericNode.Identifier.Text; if (toMethodName.NotIn(nameof(IToOrConstantBinding.To), nameof(IToOrConstantBinding.ToFactory))) { throw new DpdtException(DpdtExceptionTypeEnum.InternalError, "Cannot find To clause for custom binding"); } var factoryPayloadSemantic = GetFactoryPayloadIfExists( toGenericNode ); var bindFromTypeSemantics = GetBindFromTypes( bindGenericNode ); var bindToSyntax = toGenericNode.TypeArgumentList.Arguments.First(); var bindToTypeSemantic = _semanticModel.GetTypeInfo(bindToSyntax).Type; if (bindToTypeSemantic == null) { throw new DpdtException( DpdtExceptionTypeEnum.InternalError, $"Unknown problem to access {nameof(bindToTypeSemantic)}" ); } CheckForFromAndToTypes( bindFromTypeSemantics, bindToTypeSemantic, !(factoryPayloadSemantic is null) ); var fullBindToTypeName = _typeInfoProvider.GetTypeByMetadataName(bindToTypeSemantic.ToDisplayString()); if (fullBindToTypeName == null) { throw new DpdtException( DpdtExceptionTypeEnum.InternalError, $"Unknown problem to access type for {bindToTypeSemantic.ToDisplayString()}" ); } var caExtractor = new ConstructorArgumentExtractor( _typeInfoProvider, _semanticModel ); caExtractor.Visit(expressionNode); var constructorArguments = GetConstructorArguments( caExtractor, fullBindToTypeName ); var bindingContainer = new BindingContainerWithInstance( bindFromTypeSemantics, bindToTypeSemantic, constructorArguments, BindScopeEnum.Custom, whenArgumentClause, factoryPayloadSemantic ); _bindingContainers.Add(bindingContainer); }
internal static bool TrySingleArgument(this InvocationExpressionSyntax?invocation, [NotNullWhen(true)] out ArgumentSyntax?result) { result = null; return(invocation?.ArgumentList?.Arguments.TrySingle(out result) == true); }
internal ArgumentAndValue(ArgumentSyntax?argument, T value) { this.Argument = argument; this.Value = value; }
private static bool TryGetArgs(InvocationExpressionSyntax invocation, SyntaxNodeAnalysisContext context, [NotNullWhen(true)] out IMethodSymbol?target, [NotNullWhen(true)] out ExpressionSyntax?member, out ArgumentAndValue <ITypeSymbol> attributeType, out ArgumentSyntax?inheritsArg) { if ((invocation.TryGetTarget(KnownSymbol.Attribute.IsDefined, context.SemanticModel, context.CancellationToken, out target) || invocation.TryGetTarget(KnownSymbol.CustomAttributeExtensions.IsDefined, context.SemanticModel, context.CancellationToken, out target)) && target.TryFindParameter("attributeType", out var attributeTypeParameter) && invocation.TryFindArgument(attributeTypeParameter, out var attributeTypeArg) && attributeTypeArg.Expression is TypeOfExpressionSyntax typeOf && context.SemanticModel.TryGetType(typeOf.Type, context.CancellationToken, out var typeSymbol)) { attributeType = new ArgumentAndValue <ITypeSymbol>(attributeTypeArg, typeSymbol); if (!target.TryFindParameter("inherit", out var inheritParameter) || !invocation.TryFindArgument(inheritParameter, out inheritsArg)) { inheritsArg = null; } if (target.IsExtensionMethod && invocation.Expression is MemberAccessExpressionSyntax memberAccess) { member = memberAccess.Expression; return(true); } if (target.TryFindParameter("element", out var elementParameter) && invocation.TryFindArgument(elementParameter, out var elementArg)) { member = elementArg.Expression; return(true); } } member = null; attributeType = default; inheritsArg = null; return(false); }
internal static bool TryGetRegisteredName(ObjectCreationExpressionSyntax objectCreation, SemanticModel semanticModel, CancellationToken cancellationToken, [NotNullWhen(true)] out ArgumentSyntax?nameArg, out string registeredName) { nameArg = null; registeredName = null; return(TryGetConstructor(objectCreation, semanticModel, cancellationToken, out _) && objectCreation.TryFirstAncestor(out InvocationExpressionSyntax? invocation) && DependencyProperty.TryGetRegisteredName(invocation, semanticModel, cancellationToken, out nameArg, out registeredName)); }
public override async Task RegisterCodeFixesAsync(CodeFixContext context) { Diagnostic?diagnostic = context.Diagnostics.First(); SyntaxNode?root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); var syntaxNode = (ExpressionSyntax)root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); CSharpUtils.ContainingFunctionData container = CSharpUtils.GetContainingFunction(syntaxNode); if (container.BlockOrExpression is null) { return; } SemanticModel?semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); ISymbol?enclosingSymbol = semanticModel.GetEnclosingSymbol(diagnostic.Location.SourceSpan.Start, context.CancellationToken); if (enclosingSymbol is null) { return; } bool convertToAsync = !container.IsAsync && Utils.HasAsyncCompatibleReturnType(enclosingSymbol as IMethodSymbol); if (convertToAsync) { // We don't support this yet, and we don't want to take the sync method path in this case. // The user will have to fix this themselves. return; } Regex lookupKey = (container.IsAsync || convertToAsync) ? CommonInterest.FileNamePatternForMethodsThatSwitchToMainThread : CommonInterest.FileNamePatternForMethodsThatAssertMainThread; string[] options = diagnostic.Properties[lookupKey.ToString()].Split('\n'); if (options.Length > 0) { // For any symbol lookups, we want to consider the position of the very first statement in the block. int positionForLookup = container.BlockOrExpression.GetLocation().SourceSpan.Start + 1; Lazy <ISymbol> cancellationTokenSymbol = new Lazy <ISymbol>(() => Utils.FindCancellationToken(semanticModel, positionForLookup, context.CancellationToken).FirstOrDefault()); foreach (var option in options) { // We're looking for methods that either require no parameters, // or (if we have one to give) that have just one parameter that is a CancellationToken. IMethodSymbol?proposedMethod = Utils.FindMethodGroup(semanticModel, option) .FirstOrDefault(m => !m.Parameters.Any(p => !p.HasExplicitDefaultValue) || (cancellationTokenSymbol.Value is object && m.Parameters.Length == 1 && Utils.IsCancellationTokenParameter(m.Parameters[0]))); if (proposedMethod is null) { // We can't find it, so don't offer to use it. continue; } if (proposedMethod.IsStatic) { OfferFix(option); } else { foreach (Tuple <bool, ISymbol>?candidate in Utils.FindInstanceOf(proposedMethod.ContainingType, semanticModel, positionForLookup, context.CancellationToken)) { if (candidate.Item1) { OfferFix($"{candidate.Item2.Name}.{proposedMethod.Name}"); } else { OfferFix($"{candidate.Item2.ContainingNamespace}.{candidate.Item2.ContainingType.Name}.{candidate.Item2.Name}.{proposedMethod.Name}"); } } } void OfferFix(string fullyQualifiedMethod) { context.RegisterCodeFix(CodeAction.Create($"Add call to {fullyQualifiedMethod}", ct => Fix(fullyQualifiedMethod, proposedMethod, cancellationTokenSymbol), fullyQualifiedMethod), context.Diagnostics); } } } Task <Document> Fix(string fullyQualifiedMethod, IMethodSymbol methodSymbol, Lazy <ISymbol> cancellationTokenSymbol) { int typeAndMethodDelimiterIndex = fullyQualifiedMethod.LastIndexOf('.'); IdentifierNameSyntax methodName = SyntaxFactory.IdentifierName(fullyQualifiedMethod.Substring(typeAndMethodDelimiterIndex + 1)); ExpressionSyntax invokedMethod = CSharpUtils.MemberAccess(fullyQualifiedMethod.Substring(0, typeAndMethodDelimiterIndex).Split('.'), methodName); InvocationExpressionSyntax?invocationExpression = SyntaxFactory.InvocationExpression(invokedMethod); IParameterSymbol? cancellationTokenParameter = methodSymbol.Parameters.FirstOrDefault(Utils.IsCancellationTokenParameter); if (cancellationTokenParameter is object && cancellationTokenSymbol.Value is object) { ArgumentSyntax?arg = SyntaxFactory.Argument(SyntaxFactory.IdentifierName(cancellationTokenSymbol.Value.Name)); if (methodSymbol.Parameters.IndexOf(cancellationTokenParameter) > 0) { arg = arg.WithNameColon(SyntaxFactory.NameColon(SyntaxFactory.IdentifierName(cancellationTokenParameter.Name))); } invocationExpression = invocationExpression.AddArgumentListArguments(arg); } ExpressionSyntax? awaitExpression = container.IsAsync ? SyntaxFactory.AwaitExpression(invocationExpression) : null; ExpressionStatementSyntax?addedStatement = SyntaxFactory.ExpressionStatement(awaitExpression ?? invocationExpression) .WithAdditionalAnnotations(Simplifier.Annotation, Formatter.Annotation); var initialBlockSyntax = container.BlockOrExpression as BlockSyntax; if (initialBlockSyntax is null) { SyntaxToken openBrace = SyntaxFactory.Token(SyntaxFactory.TriviaList(), SyntaxKind.OpenBraceToken, SyntaxFactory.TriviaList(SyntaxFactory.EndOfLine("\r\n"))); SyntaxToken closeBrace = SyntaxFactory.Token(SyntaxKind.CloseBraceToken); SyntaxList <StatementSyntax> statementList = SyntaxFactory.List <StatementSyntax>(new[] { SyntaxFactory.ReturnStatement((ExpressionSyntax)container.BlockOrExpression) }); initialBlockSyntax = SyntaxFactory.Block(openBrace, statementList, closeBrace) .WithAdditionalAnnotations(Formatter.Annotation); } BlockSyntax?newBlock = initialBlockSyntax.WithStatements(initialBlockSyntax.Statements.Insert(0, addedStatement)); return(Task.FromResult(context.Document.WithSyntaxRoot(root.ReplaceNode(container.BlockOrExpression.Parent, container.BodyReplacement(newBlock))))); } }
public override async Task RegisterCodeFixesAsync(CodeFixContext context) { foreach (Diagnostic?diagnostic in context.Diagnostics) { SyntaxNode?root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); ExpressionSyntax?syntaxNode = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true) as ExpressionSyntax; if (syntaxNode is null) { continue; } CSharpUtils.ContainingFunctionData container = CSharpUtils.GetContainingFunction(syntaxNode); if (container.BlockOrExpression is null) { return; } if (!container.IsAsync) { if (!(container.Function is MethodDeclarationSyntax || container.Function is AnonymousFunctionExpressionSyntax)) { // We don't support converting whatever this is into an async method. return; } } SemanticModel?semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); ISymbol?enclosingSymbol = semanticModel.GetEnclosingSymbol(diagnostic.Location.SourceSpan.Start, context.CancellationToken); if (enclosingSymbol is null) { return; } var hasReturnValue = ((enclosingSymbol as IMethodSymbol)?.ReturnType as INamedTypeSymbol)?.IsGenericType ?? false; ImmutableArray <CommonInterest.QualifiedMember> options = await CommonFixes.ReadMethodsAsync(context, CommonInterest.FileNamePatternForMethodsThatSwitchToMainThread, context.CancellationToken); int positionForLookup = diagnostic.Location.SourceSpan.Start; ISymbol cancellationTokenSymbol = Utils.FindCancellationToken(semanticModel, positionForLookup, context.CancellationToken).FirstOrDefault(); foreach (CommonInterest.QualifiedMember option in options) { // We're looking for methods that either require no parameters, // or (if we have one to give) that have just one parameter that is a CancellationToken. IMethodSymbol?proposedMethod = Utils.FindMethodGroup(semanticModel, option) .FirstOrDefault(m => !m.Parameters.Any(p => !p.HasExplicitDefaultValue) || (cancellationTokenSymbol is object && m.Parameters.Length == 1 && Utils.IsCancellationTokenParameter(m.Parameters[0]))); if (proposedMethod is null) { // We can't find it, so don't offer to use it. continue; } if (proposedMethod.IsStatic) { OfferFix(option.ToString()); } else { foreach (Tuple <bool, ISymbol>?candidate in Utils.FindInstanceOf(proposedMethod.ContainingType, semanticModel, positionForLookup, context.CancellationToken)) { if (candidate.Item1) { OfferFix($"{candidate.Item2.Name}.{proposedMethod.Name}"); } else { OfferFix($"{candidate.Item2.ContainingNamespace}.{candidate.Item2.ContainingType.Name}.{candidate.Item2.Name}.{proposedMethod.Name}"); } } } void OfferFix(string fullyQualifiedMethod) { context.RegisterCodeFix(CodeAction.Create($"Use 'await {fullyQualifiedMethod}'", ct => Fix(fullyQualifiedMethod, proposedMethod, hasReturnValue, ct), fullyQualifiedMethod), context.Diagnostics); } } async Task <Solution> Fix(string fullyQualifiedMethod, IMethodSymbol methodSymbol, bool hasReturnValue, CancellationToken cancellationToken) { StatementSyntax?assertionStatementToRemove = syntaxNode !.FirstAncestorOrSelf <StatementSyntax>(); int typeAndMethodDelimiterIndex = fullyQualifiedMethod.LastIndexOf('.'); IdentifierNameSyntax methodName = SyntaxFactory.IdentifierName(fullyQualifiedMethod.Substring(typeAndMethodDelimiterIndex + 1)); ExpressionSyntax invokedMethod = CSharpUtils.MemberAccess(fullyQualifiedMethod.Substring(0, typeAndMethodDelimiterIndex).Split('.'), methodName) .WithAdditionalAnnotations(Simplifier.Annotation); InvocationExpressionSyntax?invocationExpression = SyntaxFactory.InvocationExpression(invokedMethod); IParameterSymbol? cancellationTokenParameter = methodSymbol.Parameters.FirstOrDefault(Utils.IsCancellationTokenParameter); if (cancellationTokenParameter is object && cancellationTokenSymbol is object) { ArgumentSyntax?arg = SyntaxFactory.Argument(SyntaxFactory.IdentifierName(cancellationTokenSymbol.Name)); if (methodSymbol.Parameters.IndexOf(cancellationTokenParameter) > 0) { arg = arg.WithNameColon(SyntaxFactory.NameColon(SyntaxFactory.IdentifierName(cancellationTokenParameter.Name))); } invocationExpression = invocationExpression.AddArgumentListArguments(arg); } ExpressionSyntax awaitExpression = SyntaxFactory.AwaitExpression(invocationExpression); ExpressionStatementSyntax?addedStatement = SyntaxFactory.ExpressionStatement(awaitExpression) .WithAdditionalAnnotations(Simplifier.Annotation, Formatter.Annotation); var methodAnnotation = new SyntaxAnnotation(); CSharpSyntaxNode methodSyntax = container.Function.ReplaceNode(assertionStatementToRemove, addedStatement) .WithAdditionalAnnotations(methodAnnotation); Document newDocument = context.Document.WithSyntaxRoot(root.ReplaceNode(container.Function, methodSyntax)); SyntaxNode?newSyntaxRoot = await newDocument.GetSyntaxRootAsync(cancellationToken); methodSyntax = (CSharpSyntaxNode)newSyntaxRoot.GetAnnotatedNodes(methodAnnotation).Single(); if (!container.IsAsync) { switch (methodSyntax) { case AnonymousFunctionExpressionSyntax anonFunc: semanticModel = await newDocument.GetSemanticModelAsync(cancellationToken); methodSyntax = FixUtils.MakeMethodAsync(anonFunc, hasReturnValue, semanticModel, cancellationToken); newDocument = newDocument.WithSyntaxRoot(newSyntaxRoot.ReplaceNode(anonFunc, methodSyntax)); break; case MethodDeclarationSyntax methodDecl: (newDocument, methodSyntax) = await FixUtils.MakeMethodAsync(methodDecl, newDocument, cancellationToken); break; } } return(newDocument.Project.Solution); } } }
internal static bool TryGetRegisteredName(BackingFieldOrProperty backing, SemanticModel semanticModel, CancellationToken cancellationToken, [NotNullWhen(true)] out ArgumentSyntax?nameArg, [NotNullWhen(true)] out string?result) { nameArg = null; result = null; if (TryGetRegisterInvocationRecursive(backing, semanticModel, cancellationToken, out var invocation, out var method)) { return(method.TryFindParameter("name", out var parameter) && invocation.TryFindArgument(parameter, out nameArg) && nameArg.TryGetStringValue(semanticModel, cancellationToken, out result)); } if (TryGetDependencyAddOwnerSourceField(backing, semanticModel, cancellationToken, out var source) && !source.Symbol.Equals(backing.Symbol)) { return(TryGetRegisteredName(source, semanticModel, cancellationToken, out nameArg, out result)); } if (backing.Symbol.Locations.All(x => !x.IsInSource) && TryGetPropertyByName(backing, out var property)) { result = property.Name; return(true); } return(false); }
/// <summary> /// Get the argument that matches <paramref name="parameter"/>. /// </summary> /// <param name="objectCreation">The <see cref="ObjectCreationExpressionSyntax"/>.</param> /// <param name="parameter">The <see cref="IParameterSymbol"/>.</param> /// <param name="argument">The <see cref="ArgumentSyntax"/>.</param> /// <returns>True if a match was found.</returns> public static bool TryFindArgument(this ObjectCreationExpressionSyntax objectCreation, IParameterSymbol parameter, [NotNullWhen(true)] out ArgumentSyntax?argument) { if (objectCreation is null) { throw new System.ArgumentNullException(nameof(objectCreation)); } if (parameter is null) { throw new System.ArgumentNullException(nameof(parameter)); } argument = null; return(objectCreation.ArgumentList is { } argumentList&& argumentList.TryFind(parameter, out argument)); }
internal static bool TryGetRegisteredName(FieldOrProperty fieldOrProperty, SemanticModel semanticModel, CancellationToken cancellationToken, [NotNullWhen(true)] out ArgumentSyntax?nameArg, [NotNullWhen(true)] out string?result) { nameArg = null; result = null; if (fieldOrProperty.TryGetAssignedValue(cancellationToken, out var value) && value is InvocationExpressionSyntax invocation) { if (TryGetRegisterCall(invocation, semanticModel, cancellationToken, out _) && invocation.TryGetArgumentAtIndex(0, out nameArg)) { return(nameArg.TryGetStringValue(semanticModel, cancellationToken, out result)); } } return(false); }