// Create a member model for the given declaration syntax. In certain very malformed // syntax trees, there may not be a symbol that can have a member model associated with it // (although we try to minimize such cases). In such cases, null is returned. private MemberSemanticModel CreateMemberModel(CSharpSyntaxNode node) { var outer = this.binderFactory.GetBinder(node); switch (node.Kind) { case SyntaxKind.Block: if (node.Parent != null && node.Parent.Kind == SyntaxKind.PrimaryConstructorBody) { if (node.Parent.Parent != null && (node.Parent.Parent.Kind == SyntaxKind.ClassDeclaration || node.Parent.Parent.Kind == SyntaxKind.StructDeclaration)) { var typeDecl = (TypeDeclarationSyntax)node.Parent.Parent; var constructorSymbol = (SourceMethodSymbol)GetDeclaredConstructorSymbol(typeDecl); if ((object)constructorSymbol == null) return null; // We need to share locals declared in constructor initializer with // constructor initializer member model. We want both models to use the same symbols for the locals. var localsDeclaredInInitializer = ImmutableArray<LocalSymbol>.Empty; if (typeDecl.Kind == SyntaxKind.ClassDeclaration) { var classDecl = (ClassDeclarationSyntax)typeDecl; if (classDecl.BaseList != null && classDecl.BaseList.Types.Count > 0 && classDecl.BaseList.Types[0].Kind == SyntaxKind.BaseClassWithArguments) { ArgumentListSyntax argList = ((BaseClassWithArgumentsSyntax)classDecl.BaseList.Types[0]).ArgumentList; MemberSemanticModel initializerModel = GetOrAddModel(argList); if (initializerModel != null) { localsDeclaredInInitializer = initializerModel.RootBinder.GetDeclaredLocalsForScope(argList); } } } // Locals declared in instance initializers within the current partial class declaration // should be in scope within the body. outer = AddInitializersLocalsScope(outer, typeDecl, constructorSymbol); if (!localsDeclaredInInitializer.IsDefaultOrEmpty) { outer = new SimpleLocalScopeBinder(localsDeclaredInInitializer, outer); } return MethodBodySemanticModel.Create(this.Compilation, constructorSymbol, outer, node); } return null; } else { MemberDeclarationSyntax memberDecl; AccessorDeclarationSyntax accessorDecl; if ((memberDecl = node.Parent as MemberDeclarationSyntax) != null) { var symbol = (SourceMethodSymbol)GetDeclaredSymbol(memberDecl); if ((object)symbol == null) return null; // In case of constructor, we need to share locals declared in constructor initializer with // constructor initializer member model. We want both models to use the same symbols for the locals. if (node.Parent.Kind == SyntaxKind.ConstructorDeclaration && !symbol.IsStatic) { var decl = (ConstructorDeclarationSyntax)node.Parent; if (decl.Initializer != null) { MemberSemanticModel initializerModel = GetOrAddModel(decl.Initializer); if (initializerModel != null) { var localsDeclaredInInitializer = initializerModel.RootBinder.GetDeclaredLocalsForScope(decl); if (!localsDeclaredInInitializer.IsDefaultOrEmpty) { outer = new SimpleLocalScopeBinder(localsDeclaredInInitializer, outer); } } } } return MethodBodySemanticModel.Create(this.Compilation, symbol, outer, memberDecl); } else if ((accessorDecl = node.Parent as AccessorDeclarationSyntax) != null) { var symbol = (SourceMethodSymbol)GetDeclaredSymbol(accessorDecl); if ((object)symbol == null) return null; return MethodBodySemanticModel.Create(this.Compilation, symbol, outer, accessorDecl); } else { Debug.Assert(false, "Unexpected node: " + node.Parent); return null; } } case SyntaxKind.EqualsValueClause: switch (node.Parent.Kind) { case SyntaxKind.VariableDeclarator: { var variableDecl = (VariableDeclaratorSyntax)node.Parent; SourceMemberFieldSymbol fieldSymbol = GetDeclaredFieldSymbol(variableDecl); return InitializerSemanticModel.Create( this.Compilation, variableDecl, //pass in the entire field initializer to permit region analysis. fieldSymbol, //if we're in regular C#, then insert an extra binder to perform field initialization checks GetFieldOrPropertyInitializerBinder(fieldSymbol, outer)); } case SyntaxKind.PropertyDeclaration: { var propertyDecl = (PropertyDeclarationSyntax)node.Parent; var propertySymbol = (SourcePropertySymbol)GetDeclaredSymbol(propertyDecl); return InitializerSemanticModel.Create( this.Compilation, propertyDecl, propertySymbol, GetFieldOrPropertyInitializerBinder(propertySymbol.BackingField, outer)); } case SyntaxKind.Parameter: { // NOTE: we don't need to create a member model for lambda parameter default value // (which is bad code anyway) because lambdas only appear in code with associated // member models. ParameterSyntax parameterDecl = (ParameterSyntax)node.Parent; ParameterSymbol parameterSymbol = GetDeclaredNonLambdaParameterSymbol(parameterDecl); if ((object)parameterSymbol == null) return null; return InitializerSemanticModel.Create( this.Compilation, parameterDecl, parameterSymbol, outer.CreateBinderForParameterDefaultValue(parameterSymbol, (EqualsValueClauseSyntax)node)); } case SyntaxKind.EnumMemberDeclaration: { var enumDecl = (EnumMemberDeclarationSyntax)node.Parent; var enumSymbol = (FieldSymbol)GetDeclaredSymbol(enumDecl); if ((object)enumSymbol == null) return null; return InitializerSemanticModel.Create( this.Compilation, enumDecl, enumSymbol, GetFieldOrPropertyInitializerBinder(enumSymbol, outer)); } default: Debug.Assert(false, "Unexpected node: " + node.Parent); return null; } case SyntaxKind.ArrowExpressionClause: { SourceMethodSymbol symbol = null; MemberDeclarationSyntax memberSyntax; var exprDecl = (ArrowExpressionClauseSyntax)node; if (node.Parent is BasePropertyDeclarationSyntax) { symbol = (SourceMethodSymbol)GetDeclaredSymbol(exprDecl); } else if ((memberSyntax = node.Parent as MemberDeclarationSyntax) != null) { symbol = (SourceMethodSymbol)GetDeclaredSymbol(node.Parent as MemberDeclarationSyntax); } else { // Don't throw, just use for the assert ExceptionUtilities.UnexpectedValue(node.Parent); } if ((object)symbol == null) return null; return MethodBodySemanticModel.Create( this.compilation, symbol, outer.WithContainingMemberOrLambda(symbol), exprDecl); } case SyntaxKind.GlobalStatement: { Debug.Assert(!this.IsRegularCSharp); // TODO (tomat): handle misplaced global statements if (node.Parent.Kind == SyntaxKind.CompilationUnit) { var scriptConstructor = this.Compilation.ScriptClass.InstanceConstructors.First(); return MethodBodySemanticModel.Create( this.Compilation, scriptConstructor, outer, node); } } break; case SyntaxKind.BaseConstructorInitializer: case SyntaxKind.ThisConstructorInitializer: { var constructorDecl = (ConstructorDeclarationSyntax)node.Parent; var constructorSymbol = (SourceMethodSymbol)GetDeclaredSymbol(constructorDecl); if ((object)constructorSymbol == null) return null; return InitializerSemanticModel.Create( this.Compilation, (ConstructorInitializerSyntax)node, constructorSymbol, //insert an extra binder to perform constructor initialization checks new WithConstructorInitializerLocalsBinder(outer.WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags.ConstructorInitializer, constructorSymbol), constructorDecl)); } case SyntaxKind.ArgumentList: CSharpSyntaxNode parent = node.Parent; if (parent != null && parent.Kind == SyntaxKind.BaseClassWithArguments) { var initializer = (BaseClassWithArgumentsSyntax)parent; parent = initializer.Parent; if (parent != null && parent.Kind == SyntaxKind.BaseList && initializer == ((BaseListSyntax)parent).Types[0]) { parent = parent.Parent; if (parent != null && parent.Kind == SyntaxKind.ClassDeclaration) { var classDecl = (ClassDeclarationSyntax)parent; var constructorSymbol = (SourceMethodSymbol)GetDeclaredConstructorSymbol(classDecl); if ((object)constructorSymbol == null) return null; // Locals declared in instance initializers within the current partial class declaration // should be in scope within the argument list. outer = AddInitializersLocalsScope(outer, classDecl, constructorSymbol); //insert an extra binder to perform constructor initialization checks outer = outer.WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags.ConstructorInitializer, constructorSymbol); outer = new WithConstructorInitializerLocalsBinder(outer, initializer.ArgumentList); return InitializerSemanticModel.Create( this.Compilation, initializer.ArgumentList, constructorSymbol, outer); } } } break; case SyntaxKind.Attribute: { var attribute = (AttributeSyntax)node; AliasSymbol aliasOpt; DiagnosticBag discarded = DiagnosticBag.GetInstance(); var attributeType = (NamedTypeSymbol)outer.BindType(attribute.Name, discarded, out aliasOpt); discarded.Free(); return AttributeSemanticModel.Create( this.compilation, attribute, attributeType, aliasOpt, outer.WithAdditionalFlags(BinderFlags.AttributeArgument)); } } return null; }
/// <summary> /// In regular C#, all field initializers are assignments to fields and the assigned expressions /// may not reference instance members. /// </summary> private static void BindRegularCSharpFieldInitializers( CSharpCompilation compilation, ImmutableArray <FieldInitializers> initializers, ArrayBuilder <BoundInitializer> boundInitializers, DiagnosticBag diagnostics, bool generateDebugInfo, out ConsList <Imports> firstDebugImports) { firstDebugImports = null; foreach (FieldInitializers siblingInitializers in initializers) { // All sibling initializers share the same parent node and tree so we can reuse the binder // factory across siblings. Unfortunately, we cannot reuse the binder itself, because // individual fields might have their own binders (e.g. because of being declared unsafe). BinderFactory binderFactory = null; var infos = ArrayBuilder <FieldInitializerInfo> .GetInstance(); // Exact size is not known up front. foreach (FieldInitializer initializer in siblingInitializers.Initializers) { FieldSymbol fieldSymbol = initializer.Field; Debug.Assert((object)fieldSymbol != null); // A constant field of type decimal needs a field initializer, so // check if it is a metadata constant, not just a constant to exclude // decimals. Other constants do not need field initializers. if (!fieldSymbol.IsMetadataConstant) { //Can't assert that this is a regular C# compilation, because we could be in a nested type of a script class. SyntaxReference syntaxRef = initializer.Syntax; var initializerNode = (EqualsValueClauseSyntax)syntaxRef.GetSyntax(); if (binderFactory == null) { binderFactory = compilation.GetBinderFactory(syntaxRef.SyntaxTree); } Binder parentBinder = binderFactory.GetBinder(initializerNode); Debug.Assert(parentBinder.ContainingMemberOrLambda == fieldSymbol.ContainingType || //should be the binder for the type fieldSymbol.ContainingType.IsImplicitClass); //however, we also allow fields in namespaces to help support script scenarios if (generateDebugInfo && firstDebugImports == null) { firstDebugImports = parentBinder.ImportsList; } parentBinder = new LocalScopeBinder(parentBinder).WithAdditionalFlagsAndContainingMemberOrLambda(parentBinder.Flags | BinderFlags.FieldInitializer, fieldSymbol); if (!fieldSymbol.IsConst && !fieldSymbol.IsStatic) { parentBinder = parentBinder.WithPrimaryConstructorParametersIfNecessary(fieldSymbol.ContainingType); } infos.Add(new FieldInitializerInfo(initializer, parentBinder, initializerNode)); } } // See if there are locals that we need to bring into the scope. var locals = default(ImmutableArray <LocalSymbol>); if (siblingInitializers.TypeDeclarationSyntax != null) { locals = GetInitializationScopeLocals(infos); } ArrayBuilder <BoundInitializer> initializersBuilder = locals.IsDefaultOrEmpty ? boundInitializers : ArrayBuilder <BoundInitializer> .GetInstance(infos.Count); foreach (var info in infos) { Binder binder = info.Binder; ScopedExpressionBinder scopedExpressionBinder = null; // Constant initializers is not part of the initialization scope. if (info.Initializer.Field.IsConst || locals.IsDefault) { binder = scopedExpressionBinder = new ScopedExpressionBinder(binder, info.EqualsValue.Value); } else if (!locals.IsEmpty) { binder = new SimpleLocalScopeBinder(locals, binder); } BoundFieldInitializer boundInitializer = BindFieldInitializer(binder, info.Initializer.Field, info.EqualsValue, diagnostics); if (scopedExpressionBinder != null && !scopedExpressionBinder.Locals.IsDefaultOrEmpty) { boundInitializer = boundInitializer.Update(boundInitializer.Field, scopedExpressionBinder.AddLocalScopeToExpression(boundInitializer.InitialValue)); } initializersBuilder.Add(boundInitializer); } Debug.Assert(locals.IsDefaultOrEmpty == (initializersBuilder == boundInitializers)); if (!locals.IsDefaultOrEmpty) { boundInitializers.Add(new BoundInitializationScope((CSharpSyntaxNode)siblingInitializers.TypeDeclarationSyntax.GetSyntax(), locals, initializersBuilder.ToImmutableAndFree())); } } }