protected override bool TryInitializeState( SemanticDocument document, SimpleNameSyntax simpleName, CancellationToken cancellationToken, out GenerateTypeServiceStateOptions generateTypeServiceStateOptions) { generateTypeServiceStateOptions = new GenerateTypeServiceStateOptions(); if (simpleName.IsNullWithNoType()) { return(false); } if (SyntaxFacts.IsAliasQualifier(simpleName)) { return(false); } // Never offer if we're in a using directive, unless its a static using. The feeling here is that it's highly // unlikely that this would be a location where a user would be wanting to generate // something. They're really just trying to reference something that exists but // isn't available for some reason (i.e. a missing reference). var usingDirectiveSyntax = simpleName.GetAncestorOrThis <ImportDirectiveSyntax>(); if (usingDirectiveSyntax != null && usingDirectiveSyntax.StaticKeyword.Kind() != SyntaxKind.StaticKeyword) { return(false); } ExpressionSyntax nameOrMemberAccessExpression = null; if (simpleName.IsRightSideOfDot()) { // This simplename comes from the cref if (simpleName.IsParentKind(SyntaxKind.NameMemberCref)) { return(false); } nameOrMemberAccessExpression = generateTypeServiceStateOptions.NameOrMemberAccessExpression = (ExpressionSyntax)simpleName.Parent; // If we're on the right side of a dot, then the left side better be a name (and // not an arbitrary expression). var leftSideExpression = simpleName.GetLeftSideOfDot(); if (!leftSideExpression.IsKind( SyntaxKind.QualifiedName, SyntaxKind.IdentifierName, SyntaxKind.AliasQualifiedName, SyntaxKind.GenericName, SyntaxKind.SimpleMemberAccessExpression)) { return(false); } } else { nameOrMemberAccessExpression = generateTypeServiceStateOptions.NameOrMemberAccessExpression = simpleName; } // BUG(5712): Don't offer generate type in an enum's base list. if (nameOrMemberAccessExpression.Parent is BaseTypeSyntax && nameOrMemberAccessExpression.Parent.IsParentKind(SyntaxKind.ImplementList) && ((BaseTypeSyntax)nameOrMemberAccessExpression.Parent).Type == nameOrMemberAccessExpression && nameOrMemberAccessExpression.Parent.Parent.IsParentKind(SyntaxKind.EnumDeclaration)) { return(false); } // If we can guarantee it's a type only context, great. Otherwise, we may not want to // provide this here. var semanticModel = document.SemanticModel; if (!SyntaxFacts.IsInNamespaceOrTypeContext(nameOrMemberAccessExpression)) { // Don't offer Generate Type in an expression context *unless* we're on the left // side of a dot. In that case the user might be making a type that they're // accessing a static off of. var syntaxTree = semanticModel.SyntaxTree; var start = nameOrMemberAccessExpression.SpanStart; var tokenOnLeftOfStart = syntaxTree.FindTokenOnLeftOfPosition(start, cancellationToken); var isExpressionContext = syntaxTree.IsExpressionContext(start, tokenOnLeftOfStart, attributes: true, cancellationToken: cancellationToken, semanticModelOpt: semanticModel); var isStatementContext = syntaxTree.IsStatementContext(start, tokenOnLeftOfStart, cancellationToken); var isExpressionOrStatementContext = isExpressionContext || isStatementContext; // Delegate Type Creation is not allowed in Non Type Namespace Context generateTypeServiceStateOptions.IsDelegateAllowed = false; if (!isExpressionOrStatementContext) { return(false); } if (!simpleName.IsLeftSideOfDot() && !simpleName.IsInsideNameOfExpression(semanticModel, cancellationToken)) { if (nameOrMemberAccessExpression == null || !nameOrMemberAccessExpression.IsKind(SyntaxKind.SimpleMemberAccessExpression) || !simpleName.IsRightSideOfDot()) { return(false); } var leftSymbol = semanticModel.GetSymbolInfo(((MemberAccessExpressionSyntax)nameOrMemberAccessExpression).Expression, cancellationToken).Symbol; var token = simpleName.GetLastToken().GetNextToken(); // We let only the Namespace to be left of the Dot if (leftSymbol == null || !leftSymbol.IsKind(SymbolKind.Namespace) || !token.IsKind(SyntaxKind.DotToken)) { return(false); } else { generateTypeServiceStateOptions.IsMembersWithModule = true; generateTypeServiceStateOptions.IsTypeGeneratedIntoNamespaceFromMemberAccess = true; } } // Global Namespace if (!generateTypeServiceStateOptions.IsTypeGeneratedIntoNamespaceFromMemberAccess && !SyntaxFacts.IsInNamespaceOrTypeContext(simpleName)) { var token = simpleName.GetLastToken().GetNextToken(); if (token.IsKind(SyntaxKind.DotToken) && simpleName.Parent == token.Parent) { generateTypeServiceStateOptions.IsMembersWithModule = true; generateTypeServiceStateOptions.IsTypeGeneratedIntoNamespaceFromMemberAccess = true; } } } var fieldDeclaration = simpleName.GetAncestor <FieldDeclarationSyntax>(); if (fieldDeclaration != null && fieldDeclaration.Parent is CompilationUnitSyntax && document.Document.SourceCodeKind == SourceCodeKind.Regular) { return(false); } // Check to see if Module could be an option in the Type Generation in Cross Language Generation var nextToken = simpleName.GetLastToken().GetNextToken(); if (simpleName.IsLeftSideOfDot() || nextToken.IsKind(SyntaxKind.DotToken)) { if (simpleName.IsRightSideOfDot()) { if (simpleName.Parent is QualifiedNameSyntax parent) { var leftSymbol = semanticModel.GetSymbolInfo(parent.Left, cancellationToken).Symbol; if (leftSymbol != null && leftSymbol.IsKind(SymbolKind.Namespace)) { generateTypeServiceStateOptions.IsMembersWithModule = true; } } } } if (SyntaxFacts.IsInNamespaceOrTypeContext(nameOrMemberAccessExpression)) { if (nextToken.IsKind(SyntaxKind.DotToken)) { // In Namespace or Type Context we cannot have Interface, Enum, Delegate as part of the Left Expression of a QualifiedName generateTypeServiceStateOptions.IsDelegateAllowed = false; generateTypeServiceStateOptions.IsInterfaceOrEnumNotAllowedInTypeContext = true; generateTypeServiceStateOptions.IsMembersWithModule = true; } // case: class Goo<T> where T: MyType if (nameOrMemberAccessExpression.GetAncestors <TypeConstraintSyntax>().Any()) { generateTypeServiceStateOptions.IsClassInterfaceTypes = true; return(true); } // Events if (nameOrMemberAccessExpression.GetAncestors <EventFieldDeclarationSyntax>().Any() || nameOrMemberAccessExpression.GetAncestors <EventDeclarationSyntax>().Any()) { // Case : event goo name11 // Only Delegate if (simpleName.Parent != null && !(simpleName.Parent is QualifiedNameSyntax)) { generateTypeServiceStateOptions.IsDelegateOnly = true; return(true); } // Case : event SomeSymbol.goo name11 if (nameOrMemberAccessExpression is QualifiedNameSyntax) { // Only Namespace, Class, Struct and Module are allowed to contain Delegate // Case : event Something.Mytype.<Delegate> Identifier if (nextToken.IsKind(SyntaxKind.DotToken)) { if (nameOrMemberAccessExpression.Parent != null && nameOrMemberAccessExpression.Parent is QualifiedNameSyntax) { return(true); } else { Contract.Fail("Cannot reach this point"); } } else { // Case : event Something.<Delegate> Identifier generateTypeServiceStateOptions.IsDelegateOnly = true; return(true); } } } } else { // MemberAccessExpression if ((nameOrMemberAccessExpression.IsKind(SyntaxKind.SimpleMemberAccessExpression) || (nameOrMemberAccessExpression.Parent != null && nameOrMemberAccessExpression.IsParentKind(SyntaxKind.SimpleMemberAccessExpression))) && nameOrMemberAccessExpression.IsLeftSideOfDot()) { // Check to see if the expression is part of Invocation Expression ExpressionSyntax outerMostMemberAccessExpression = null; if (nameOrMemberAccessExpression.IsKind(SyntaxKind.SimpleMemberAccessExpression)) { outerMostMemberAccessExpression = nameOrMemberAccessExpression; } else { Debug.Assert(nameOrMemberAccessExpression.IsParentKind(SyntaxKind.SimpleMemberAccessExpression)); outerMostMemberAccessExpression = (ExpressionSyntax)nameOrMemberAccessExpression.Parent; } outerMostMemberAccessExpression = outerMostMemberAccessExpression.GetAncestorsOrThis <ExpressionSyntax>().SkipWhile(n => n != null && n.IsKind(SyntaxKind.SimpleMemberAccessExpression)).FirstOrDefault(); if (outerMostMemberAccessExpression != null && outerMostMemberAccessExpression is InvocationExpressionSyntax) { generateTypeServiceStateOptions.IsEnumNotAllowed = true; } } } // Cases: // // 1 - Function Address // var s2 = new MyD2(goo); // // 2 - Delegate // MyD1 d = null; // var s1 = new MyD2(d); // // 3 - Action // Action action1 = null; // var s3 = new MyD2(action1); // // 4 - Func // Func<int> lambda = () => { return 0; }; // var s4 = new MyD3(lambda); if (nameOrMemberAccessExpression.Parent is ObjectCreationExpressionSyntax) { var objectCreationExpressionOpt = generateTypeServiceStateOptions.ObjectCreationExpressionOpt = (ObjectCreationExpressionSyntax)nameOrMemberAccessExpression.Parent; // Enum and Interface not Allowed in Object Creation Expression generateTypeServiceStateOptions.IsInterfaceOrEnumNotAllowedInTypeContext = true; if (objectCreationExpressionOpt.ArgumentList != null) { if (objectCreationExpressionOpt.ArgumentList.CloseParenToken.IsMissing) { return(false); } // Get the Method symbol for the Delegate to be created if (generateTypeServiceStateOptions.IsDelegateAllowed && objectCreationExpressionOpt.ArgumentList.Arguments.Count == 1 && objectCreationExpressionOpt.ArgumentList.Arguments[0].Expression.Kind() != SyntaxKind.DeclarationExpression) { generateTypeServiceStateOptions.DelegateCreationMethodSymbol = GetMethodSymbolIfPresent(semanticModel, objectCreationExpressionOpt.ArgumentList.Arguments[0].Expression, cancellationToken); } else { generateTypeServiceStateOptions.IsDelegateAllowed = false; } } if (objectCreationExpressionOpt.Initializer != null) { foreach (var expression in objectCreationExpressionOpt.Initializer.Expressions) { var simpleAssignmentExpression = expression as AssignmentExpressionSyntax; if (simpleAssignmentExpression == null) { continue; } var name = simpleAssignmentExpression.Left as SimpleNameSyntax; if (name == null) { continue; } generateTypeServiceStateOptions.PropertiesToGenerate.Add(name); } } } if (generateTypeServiceStateOptions.IsDelegateAllowed) { // MyD1 z1 = goo; if (nameOrMemberAccessExpression.Parent.IsKind(SyntaxKind.VariableDeclaration)) { var variableDeclaration = (VariableDeclarationSyntax)nameOrMemberAccessExpression.Parent; var firstVarDeclWithInitializer = variableDeclaration.Initializer != null && variableDeclaration.Initializer.Value != null ? variableDeclaration : null; if (firstVarDeclWithInitializer != null && firstVarDeclWithInitializer.Initializer != null && firstVarDeclWithInitializer.Initializer.Value != null) { generateTypeServiceStateOptions.DelegateCreationMethodSymbol = GetMethodSymbolIfPresent(semanticModel, firstVarDeclWithInitializer.Initializer.Value, cancellationToken); } } // var w1 = (MyD1)goo; if (nameOrMemberAccessExpression.Parent.IsKind(SyntaxKind.CastExpression)) { var castExpression = (CastExpressionSyntax)nameOrMemberAccessExpression.Parent; if (castExpression.Expression != null) { generateTypeServiceStateOptions.DelegateCreationMethodSymbol = GetMethodSymbolIfPresent(semanticModel, castExpression.Expression, cancellationToken); } } } return(true); }
private ExpressionSyntax VisitSimpleName(SimpleNameSyntax rewrittenSimpleName, SimpleNameSyntax originalSimpleName) { _cancellationToken.ThrowIfCancellationRequested(); // if this is "var", then do not process further if (originalSimpleName.IsVar) { return(rewrittenSimpleName); } var identifier = rewrittenSimpleName.Identifier; ExpressionSyntax newNode = rewrittenSimpleName; var isInsideCref = originalSimpleName.AncestorsAndSelf(ascendOutOfTrivia: true).Any(n => n is CrefSyntax); //// //// 1. if this identifier is an alias, we'll expand it here and replace the node completely. //// if (!SyntaxFacts.IsAliasQualifier(originalSimpleName)) { var aliasInfo = _semanticModel.GetAliasInfo(originalSimpleName, _cancellationToken); if (aliasInfo != null) { var aliasTarget = aliasInfo.Target; if (aliasTarget.IsNamespace() && ((INamespaceSymbol)aliasTarget).IsGlobalNamespace) { return(rewrittenSimpleName); } // if the enclosing expression is a typeof expression that already contains open type we cannot // we need to insert an open type as well. var typeOfExpression = originalSimpleName.GetAncestor <TypeOfExpressionSyntax>(); if (typeOfExpression != null && IsTypeOfUnboundGenericType(_semanticModel, typeOfExpression)) { aliasTarget = ((INamedTypeSymbol)aliasTarget).ConstructUnboundGenericType(); } // the expanded form replaces the current identifier name. var replacement = FullyQualifyIdentifierName( aliasTarget, newNode, originalSimpleName, replaceNode: true, isInsideCref: isInsideCref, omitLeftHandSide: false) .WithAdditionalAnnotations(Simplifier.Annotation); // We replace the simple name completely, so we can't continue and rename the token // with a RenameLocationAnnotation. // There's also no way of removing annotations, so we just add a DoNotRenameAnnotation. if (replacement.Kind() == SyntaxKind.AliasQualifiedName) { var qualifiedReplacement = (AliasQualifiedNameSyntax)replacement; var newIdentifier = identifier.CopyAnnotationsTo(qualifiedReplacement.Name.Identifier); if (_annotationForReplacedAliasIdentifier != null) { newIdentifier = newIdentifier.WithAdditionalAnnotations(_annotationForReplacedAliasIdentifier); } var aliasAnnotationInfo = AliasAnnotation.Create(aliasInfo.Name); newIdentifier = newIdentifier.WithAdditionalAnnotations(aliasAnnotationInfo); replacement = replacement.ReplaceNode( qualifiedReplacement.Name, qualifiedReplacement.Name.WithIdentifier(newIdentifier)); replacement = newNode.CopyAnnotationsTo(replacement); var firstReplacementToken = replacement.GetFirstToken(true, false, true, true); var firstOriginalToken = originalSimpleName.GetFirstToken(true, false, true, true); SyntaxToken tokenWithLeadingWhitespace; if (TryAddLeadingElasticTriviaIfNecessary(firstReplacementToken, firstOriginalToken, out tokenWithLeadingWhitespace)) { replacement = replacement.ReplaceToken(firstOriginalToken, tokenWithLeadingWhitespace); } replacement = AppendElasticTriviaIfNecessary(replacement, originalSimpleName); return(replacement); } if (replacement.Kind() == SyntaxKind.QualifiedName) { var qualifiedReplacement = (QualifiedNameSyntax)replacement; var newIdentifier = identifier.CopyAnnotationsTo(qualifiedReplacement.Right.Identifier); if (_annotationForReplacedAliasIdentifier != null) { newIdentifier = newIdentifier.WithAdditionalAnnotations(_annotationForReplacedAliasIdentifier); } var aliasAnnotationInfo = AliasAnnotation.Create(aliasInfo.Name); newIdentifier = newIdentifier.WithAdditionalAnnotations(aliasAnnotationInfo); replacement = replacement.ReplaceNode( qualifiedReplacement.Right, qualifiedReplacement.Right.WithIdentifier(newIdentifier)); replacement = newNode.CopyAnnotationsTo(replacement); replacement = AppendElasticTriviaIfNecessary(replacement, originalSimpleName); return(replacement); } throw new NotImplementedException(); } } var symbol = _semanticModel.GetSymbolInfo(originalSimpleName.Identifier).Symbol; if (symbol == null) { return(newNode); } var typeArgumentSymbols = TypeArgumentSymbolsPresentInName(originalSimpleName); var omitLeftSideOfExpression = false; // Check to see if the type Arguments in the resultant Symbol is recursively defined. if (IsTypeArgumentDefinedRecursive(symbol, typeArgumentSymbols, enterContainingSymbol: true)) { if (symbol.ContainingSymbol.Equals(symbol.OriginalDefinition.ContainingSymbol) && symbol.Kind == SymbolKind.Method && ((IMethodSymbol)symbol).IsStatic) { if (IsTypeArgumentDefinedRecursive(symbol, typeArgumentSymbols, enterContainingSymbol: false)) { return(newNode); } else { omitLeftSideOfExpression = true; } } else { return(newNode); } } if (IsInvocationWithDynamicArguments(originalSimpleName, _semanticModel)) { return(newNode); } //// //// 2. If it's an attribute, make sure the identifier matches the attribute's class name. //// if (originalSimpleName.GetAncestor <AttributeSyntax>() != null) { if (symbol.IsConstructor() && symbol.ContainingType?.IsAttribute() == true) { symbol = symbol.ContainingType; var name = symbol.Name; Debug.Assert(name.StartsWith(originalSimpleName.Identifier.ValueText, StringComparison.Ordinal)); // if the user already used the Attribute suffix in the attribute, we'll maintain it. if (identifier.ValueText == name && name.EndsWith("Attribute", StringComparison.Ordinal)) { identifier = identifier.WithAdditionalAnnotations(SimplificationHelpers.DontSimplifyAnnotation); } identifier = identifier.CopyAnnotationsTo(SyntaxFactory.VerbatimIdentifier(identifier.LeadingTrivia, name, name, identifier.TrailingTrivia)); } } //// //// 3. Always try to escape keyword identifiers //// identifier = TryEscapeIdentifierToken(identifier, originalSimpleName, _semanticModel).WithAdditionalAnnotations(Simplifier.Annotation); if (identifier != rewrittenSimpleName.Identifier) { switch (newNode.Kind()) { case SyntaxKind.IdentifierName: case SyntaxKind.GenericName: newNode = ((SimpleNameSyntax)newNode).WithIdentifier(identifier); break; default: throw new NotImplementedException(); } } var parent = originalSimpleName.Parent; // do not complexify further for location where only simple names are allowed if (parent is MemberDeclarationSyntax || parent is MemberBindingExpressionSyntax || originalSimpleName.GetAncestor <NameEqualsSyntax>() != null || (parent is MemberAccessExpressionSyntax && parent.Kind() != SyntaxKind.SimpleMemberAccessExpression) || ((parent.Kind() == SyntaxKind.SimpleMemberAccessExpression || parent.Kind() == SyntaxKind.NameMemberCref) && originalSimpleName.IsRightSideOfDot()) || (parent.Kind() == SyntaxKind.QualifiedName && originalSimpleName.IsRightSideOfQualifiedName()) || (parent.Kind() == SyntaxKind.AliasQualifiedName)) { return(TryAddTypeArgumentToIdentifierName(newNode, symbol)); } //// //// 4. If this is a standalone identifier or the left side of a qualified name or member access try to fully qualify it //// // we need to treat the constructor as type name, so just get the containing type. if (symbol.IsConstructor() && (parent.Kind() == SyntaxKind.ObjectCreationExpression || parent.Kind() == SyntaxKind.NameMemberCref)) { symbol = symbol.ContainingType; } // if it's a namespace or type name, fully qualify it. if (symbol.Kind == SymbolKind.NamedType || symbol.Kind == SymbolKind.Namespace) { var replacement = FullyQualifyIdentifierName( (INamespaceOrTypeSymbol)symbol, newNode, originalSimpleName, replaceNode: false, isInsideCref: isInsideCref, omitLeftHandSide: omitLeftSideOfExpression) .WithAdditionalAnnotations(Simplifier.Annotation); replacement = AppendElasticTriviaIfNecessary(replacement, originalSimpleName); return(replacement); } // if it's a member access, we're fully qualifying the left side and make it a member access. if (symbol.Kind == SymbolKind.Method || symbol.Kind == SymbolKind.Field || symbol.Kind == SymbolKind.Property) { if (symbol.IsStatic || originalSimpleName.IsParentKind(SyntaxKind.NameMemberCref) || _semanticModel.SyntaxTree.IsNameOfContext(originalSimpleName.SpanStart, _semanticModel, _cancellationToken)) { newNode = FullyQualifyIdentifierName( symbol, newNode, originalSimpleName, replaceNode: false, isInsideCref: isInsideCref, omitLeftHandSide: omitLeftSideOfExpression); } else { if (!IsPropertyNameOfObjectInitializer(originalSimpleName)) { ExpressionSyntax left; // Assumption here is, if the enclosing and containing types are different then there is Inheritence relationship if (_semanticModel.GetEnclosingNamedType(originalSimpleName.SpanStart, _cancellationToken) != symbol.ContainingType) { left = SyntaxFactory.BaseExpression(); } else { left = SyntaxFactory.ThisExpression(); } var identifiersLeadingTrivia = newNode.GetLeadingTrivia(); newNode = TryAddTypeArgumentToIdentifierName(newNode, symbol); newNode = newNode.CopyAnnotationsTo( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, left, (SimpleNameSyntax)newNode.WithLeadingTrivia(null)) .WithLeadingTrivia(identifiersLeadingTrivia)); } } } var result = newNode.WithAdditionalAnnotations(Simplifier.Annotation); result = AppendElasticTriviaIfNecessary(result, originalSimpleName); return(result); }