private static bool TryReduce( this NameSyntax name, SemanticModel semanticModel, out TypeSyntax replacementNode, out TextSpan issueSpan, OptionSet optionSet, CancellationToken cancellationToken) { replacementNode = null; issueSpan = default(TextSpan); if (name.IsVar) { return false; } // we should not simplify a name of a namespace declaration if (IsPartOfNamespaceDeclarationName(name)) { return false; } // We can simplify Qualified names and AliasQualifiedNames. Generally, if we have // something like "A.B.C.D", we only consider the full thing something we can simplify. // However, in the case of "A.B.C<>.D", then we'll only consider simplifying up to the // first open name. This is because if we remove the open name, we'll often change // meaning as "D" will bind to C<T>.D which is different than C<>.D! if (name is QualifiedNameSyntax) { var left = ((QualifiedNameSyntax)name).Left; if (ContainsOpenName(left)) { // Don't simplify A.B<>.C return false; } } // 1. see whether binding the name binds to a symbol/type. if not, it is ambiguous and // nothing we can do here. var symbol = SimplificationHelpers.GetOriginalSymbolInfo(semanticModel, name); if (symbol == null) { return false; } // treat constructor names as types var method = symbol as IMethodSymbol; if (method.IsConstructor()) { symbol = method.ContainingType; } if (symbol.Kind == SymbolKind.Method && name.Kind() == SyntaxKind.GenericName) { // The option wants the generic method invocation name to be explicit, then quit the reduction if (!optionSet.GetOption(SimplificationOptions.PreferImplicitTypeInference)) { return false; } var genericName = (GenericNameSyntax)name; replacementNode = SyntaxFactory.IdentifierName(genericName.Identifier) .WithLeadingTrivia(genericName.GetLeadingTrivia()) .WithTrailingTrivia(genericName.GetTrailingTrivia()); issueSpan = genericName.TypeArgumentList.Span; return name.CanReplaceWithReducedName(replacementNode, semanticModel, cancellationToken); } if (!(symbol is INamespaceOrTypeSymbol)) { return false; } if (name.HasAnnotations(SpecialTypeAnnotation.Kind)) { replacementNode = SyntaxFactory.PredefinedType( SyntaxFactory.Token( name.GetLeadingTrivia(), GetPredefinedKeywordKind(SpecialTypeAnnotation.GetSpecialType(name.GetAnnotations(SpecialTypeAnnotation.Kind).First())), name.GetTrailingTrivia())); issueSpan = name.Span; return name.CanReplaceWithReducedNameInContext(replacementNode, semanticModel, cancellationToken); } else { if (!name.IsRightSideOfDotOrColonColon()) { IAliasSymbol aliasReplacement; if (name.TryReplaceWithAlias(semanticModel, optionSet.GetOption(SimplificationOptions.PreferAliasToQualification), cancellationToken, out aliasReplacement)) { // get the token text as it appears in source code to preserve e.g. unicode character escaping var text = aliasReplacement.Name; var syntaxRef = aliasReplacement.DeclaringSyntaxReferences.FirstOrDefault(); if (syntaxRef != null) { var declIdentifier = ((UsingDirectiveSyntax)syntaxRef.GetSyntax(cancellationToken)).Alias.Name.Identifier; text = declIdentifier.IsVerbatimIdentifier() ? declIdentifier.ToString().Substring(1) : declIdentifier.ToString(); } var identifierToken = SyntaxFactory.Identifier( name.GetLeadingTrivia(), SyntaxKind.IdentifierToken, text, aliasReplacement.Name, name.GetTrailingTrivia()); identifierToken = CSharpSimplificationService.TryEscapeIdentifierToken(identifierToken, name, semanticModel); replacementNode = SyntaxFactory.IdentifierName(identifierToken); // Merge annotation to new syntax node var annotatedNodesOrTokens = name.GetAnnotatedNodesAndTokens(RenameAnnotation.Kind); foreach (var annotatedNodeOrToken in annotatedNodesOrTokens) { if (annotatedNodeOrToken.IsToken) { identifierToken = annotatedNodeOrToken.AsToken().CopyAnnotationsTo(identifierToken); } else { replacementNode = annotatedNodeOrToken.AsNode().CopyAnnotationsTo(replacementNode); } } annotatedNodesOrTokens = name.GetAnnotatedNodesAndTokens(AliasAnnotation.Kind); foreach (var annotatedNodeOrToken in annotatedNodesOrTokens) { if (annotatedNodeOrToken.IsToken) { identifierToken = annotatedNodeOrToken.AsToken().CopyAnnotationsTo(identifierToken); } else { replacementNode = annotatedNodeOrToken.AsNode().CopyAnnotationsTo(replacementNode); } } replacementNode = ((SimpleNameSyntax)replacementNode).WithIdentifier(identifierToken); issueSpan = name.Span; // In case the alias name is the same as the last name of the alias target, we only include // the left part of the name in the unnecessary span to Not confuse uses. if (name.Kind() == SyntaxKind.QualifiedName) { QualifiedNameSyntax qualifiedName = (QualifiedNameSyntax)name; if (qualifiedName.Right.Identifier.ValueText == identifierToken.ValueText) { issueSpan = qualifiedName.Left.Span; } } // first check if this would be a valid reduction if (name.CanReplaceWithReducedNameInContext(replacementNode, semanticModel, cancellationToken)) { // in case this alias name ends with "Attribute", we're going to see if we can also // remove that suffix. TypeSyntax replacementNodeWithoutAttributeSuffix; TextSpan issueSpanWithoutAttributeSuffix; if (TryReduceAttributeSuffix( name, identifierToken, semanticModel, out replacementNodeWithoutAttributeSuffix, out issueSpanWithoutAttributeSuffix, cancellationToken)) { if (name.CanReplaceWithReducedName(replacementNodeWithoutAttributeSuffix, semanticModel, cancellationToken)) { replacementNode = replacementNode.CopyAnnotationsTo(replacementNodeWithoutAttributeSuffix); issueSpan = issueSpanWithoutAttributeSuffix; } } return true; } return false; } var nameHasNoAlias = false; if (name is SimpleNameSyntax) { var simpleName = (SimpleNameSyntax)name; if (!simpleName.Identifier.HasAnnotations(AliasAnnotation.Kind)) { nameHasNoAlias = true; } } if (name is QualifiedNameSyntax) { var qualifiedName = (QualifiedNameSyntax)name; if (!qualifiedName.Right.HasAnnotation(Simplifier.SpecialTypeAnnotation)) { nameHasNoAlias = true; } } if (name is AliasQualifiedNameSyntax) { var aliasQualifiedName = (AliasQualifiedNameSyntax)name; if (aliasQualifiedName.Name is SimpleNameSyntax && !aliasQualifiedName.Name.Identifier.HasAnnotations(AliasAnnotation.Kind) && !aliasQualifiedName.Name.HasAnnotation(Simplifier.SpecialTypeAnnotation)) { nameHasNoAlias = true; } } var aliasInfo = semanticModel.GetAliasInfo(name, cancellationToken); if (nameHasNoAlias && aliasInfo == null) { if (IsReplaceableByVar(name, semanticModel, out replacementNode, out issueSpan, optionSet, cancellationToken)) { return true; } // Don't simplify to predefined type if name is part of a QualifiedName. // QualifiedNames can't contain PredefinedTypeNames (although MemberAccessExpressions can). // In other words, the left side of a QualifiedName can't be a PredefinedTypeName. if (!name.Parent.IsKind(SyntaxKind.QualifiedName) && (PreferPredefinedTypeKeywordInDeclarations(name, optionSet, semanticModel) || PreferPredefinedTypeKeywordInMemberAccess(name, optionSet, semanticModel))) { var type = semanticModel.GetTypeInfo(name, cancellationToken).Type; if (type != null) { var keywordKind = GetPredefinedKeywordKind(type.SpecialType); if (keywordKind != SyntaxKind.None) { return CanReplaceWithPredefinedTypeKeywordInContext(name, semanticModel, out replacementNode, ref issueSpan, keywordKind, cancellationToken); } } else { var typeSymbol = semanticModel.GetSymbolInfo(name, cancellationToken).Symbol; if (typeSymbol.IsKind(SymbolKind.NamedType)) { var keywordKind = GetPredefinedKeywordKind(((INamedTypeSymbol)typeSymbol).SpecialType); if (keywordKind != SyntaxKind.None) { return CanReplaceWithPredefinedTypeKeywordInContext(name, semanticModel, out replacementNode, ref issueSpan, keywordKind, cancellationToken); } } } } } // nullable rewrite: Nullable<int> -> int? // Don't rewrite in the case where Nullable<int> is part of some qualified name like Nullable<int>.Something if (!name.IsVar && (symbol.Kind == SymbolKind.NamedType) && !name.IsLeftSideOfQualifiedName()) { var type = (INamedTypeSymbol)symbol; if (aliasInfo == null && CanSimplifyNullable(type, name, semanticModel)) { GenericNameSyntax genericName; if (name.Kind() == SyntaxKind.QualifiedName) { genericName = (GenericNameSyntax)((QualifiedNameSyntax)name).Right; } else { genericName = (GenericNameSyntax)name; } var oldType = genericName.TypeArgumentList.Arguments.First(); if (oldType.Kind() == SyntaxKind.OmittedTypeArgument) { return false; } replacementNode = SyntaxFactory.NullableType(oldType) .WithLeadingTrivia(name.GetLeadingTrivia()) .WithTrailingTrivia(name.GetTrailingTrivia()); issueSpan = name.Span; // we need to simplify the whole qualified name at once, because replacing the identifier on the left in // System.Nullable<int> alone would be illegal. // If this fails we want to continue to try at least to remove the System if possible. if (name.CanReplaceWithReducedNameInContext(replacementNode, semanticModel, cancellationToken)) { return true; } } } } SyntaxToken identifier; switch (name.Kind()) { case SyntaxKind.AliasQualifiedName: var simpleName = ((AliasQualifiedNameSyntax)name).Name .WithLeadingTrivia(name.GetLeadingTrivia()); simpleName = simpleName.ReplaceToken(simpleName.Identifier, ((AliasQualifiedNameSyntax)name).Name.Identifier.CopyAnnotationsTo( simpleName.Identifier.WithLeadingTrivia( ((AliasQualifiedNameSyntax)name).Alias.Identifier.LeadingTrivia))); replacementNode = simpleName; issueSpan = ((AliasQualifiedNameSyntax)name).Alias.Span; break; case SyntaxKind.QualifiedName: replacementNode = ((QualifiedNameSyntax)name).Right.WithLeadingTrivia(name.GetLeadingTrivia()); issueSpan = ((QualifiedNameSyntax)name).Left.Span; break; case SyntaxKind.IdentifierName: identifier = ((IdentifierNameSyntax)name).Identifier; // we can try to remove the Attribute suffix if this is the attribute name TryReduceAttributeSuffix(name, identifier, semanticModel, out replacementNode, out issueSpan, cancellationToken); break; } } if (replacementNode == null) { return false; } return name.CanReplaceWithReducedName(replacementNode, semanticModel, cancellationToken); }