internal SyntaxTreeSemanticModel(CSharpCompilation compilation, SyntaxTree syntaxTree) { _compilation = compilation; _syntaxTree = syntaxTree; if (!this.Compilation.SyntaxTrees.Contains(syntaxTree)) { throw new ArgumentOutOfRangeException("tree", CSharpResources.TreeNotPartOfCompilation); } _binderFactory = compilation.GetBinderFactory(SyntaxTree); }
internal SyntaxTreeSemanticModel(CSharpCompilation compilation, SyntaxTree syntaxTree, bool ignoreAccessibility = false) { _compilation = compilation; _syntaxTree = syntaxTree; _ignoresAccessibility = ignoreAccessibility; if (!this.Compilation.SyntaxTrees.Contains(syntaxTree)) { throw new ArgumentOutOfRangeException(nameof(syntaxTree), CSharpResources.TreeNotPartOfCompilation); } _binderFactory = compilation.GetBinderFactory(SyntaxTree); }
private void BindAndReplaceCref( XAttribute attribute, CSharpSyntaxNode originatingSyntax ) { string attributeValue = attribute.Value; CrefSyntax crefSyntax = SyntaxFactory.ParseCref(attributeValue); if (crefSyntax == null) { // This can happen if the cref is verbatim (e.g. "T:C"). return; } // CONSIDER: It would be easy to construct an XmlLocation from the XAttribute, so that // we could point the user at the actual problem. Location sourceLocation = originatingSyntax.Location; RecordSyntaxDiagnostics(crefSyntax, sourceLocation); // Respects DocumentationMode. MemberDeclarationSyntax memberDeclSyntax = BinderFactory.GetAssociatedMemberForXmlSyntax(originatingSyntax); Debug.Assert( memberDeclSyntax != null, "Why are we processing a documentation comment that is not attached to a member declaration?" ); Binder binder = BinderFactory.MakeCrefBinder( crefSyntax, memberDeclSyntax, _compilation.GetBinderFactory(memberDeclSyntax.SyntaxTree) ); var crefDiagnostics = BindingDiagnosticBag.GetInstance(_diagnostics); attribute.Value = GetDocumentationCommentId(crefSyntax, binder, crefDiagnostics); // NOTE: mutation (element must be a copy) RecordBindingDiagnostics(crefDiagnostics, sourceLocation); // Respects DocumentationMode. crefDiagnostics.Free(); }
internal SyntaxTreeSemanticModel(CSharpCompilation parentCompilation, SyntaxTree parentSyntaxTree, SyntaxTree speculatedSyntaxTree) { _compilation = parentCompilation; _syntaxTree = speculatedSyntaxTree; _binderFactory = _compilation.GetBinderFactory(parentSyntaxTree); }
/// <summary> /// Bind the (implicit or explicit) constructor initializer of a constructor symbol. /// </summary> /// <param name="constructor">Constructor method.</param> /// <param name="diagnostics">Accumulates errors (e.g. access "this" in constructor initializer).</param> /// <param name="compilation">Used to retrieve binder.</param> /// <returns>A bound expression for the constructor initializer call.</returns> private static BoundExpression BindConstructorInitializer(MethodSymbol constructor, DiagnosticBag diagnostics, CSharpCompilation compilation) { // Note that the base type can be null if we're compiling System.Object in source. NamedTypeSymbol baseType = constructor.ContainingType.BaseTypeNoUseSiteDiagnostics; SourceMethodSymbol sourceConstructor = constructor as SourceMethodSymbol; CSharpSyntaxNode syntax = null; ArgumentListSyntax initializerArgumentListOpt = null; if ((object)sourceConstructor != null) { syntax = sourceConstructor.SyntaxNode; if (syntax.Kind == SyntaxKind.ConstructorDeclaration) { var constructorSyntax = (ConstructorDeclarationSyntax)syntax; if (constructorSyntax.Initializer != null) { initializerArgumentListOpt = constructorSyntax.Initializer.ArgumentList; } ErrorCode reportIfHavePrimaryCtor = ErrorCode.Void; if (initializerArgumentListOpt == null || initializerArgumentListOpt.Parent.Kind != SyntaxKind.ThisConstructorInitializer) { reportIfHavePrimaryCtor = ErrorCode.ERR_InstanceCtorMustHaveThisInitializer; } else if (initializerArgumentListOpt.Arguments.Count == 0 && constructor.ContainingType.TypeKind == TypeKind.Struct) { // Based on C# Design Notes for Oct 21, 2013: reportIfHavePrimaryCtor = ErrorCode.ERR_InstanceCtorCannotHaveDefaultThisInitializer; } if (reportIfHavePrimaryCtor != ErrorCode.Void) { var container = constructor.ContainingType as SourceMemberContainerTypeSymbol; if ((object)container != null && (object)container.PrimaryCtor != null) { diagnostics.Add(reportIfHavePrimaryCtor, constructor.Locations[0]); } } } else { // Primary constuctor case. Debug.Assert(syntax.Kind == SyntaxKind.ParameterList); if (syntax.Parent.Kind == SyntaxKind.ClassDeclaration) { var classDecl = (ClassDeclarationSyntax)syntax.Parent; if (classDecl.BaseList != null && classDecl.BaseList.Types.Count > 0) { TypeSyntax baseTypeSyntax = classDecl.BaseList.Types[0]; if (baseTypeSyntax.Kind == SyntaxKind.BaseClassWithArguments) { initializerArgumentListOpt = ((BaseClassWithArgumentsSyntax)baseTypeSyntax).ArgumentList; } } } else { Debug.Assert(syntax.Parent.Kind == SyntaxKind.StructDeclaration); } } } // The common case is that we have no constructor initializer and the type inherits directly from object. // Also, we might be trying to generate a constructor for an entirely compiler-generated class such // as a closure class; in that case it is vexing to try to find a suitable binder for the non-existing // constructor syntax so that we can do unnecessary overload resolution on the non-existing initializer! // Simply take the early out: bind directly to the parameterless object ctor rather than attempting // overload resolution. if (initializerArgumentListOpt == null && (object)baseType != null) { if (baseType.SpecialType == SpecialType.System_Object) { return GenerateObjectConstructorInitializer(constructor, diagnostics); } else if (baseType.IsErrorType() || baseType.IsStatic) { // If the base type is bad and there is no initializer then we can just bail. // We have no expressions we need to analyze to report errors on. return null; } } // Either our base type is not object, or we have an initializer syntax, or both. We're going to // need to do overload resolution on the set of constructors of the base type, either on // the provided initializer syntax, or on an implicit ": base()" syntax. // SPEC ERROR: The specification states that if you have the situation // SPEC ERROR: class B { ... } class D1 : B {} then the default constructor // SPEC ERROR: generated for D1 must call an accessible *parameterless* constructor // SPEC ERROR: in B. However, it also states that if you have // SPEC ERROR: class B { ... } class D2 : B { D2() {} } or // SPEC ERROR: class B { ... } class D3 : B { D3() : base() {} } then // SPEC ERROR: the compiler performs *overload resolution* to determine // SPEC ERROR: which accessible constructor of B is called. Since B might have // SPEC ERROR: a ctor with all optional parameters, overload resolution might // SPEC ERROR: succeed even if there is no parameterless constructor. This // SPEC ERROR: is unintentionally inconsistent, and the native compiler does not // SPEC ERROR: implement this behavior. Rather, we should say in the spec that // SPEC ERROR: if there is no ctor in D1, then a ctor is created for you exactly // SPEC ERROR: as though you'd said "D1() : base() {}". // SPEC ERROR: This is what we now do in Roslyn. // Now, in order to do overload resolution, we're going to need a binder. There are // three possible situations: // // class D1 : B { } // class D2 : B { D2(int x) { } } // class D3 : B { D3(int x) : base(x) { } } // // In the first case the binder needs to be the binder associated with // the *body* of D1 because if the base class ctor is protected, we need // to be inside the body of a derived class in order for it to be in the // accessibility domain of the protected base class ctor. // // In the second case the binder could be the binder associated with // the body of D2; since the implicit call to base() will have no arguments // there is no need to look up "x". // // In the third case the binder must be the binder that knows about "x" // because x is in scope. Binder outerBinder; if ((object)sourceConstructor == null) { // The constructor is implicit. We need to get the binder for the body // of the enclosing class. CSharpSyntaxNode containerNode = constructor.GetNonNullSyntaxNode(); SyntaxToken bodyToken; if (containerNode.Kind == SyntaxKind.ClassDeclaration) { bodyToken = ((ClassDeclarationSyntax)containerNode).OpenBraceToken; } else if (containerNode.Kind == SyntaxKind.StructDeclaration) { bodyToken = ((StructDeclarationSyntax)containerNode).OpenBraceToken; } else if (containerNode.Kind == SyntaxKind.EnumDeclaration) { // We're not going to find any non-default ctors, but we'll look anyway. bodyToken = ((EnumDeclarationSyntax)containerNode).OpenBraceToken; } else { Debug.Assert(false, "How did we get an implicit constructor added to something that is neither a class nor a struct?"); bodyToken = containerNode.GetFirstToken(); } outerBinder = compilation.GetBinderFactory(containerNode.SyntaxTree).GetBinder(containerNode, bodyToken.Position); } else if (initializerArgumentListOpt == null) { // We have a ctor in source but no explicit constructor initializer. We can't just use the binder for the // type containing the ctor because the ctor might be marked unsafe. Use the binder for the parameter list // as an approximation - the extra symbols won't matter because there are no identifiers to bind. outerBinder = compilation.GetBinderFactory(sourceConstructor.SyntaxTree).GetBinder(syntax.Kind == SyntaxKind.ParameterList ? syntax : ((ConstructorDeclarationSyntax)syntax).ParameterList); } else { outerBinder = compilation.GetBinderFactory(sourceConstructor.SyntaxTree).GetBinder(initializerArgumentListOpt); } //wrap in ConstructorInitializerBinder for appropriate errors Binder initializerBinder = outerBinder.WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags.ConstructorInitializer, constructor); return initializerBinder.BindConstructorInitializer(initializerArgumentListOpt, constructor, diagnostics); }
/// <summary> /// In script C#, some field initializers are assignments to fields and others are global /// statements. There are no restrictions on accessing instance members. /// </summary> private static void BindScriptFieldInitializers(CSharpCompilation compilation, MethodSymbol scriptCtor, ImmutableArray<ImmutableArray<FieldInitializer>> initializers, ArrayBuilder<BoundInitializer> boundInitializers, DiagnosticBag diagnostics, bool generateDebugInfo, out ConsList<Imports> firstDebugImports) { Debug.Assert((object)scriptCtor != null); firstDebugImports = null; for (int i = 0; i < initializers.Length; i++) { ImmutableArray<FieldInitializer> siblingInitializers = initializers[i]; // 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; for (int j = 0; j < siblingInitializers.Length; j++) { var initializer = siblingInitializers[j]; var fieldSymbol = initializer.Field; if ((object)fieldSymbol != null && fieldSymbol.IsConst) { // Constants do not need field initializers. continue; } var syntaxRef = initializer.Syntax; Debug.Assert(syntaxRef.SyntaxTree.Options.Kind != SourceCodeKind.Regular); var initializerNode = (CSharpSyntaxNode)syntaxRef.GetSyntax(); if (binderFactory == null) { binderFactory = compilation.GetBinderFactory(syntaxRef.SyntaxTree); } Binder scriptClassBinder = binderFactory.GetBinder(initializerNode); Debug.Assert(((ImplicitNamedTypeSymbol)scriptClassBinder.ContainingMemberOrLambda).IsScriptClass); if (generateDebugInfo && firstDebugImports == null) { firstDebugImports = scriptClassBinder.ImportsList; } Binder parentBinder = new ExecutableCodeBinder((CSharpSyntaxNode)syntaxRef.SyntaxTree.GetRoot(), scriptCtor, scriptClassBinder); BoundInitializer boundInitializer; if ((object)fieldSymbol != null) { boundInitializer = BindFieldInitializer( new LocalScopeBinder(parentBinder).WithAdditionalFlagsAndContainingMemberOrLambda(parentBinder.Flags | BinderFlags.FieldInitializer, fieldSymbol), fieldSymbol, (EqualsValueClauseSyntax)initializerNode, diagnostics); } else if (initializerNode.Kind == SyntaxKind.LabeledStatement) { // TODO: labels in interactive var boundStatement = new BoundBadStatement(initializerNode, ImmutableArray<BoundNode>.Empty, true); boundInitializer = new BoundGlobalStatementInitializer(initializerNode, boundStatement); } else { var collisionDetector = new LocalScopeBinder(parentBinder); boundInitializer = BindGlobalStatement(collisionDetector, (StatementSyntax)initializerNode, diagnostics, isLast: i == initializers.Length - 1 && j == siblingInitializers.Length - 1); } boundInitializers.Add(boundInitializer); } } }
/// <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<ImmutableArray<FieldInitializer>> initializers, ArrayBuilder<BoundInitializer> boundInitializers, DiagnosticBag diagnostics, bool generateDebugInfo, out ConsList<Imports> firstDebugImports) { firstDebugImports = null; foreach (ImmutableArray<FieldInitializer> 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; foreach (FieldInitializer initializer in siblingInitializers) { 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 = (CSharpSyntaxNode)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; } BoundInitializer boundInitializer = BindFieldInitializer( new LocalScopeBinder(parentBinder.WithPrimaryConstructorParametersIfNecessary(fieldSymbol.ContainingType, shadowBackingFields: true)). WithAdditionalFlagsAndContainingMemberOrLambda(parentBinder.Flags | BinderFlags.FieldInitializer, fieldSymbol), fieldSymbol, (EqualsValueClauseSyntax)initializerNode, diagnostics); boundInitializers.Add(boundInitializer); } } } }
/// <summary> /// Method to merge attributes from the given attributesSyntaxLists and filter out attributes by attribute target. /// This is the first step in attribute binding. /// </summary> /// <remarks> /// This method can generate diagnostics for few cases where we have an invalid target specifier and the parser hasn't generated the necessary diagnostics. /// It should not perform any bind operations as it can lead to an attribute binding cycle. /// </remarks> private ImmutableArray<AttributeSyntax> GetAttributesToBind( OneOrMany<SyntaxList<AttributeListSyntax>> attributeDeclarationSyntaxLists, AttributeLocation symbolPart, DiagnosticBag diagnostics, CSharpCompilation compilation, out ImmutableArray<Binder> binders) { var attributeTarget = (IAttributeTargetSymbol)this; ArrayBuilder<AttributeSyntax> syntaxBuilder = null; ArrayBuilder<Binder> bindersBuilder = null; int attributesToBindCount = 0; for (int listIndex = 0; listIndex < attributeDeclarationSyntaxLists.Count; listIndex++) { var attributeDeclarationSyntaxList = attributeDeclarationSyntaxLists[listIndex]; if (attributeDeclarationSyntaxList.Any()) { int prevCount = attributesToBindCount; foreach (var attributeDeclarationSyntax in attributeDeclarationSyntaxList) { // We bind the attribute only if it has a matching target for the given ownerSymbol and attributeLocation. if (MatchAttributeTarget(attributeTarget, symbolPart, attributeDeclarationSyntax.Target, diagnostics)) { if (syntaxBuilder == null) { syntaxBuilder = new ArrayBuilder<AttributeSyntax>(); bindersBuilder = new ArrayBuilder<Binder>(); } var attributesToBind = attributeDeclarationSyntax.Attributes; syntaxBuilder.AddRange(attributesToBind); attributesToBindCount += attributesToBind.Count; } } if (attributesToBindCount != prevCount) { Debug.Assert(attributeDeclarationSyntaxList.Node != null); Debug.Assert(bindersBuilder != null); var syntaxTree = attributeDeclarationSyntaxList.Node.SyntaxTree; var binder = compilation.GetBinderFactory(syntaxTree).GetBinder((CSharpSyntaxNode)attributeDeclarationSyntaxList.Node); binder = new ContextualAttributeBinder(binder, this); Debug.Assert(!binder.InAttributeArgument, "Possible cycle in attribute binding"); for (int i = 0; i < attributesToBindCount - prevCount; i++) { bindersBuilder.Add(binder); } } } } if (syntaxBuilder != null) { binders = bindersBuilder.ToImmutableAndFree(); return syntaxBuilder.ToImmutableAndFree(); } else { binders = ImmutableArray<Binder>.Empty; return ImmutableArray<AttributeSyntax>.Empty; } }
/// <summary> /// In script C#, some field initializers are assignments to fields and others are global /// statements. There are no restrictions on accessing instance members. /// </summary> private static void BindScriptFieldInitializers( CSharpCompilation compilation, SynthesizedInteractiveInitializerMethod scriptInitializer, ImmutableArray<ImmutableArray<FieldOrPropertyInitializer>> initializers, ArrayBuilder<BoundInitializer> boundInitializers, DiagnosticBag diagnostics, out ImportChain firstDebugImports) { firstDebugImports = null; for (int i = 0; i < initializers.Length; i++) { ImmutableArray<FieldOrPropertyInitializer> siblingInitializers = initializers[i]; // 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; // Label instances must be shared across all global statements. ScriptLocalScopeBinder.Labels labels = null; for (int j = 0; j < siblingInitializers.Length; j++) { var initializer = siblingInitializers[j]; var fieldSymbol = initializer.FieldOpt; if ((object)fieldSymbol != null && fieldSymbol.IsConst) { // Constants do not need field initializers. continue; } var syntaxRef = initializer.Syntax; var syntaxTree = syntaxRef.SyntaxTree; Debug.Assert(syntaxTree.Options.Kind != SourceCodeKind.Regular); var syntax = (CSharpSyntaxNode)syntaxRef.GetSyntax(); var syntaxRoot = syntaxTree.GetCompilationUnitRoot(); if (binderFactory == null) { binderFactory = compilation.GetBinderFactory(syntaxTree); labels = new ScriptLocalScopeBinder.Labels(scriptInitializer, syntaxRoot); } Binder scriptClassBinder = binderFactory.GetBinder(syntax); Debug.Assert(((NamedTypeSymbol)scriptClassBinder.ContainingMemberOrLambda).IsScriptClass); if (firstDebugImports == null) { firstDebugImports = scriptClassBinder.ImportChain; } Binder parentBinder = new ExecutableCodeBinder( syntaxRoot, scriptInitializer, new ScriptLocalScopeBinder(labels, scriptClassBinder)); BoundInitializer boundInitializer; if ((object)fieldSymbol != null) { boundInitializer = BindFieldInitializer( parentBinder.WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags.FieldInitializer, fieldSymbol), fieldSymbol, (EqualsValueClauseSyntax)syntax, diagnostics); } else { boundInitializer = BindGlobalStatement( parentBinder, scriptInitializer, (StatementSyntax)syntax, diagnostics, isLast: i == initializers.Length - 1 && j == siblingInitializers.Length - 1); } boundInitializers.Add(boundInitializer); } } }
internal static ImmutableArray<LocalSymbol> GetFieldInitializerInfos( CSharpCompilation compilation, FieldInitializers siblingInitializers, ArrayBuilder<FieldInitializerInfo> infos, bool generateDebugInfo, ref ConsList<Imports> firstDebugImports) { // 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; 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); if (!locals.IsDefaultOrEmpty) { for (int i = 0; i < infos.Count; i++) { FieldInitializerInfo info = infos[i]; // Constant initializers is not part of the initialization scope. if (!info.Initializer.Field.IsConst) { infos[i] = new FieldInitializerInfo(info.Initializer, new SimpleLocalScopeBinder(locals, info.Binder), info.EqualsValue); } } } } return locals; }
internal static ImmutableArray <LocalSymbol> GetFieldInitializerInfos( CSharpCompilation compilation, FieldInitializers siblingInitializers, ArrayBuilder <FieldInitializerInfo> infos, bool generateDebugInfo, ref ConsList <Imports> firstDebugImports) { // 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; 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); if (!locals.IsDefaultOrEmpty) { for (int i = 0; i < infos.Count; i++) { FieldInitializerInfo info = infos[i]; // Constant initializers is not part of the initialization scope. if (!info.Initializer.Field.IsConst) { infos[i] = new FieldInitializerInfo(info.Initializer, new SimpleLocalScopeBinder(locals, info.Binder), info.EqualsValue); } } } } return(locals); }
private ImmutableArray <AttributeSyntax> GetAttributesToBind( OneOrMany <SyntaxList <AttributeListSyntax> > attributeDeclarationSyntaxLists, AttributeLocation symbolPart, BindingDiagnosticBag diagnostics, CSharpCompilation compilation, Func <AttributeSyntax, bool> attributeMatchesOpt, Binder rootBinderOpt, out ImmutableArray <Binder> binders) { var attributeTarget = (IAttributeTargetSymbol)this; ArrayBuilder <AttributeSyntax> syntaxBuilder = null; ArrayBuilder <Binder> bindersBuilder = null; int attributesToBindCount = 0; for (int listIndex = 0; listIndex < attributeDeclarationSyntaxLists.Count; listIndex++) { var attributeDeclarationSyntaxList = attributeDeclarationSyntaxLists[listIndex]; if (attributeDeclarationSyntaxList.Any()) { int prevCount = attributesToBindCount; foreach (var attributeDeclarationSyntax in attributeDeclarationSyntaxList) { // We bind the attribute only if it has a matching target for the given ownerSymbol and attributeLocation. if (MatchAttributeTarget(attributeTarget, symbolPart, attributeDeclarationSyntax.Target, diagnostics)) { if (syntaxBuilder == null) { syntaxBuilder = new ArrayBuilder <AttributeSyntax>(); bindersBuilder = new ArrayBuilder <Binder>(); } var attributesToBind = attributeDeclarationSyntax.Attributes; if (attributeMatchesOpt is null) { syntaxBuilder.AddRange(attributesToBind); attributesToBindCount += attributesToBind.Count; } else { foreach (var attribute in attributesToBind) { if (attributeMatchesOpt(attribute)) { syntaxBuilder.Add(attribute); attributesToBindCount++; } } } } } if (attributesToBindCount != prevCount) { Debug.Assert(attributeDeclarationSyntaxList.Node != null); Debug.Assert(bindersBuilder != null); var syntaxTree = attributeDeclarationSyntaxList.Node.SyntaxTree; var binder = rootBinderOpt ?? compilation.GetBinderFactory(syntaxTree).GetBinder(attributeDeclarationSyntaxList.Node); binder = new ContextualAttributeBinder(binder, this); Debug.Assert(!binder.InAttributeArgument || this is MethodSymbol { MethodKind: MethodKind.LambdaMethod }, "Possible cycle in attribute binding");
/// <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())); } } }