public TypeSyntax Type0(Typ typ) { bool aliasable; if (!$"{Namespace}.".StartsWith($"{typ.Namespace}.")) { AddImport(typ.Namespace); aliasable = false; } else { aliasable = true; } SimpleNameSyntax result = IdentifierName(typ.Name); if (typ.IsDeprecated) { result = result.WithIdentifier(result.Identifier.WithPragmaWarning(PragmaWarnings.Obsolete)); } if (typ.GenericArgTyps != null) { // Generic typ, so return a generic name by recursively calling this method on all type args. result = GenericName(result.Identifier, TypeArgumentList(SeparatedList(typ.GenericArgTyps.Select(Type)))); } if (aliasable) { // Annotate this type to show it might need fully aliasing if there is a name collision. result = result.WithAdditionalAnnotations(new SyntaxAnnotation(AliasableType, typ.Namespace)); } return(result); }
private static SimpleNameSyntax ChangeName(SimpleNameSyntax simpleName, string newName) { return(simpleName.WithIdentifier( Identifier( simpleName.GetLeadingTrivia(), newName, simpleName.GetTrailingTrivia()))); }
private static Task <Document> RefactorAsync( Document document, SimpleNameSyntax simpleName, string newName, CancellationToken cancellationToken = default(CancellationToken)) { SimpleNameSyntax newNode = simpleName.WithIdentifier(Identifier(newName).WithTriviaFrom(simpleName.Identifier)); return(document.ReplaceNodeAsync(simpleName, newNode, cancellationToken)); }
private CodeFixRegistrationResult ReplaceCountWithLengthOrViceVersa( CodeFixContext context, Diagnostic diagnostic, ExpressionSyntax expression, SimpleNameSyntax simpleName, SemanticModel semanticModel) { string name = simpleName.Identifier.ValueText; string newName = GetNewName(); if (newName != null) { ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(expression, context.CancellationToken); if (typeSymbol != null) { if (typeSymbol is IArrayTypeSymbol arrayType) { typeSymbol = arrayType.ElementType; } foreach (ISymbol symbol in typeSymbol.GetMembers(newName)) { if (!symbol.IsStatic && symbol.Kind == SymbolKind.Property) { var propertySymbol = (IPropertySymbol)symbol; if (!propertySymbol.IsIndexer && propertySymbol.IsReadOnly && semanticModel.IsAccessible(expression.SpanStart, symbol)) { Document document = context.Document; CodeAction codeAction = CodeAction.Create( $"Use '{newName}' instead of '{name}'", ct => { SimpleNameSyntax newNode = simpleName.WithIdentifier(Identifier(newName).WithTriviaFrom(simpleName.Identifier)); return(document.ReplaceNodeAsync(simpleName, newNode, ct)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); return(new CodeFixRegistrationResult(true)); } } } } } return(default);
private (ExpressionSyntax, SimpleNameSyntax) BuildNewSyntax(string newName, SimpleNameSyntax access) { var nameParts = newName.Split('.'); Trace.Assert(nameParts.Length > 1); if (nameParts.Length == 2) { return(IdentifierName(nameParts[0]), access.WithIdentifier(Identifier(nameParts[1]))); } var first = nameParts.First(); // copy all but the first and last elements var middle = new string[nameParts.Length - 2]; Array.Copy(nameParts, 1, middle, 0, middle.Length); var last = nameParts.Last(); return(SyntaxFactoryUtils.MemberAccess(IdentifierName(first), middle), access.WithIdentifier(Identifier(last))); }
private TypeSyntax Type0(Typ typ) { string namespaceAlias; if (!RequireFullyQualifiedTyp(typ) && $"{Namespace}.".StartsWith($"{typ.Namespace}.")) { // Within the current namespace and not required to force a fully-qualified name; no import required. namespaceAlias = null; } else if (!_namespaceAliases.TryGetValue(typ.Namespace, out namespaceAlias)) { // A namespace that hasn't been seen before. Create an alias for it. if (!_wellKnownNamespaceAliases.TryGetValue(typ.Namespace, out var rawAlias)) { // If it's not a well-known namespace (e.g. "System"), then create an alias // using the first character of each namespace part. // TODO: Ensure single-character aliased are not generated; they cause a compilation error. rawAlias = typ.Namespace .Split('.', StringSplitOptions.RemoveEmptyEntries) .Select(x => char.ToLowerInvariant(x[0])) .Aggregate("", (a, c) => a + c); } // Make sure all aliases are unique by adding a numeric suffix if necessary. int index = 0; namespaceAlias = rawAlias; while (_namespaceAliasesOnly.Contains(namespaceAlias)) { namespaceAlias = $"{rawAlias}{++index}"; } // Record the alias for later. _namespaceAliases.Add(typ.Namespace, namespaceAlias); _namespaceAliasesOnly.Add(namespaceAlias); } SimpleNameSyntax result = IdentifierName(typ.Name); if (typ.IsDeprecated) { result = result.WithIdentifier(result.Identifier.WithPragmaWarning(PragmaWarnings.Obsolete)); } if (typ.GenericArgTyps != null) { // Generic typ, so return a generic name by recursively calling this method on all type args. result = GenericName(result.Identifier, TypeArgumentList(SeparatedList(typ.GenericArgTyps.Select(Type)))); } // Return the final TypeSyntax, aliased or not as required. return(namespaceAlias == null ? (TypeSyntax)result : AliasQualifiedName(namespaceAlias, result)); }
protected override async Task <Solution> GetChangedSolutionAsync(CancellationToken cancellationToken) { var document = this.document; var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); // Find the synchronously blocking call member, // and bookmark it so we can find it again after some mutations have taken place. var syncAccessBookmark = new SyntaxAnnotation(); SimpleNameSyntax syncMethodName = (SimpleNameSyntax)root.FindNode(this.diagnostic.Location.SourceSpan); if (syncMethodName == null) { var syncMemberAccess = root.FindNode(this.diagnostic.Location.SourceSpan).FirstAncestorOrSelf <MemberAccessExpressionSyntax>(); syncMethodName = syncMemberAccess.Name; } // When we give the Document a modified SyntaxRoot, yet another is created. So we first assign it to the Document, // then we query for the SyntaxRoot from the Document. document = document.WithSyntaxRoot( root.ReplaceNode(syncMethodName, syncMethodName.WithAdditionalAnnotations(syncAccessBookmark))); root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); syncMethodName = (SimpleNameSyntax)root.GetAnnotatedNodes(syncAccessBookmark).Single(); // We'll need the semantic model later. But because we've annotated a node, that changes the SyntaxRoot // and that renders the default semantic model broken (even though we've already updated the document's SyntaxRoot?!). // So after acquiring the semantic model, update it with the new method body. var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var originalAnonymousMethodContainerIfApplicable = syncMethodName.FirstAncestorOrSelf <AnonymousFunctionExpressionSyntax>(); var originalMethodDeclaration = syncMethodName.FirstAncestorOrSelf <MethodDeclarationSyntax>(); // Ensure that the method or anonymous delegate is using the async keyword. MethodDeclarationSyntax updatedMethod; if (originalAnonymousMethodContainerIfApplicable != null) { updatedMethod = originalMethodDeclaration.ReplaceNode( originalAnonymousMethodContainerIfApplicable, originalAnonymousMethodContainerIfApplicable.MakeMethodAsync(semanticModel, cancellationToken)); } else { (document, updatedMethod) = await originalMethodDeclaration.MakeMethodAsync(document, cancellationToken).ConfigureAwait(false); semanticModel = null; // out-dated } if (updatedMethod != originalMethodDeclaration) { // Re-discover our synchronously blocking member. syncMethodName = (SimpleNameSyntax)updatedMethod.GetAnnotatedNodes(syncAccessBookmark).Single(); } var syncExpression = (ExpressionSyntax)syncMethodName.FirstAncestorOrSelf <InvocationExpressionSyntax>() ?? syncMethodName.FirstAncestorOrSelf <MemberAccessExpressionSyntax>(); ExpressionSyntax awaitExpression; if (this.AlternativeAsyncMethod != string.Empty) { // Replace the member being called and await the invocation expression. // While doing so, move leading trivia to the surrounding await expression. var asyncMethodName = syncMethodName.WithIdentifier(SyntaxFactory.Identifier(this.diagnostic.Properties[AsyncMethodKeyName])); awaitExpression = SyntaxFactory.AwaitExpression( syncExpression.ReplaceNode(syncMethodName, asyncMethodName).WithoutLeadingTrivia()) .WithLeadingTrivia(syncExpression.GetLeadingTrivia()); if (!(syncExpression.Parent is ExpressionStatementSyntax)) { awaitExpression = SyntaxFactory.ParenthesizedExpression(awaitExpression) .WithAdditionalAnnotations(Simplifier.Annotation); } } else { // Remove the member being accessed that causes a synchronous block and simply await the object. var syncMemberAccess = syncMethodName.FirstAncestorOrSelf <MemberAccessExpressionSyntax>(); var syncMemberStrippedExpression = syncMemberAccess.Expression; // Special case a common pattern of calling task.GetAwaiter().GetResult() and remove both method calls. var expressionMethodCall = (syncMemberStrippedExpression as InvocationExpressionSyntax)?.Expression as MemberAccessExpressionSyntax; if (expressionMethodCall?.Name.Identifier.Text == nameof(Task.GetAwaiter)) { syncMemberStrippedExpression = expressionMethodCall.Expression; } awaitExpression = SyntaxFactory.AwaitExpression(syncMemberStrippedExpression.WithoutLeadingTrivia()) .WithLeadingTrivia(syncMemberStrippedExpression.GetLeadingTrivia()); } updatedMethod = updatedMethod .ReplaceNode(syncExpression, awaitExpression); var newRoot = root.ReplaceNode(originalMethodDeclaration, updatedMethod); var newDocument = document.WithSyntaxRoot(newRoot); return(newDocument.Project.Solution); }
private async Task ComputeCodeFixAsync( CodeFixContext context, Diagnostic diagnostic, ExpressionSyntax expression, SimpleNameSyntax simpleName) { string name = simpleName.Identifier.ValueText; if (name != "Count" && name != "Length") { return; } string newName = (name == "Count") ? "Length" : "Count"; SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); if (!IsFixable()) { return; } CodeAction codeAction = CodeAction.Create( $"Use '{newName}' instead of '{name}'", cancellationToken => { SimpleNameSyntax newNode = simpleName.WithIdentifier(Identifier(newName).WithTriviaFrom(simpleName.Identifier)); return(context.Document.ReplaceNodeAsync(simpleName, newNode, cancellationToken)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); bool IsFixable() { ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(expression, context.CancellationToken); if (typeSymbol != null) { if (typeSymbol is IArrayTypeSymbol arrayType) { typeSymbol = arrayType.ElementType; } foreach (ISymbol symbol in typeSymbol.GetMembers(newName)) { if (!symbol.IsStatic && symbol.Kind == SymbolKind.Property) { var propertySymbol = (IPropertySymbol)symbol; if (!propertySymbol.IsIndexer && propertySymbol.IsReadOnly && semanticModel.IsAccessible(expression.SpanStart, symbol)) { return(true); } } } } return(false); } }