/// <summary> /// In regular C#, all field initializers are assignments to fields and the assigned expressions /// may not reference instance members. /// </summary> internal static void BindRegularCSharpFieldInitializers( CSharpCompilation compilation, ImmutableArray <ImmutableArray <FieldOrPropertyInitializer> > initializers, ArrayBuilder <BoundInitializer> boundInitializers, DiagnosticBag diagnostics, out ImportChain firstDebugImports) { firstDebugImports = null; foreach (ImmutableArray <FieldOrPropertyInitializer> 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 (FieldOrPropertyInitializer initializer in siblingInitializers) { FieldSymbol fieldSymbol = initializer.FieldOpt; 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 is TypeSymbol containing && TypeSymbol.Equals(containing, fieldSymbol.ContainingType, TypeCompareKind.ConsiderEverything2)) || //should be the binder for the type fieldSymbol.ContainingType.IsImplicitClass); //however, we also allow fields in namespaces to help support script scenarios if (firstDebugImports == null) { firstDebugImports = parentBinder.ImportChain; } parentBinder = new LocalScopeBinder(parentBinder).WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags.FieldInitializer, fieldSymbol); BoundFieldEqualsValue boundInitializer = BindFieldInitializer(parentBinder, fieldSymbol, 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 <AttributeSyntax> > attributeDeclarationSyntaxLists, AttributeLocation symbolPart, DiagnosticBag diagnostics, CSharpCompilation compilation, Func <AttributeSyntax, bool> attributeMatchesOpt, Binder rootBinderOpt, out ImmutableArray <Binder> binders, Func <Binder, Binder> contextualBinder = null) { 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 attribute in attributeDeclarationSyntaxList) { // We bind the attribute only if it has a matching target for the given ownerSymbol and attributeLocation. if (MatchAttributeTarget(attributeTarget, symbolPart, attribute.Target, diagnostics)) { if (syntaxBuilder == null) { syntaxBuilder = new ArrayBuilder <AttributeSyntax>(); bindersBuilder = new ArrayBuilder <Binder>(); } if (attributeMatchesOpt is null || 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); // We are wrapping the binder with a contextual binder if (contextualBinder != null) { binder = contextualBinder(binder); } 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); } }
public override void DefaultVisit(SyntaxNode node) { SyntaxKind nodeKind = node.Kind(); bool diagnose = node.SyntaxTree.ReportDocumentationCommentDiagnostics(); if (nodeKind == SyntaxKind.XmlCrefAttribute) { XmlCrefAttributeSyntax crefAttr = (XmlCrefAttributeSyntax)node; CrefSyntax cref = crefAttr.Cref; BinderFactory factory = _compilation.GetBinderFactory(cref.SyntaxTree); Binder binder = factory.GetBinder(cref); // Do this for the diagnostics, even if it won't be written. DiagnosticBag crefDiagnostics = DiagnosticBag.GetInstance(); string docCommentId = GetDocumentationCommentId(cref, binder, crefDiagnostics); if (diagnose) { _diagnostics.AddRange(crefDiagnostics); } crefDiagnostics.Free(); if (_writer != null) { Visit(crefAttr.Name); VisitToken(crefAttr.EqualsToken); // Not going to visit normally, because we want to skip trivia within // the attribute value. crefAttr.StartQuoteToken.WriteTo(_writer, leading: true, trailing: false); // We're not going to visit the cref because we want to bind it // and write a doc comment ID in its place. _writer.Write(docCommentId); // Not going to visit normally, because we want to skip trivia within // the attribute value. crefAttr.EndQuoteToken.WriteTo(_writer, leading: false, trailing: true); } // Don't descend - we've already written out everything necessary. return; } else if (diagnose && nodeKind == SyntaxKind.XmlNameAttribute) { XmlNameAttributeSyntax nameAttr = (XmlNameAttributeSyntax)node; BinderFactory factory = _compilation.GetBinderFactory(nameAttr.SyntaxTree); Binder binder = factory.GetBinder(nameAttr, nameAttr.Identifier.SpanStart); // Do this for diagnostics, even if we aren't writing. BindName(nameAttr, binder, _memberSymbol, ref _documentedParameters, ref _documentedTypeParameters, _diagnostics); // Do descend - we still need to write out the tokens of the attribute. } // NOTE: if we're recording any include element nodes (i.e. if includeElementsNodes is non-null), // then we want to record all of them, because we won't be able to distinguish in the XML DOM. if (_includeElementNodes != null) { XmlNameSyntax nameSyntax = null; if (nodeKind == SyntaxKind.XmlEmptyElement) { nameSyntax = ((XmlEmptyElementSyntax)node).Name; } else if (nodeKind == SyntaxKind.XmlElementStartTag) { nameSyntax = ((XmlElementStartTagSyntax)node).Name; } if (nameSyntax != null && nameSyntax.Prefix == null && DocumentationCommentXmlNames.ElementEquals(nameSyntax.LocalName.ValueText, DocumentationCommentXmlNames.IncludeElementName)) { _includeElementNodes.Add((CSharpSyntaxNode)node); } } base.DefaultVisit(node); }
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)); DiagnosticBag crefDiagnostics = DiagnosticBag.GetInstance(); attribute.Value = GetDocumentationCommentId(crefSyntax, binder, crefDiagnostics); // NOTE: mutation (element must be a copy) RecordBindingDiagnostics(crefDiagnostics, sourceLocation); // Respects DocumentationMode. crefDiagnostics.Free(); }
/// <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); } } }