/// <summary> /// Parse expression. Returns null if there are any errors. /// </summary> internal static ExpressionSyntax ParseExpression( this string expr, DiagnosticBag diagnostics, bool allowFormatSpecifiers, out ReadOnlyCollection<string> formatSpecifiers) { // Remove trailing semi-colon if any. This is to support copy/paste // of (simple cases of) RHS of assignment in Watch window, not to // allow arbitrary syntax after the semi-colon, not even comments. if (RemoveSemicolonIfAny(ref expr)) { // Format specifiers are not expected before a semi-colon. allowFormatSpecifiers = false; } var syntax = ParseDebuggerExpression(expr, consumeFullText: !allowFormatSpecifiers); diagnostics.AddRange(syntax.GetDiagnostics()); formatSpecifiers = null; if (allowFormatSpecifiers) { var builder = ArrayBuilder<string>.GetInstance(); if (ParseFormatSpecifiers(builder, expr, syntax.FullWidth, diagnostics) && (builder.Count > 0)) { formatSpecifiers = new ReadOnlyCollection<string>(builder.ToArray()); } builder.Free(); } return diagnostics.HasAnyErrors() ? null : syntax; }
internal static ExpressionSyntax ParseAssignment( this string target, string expr, DiagnosticBag diagnostics) { var text = SourceText.From(expr); var expression = SyntaxHelpers.ParseDebuggerExpressionInternal(text, consumeFullText: true); // We're creating a SyntaxTree for just the RHS so that the Diagnostic spans for parse errors // will be correct (with respect to the original input text). If we ever expose a SemanticModel // for debugger expressions, we should use this SyntaxTree. var syntaxTree = expression.CreateSyntaxTree(text); diagnostics.AddRange(syntaxTree.GetDiagnostics()); if (diagnostics.HasAnyErrors()) { return null; } // Any Diagnostic spans produced in binding will be offset by the length of the "target" expression text. // If we want to support live squiggles in debugger windows, SemanticModel, etc, we'll want to address this. var targetSyntax = SyntaxHelpers.ParseDebuggerExpressionInternal(SourceText.From(target), consumeFullText: true); Debug.Assert(!targetSyntax.GetDiagnostics().Any(), "The target of an assignment should never contain Diagnostics if we're being allowed to assign to it in the debugger."); var assignment = InternalSyntax.SyntaxFactory.AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, targetSyntax, InternalSyntax.SyntaxFactory.Token(SyntaxKind.EqualsToken), expression); return assignment.MakeDebuggerExpression(SourceText.From(assignment.ToString())); }
internal static void BindFieldInitializers( SourceMemberContainerTypeSymbol typeSymbol, MethodSymbol scriptCtor, ImmutableArray<FieldInitializers> fieldInitializers, bool generateDebugInfo, DiagnosticBag diagnostics, ref ProcessedFieldInitializers processedInitializers) //by ref so that we can store the results of lowering { DiagnosticBag diagsForInstanceInitializers = DiagnosticBag.GetInstance(); try { ConsList<Imports> firstDebugImports; processedInitializers.BoundInitializers = BindFieldInitializers(typeSymbol, scriptCtor, fieldInitializers, diagsForInstanceInitializers, generateDebugInfo, out firstDebugImports); processedInitializers.HasErrors = diagsForInstanceInitializers.HasAnyErrors(); processedInitializers.FirstDebugImports = firstDebugImports; } finally { diagnostics.AddRange(diagsForInstanceInitializers); diagsForInstanceInitializers.Free(); } }
internal void GrabDiagnostics(DiagnosticBag addTo) { // force lazy init ComputeParameters(); ComputeReturnType(); var diags = ImmutableInterlocked.InterlockedExchange(ref _diagnostics, default(ImmutableArray<Diagnostic>)); if (!diags.IsDefault) { addTo.AddRange(diags); } }
/// <summary> /// Builds a delegate that will execute just this scripts code. /// </summary> public Func<object[], object> Build( Script script, DiagnosticBag diagnostics, CancellationToken cancellationToken) { var compilation = script.GetCompilation(); using (var peStream = new MemoryStream()) { var emitResult = compilation.Emit( peStream: peStream, pdbStream: null, xmlDocumentationStream: null, win32Resources: null, manifestResources: null, options: EmitOptions.Default, cancellationToken: cancellationToken); diagnostics.AddRange(emitResult.Diagnostics); if (!emitResult.Success) { return null; } // let the loader know where to find assemblies: foreach (var referencedAssembly in compilation.GetBoundReferenceManager().GetReferencedAssemblies()) { var path = (referencedAssembly.Key as PortableExecutableReference)?.FilePath; if (path != null) { // TODO: Should the #r resolver return contract metadata and runtime assembly path - // Contract assembly used in the compiler, RT assembly path here. _assemblyLoader.RegisterDependency(referencedAssembly.Value.Identity, path); } } peStream.Position = 0; var assembly = _assemblyLoader.Load(peStream, pdbStream: null); // TODO: GetEntryPoint currently doesn't work for scripts/submissions. // See https://github.com/dotnet/roslyn/issues/3719. // var entryPoint = compilation.GetEntryPoint(cancellationToken); var entryPointMethod = GetEntryPointRuntimeMethod(emitResult.EntryPointOpt, assembly, cancellationToken); return entryPointMethod.CreateDelegate<Func<object[], object>>(); } }
internal static void BindFieldInitializers( CSharpCompilation compilation, SynthesizedInteractiveInitializerMethod scriptInitializerOpt, ImmutableArray<ImmutableArray<FieldOrPropertyInitializer>> fieldInitializers, DiagnosticBag diagnostics, ref ProcessedFieldInitializers processedInitializers) { var diagsForInstanceInitializers = DiagnosticBag.GetInstance(); ImportChain firstImportChain; processedInitializers.BoundInitializers = BindFieldInitializers(compilation, scriptInitializerOpt, fieldInitializers, diagsForInstanceInitializers, out firstImportChain); processedInitializers.HasErrors = diagsForInstanceInitializers.HasAnyErrors(); processedInitializers.FirstImportChain = firstImportChain; diagnostics.AddRange(diagsForInstanceInitializers); diagsForInstanceInitializers.Free(); }
protected BoundExpression BindTargetExpression(DiagnosticBag diagnostics) { if (lazyExpressionAndDiagnostics == null) { // Filter out method group in conversion. DiagnosticBag expressionDiagnostics = DiagnosticBag.GetInstance(); BoundExpression boundExpression = this.BindValue(TargetExpressionSyntax, expressionDiagnostics, Binder.BindValueKind.RValueOrMethodGroup); Interlocked.CompareExchange(ref lazyExpressionAndDiagnostics, new ExpressionAndDiagnostics(boundExpression, expressionDiagnostics.ToReadOnlyAndFree()), null); } Debug.Assert(lazyExpressionAndDiagnostics != null); if (diagnostics != null) { diagnostics.AddRange(lazyExpressionAndDiagnostics.Diagnostics); } return lazyExpressionAndDiagnostics.Expression; }
internal override BoundStatement BindSwitchExpressionAndSections(SwitchStatementSyntax node, Binder originalBinder, DiagnosticBag diagnostics) { // If it is a valid C# 6 switch statement, we use the old binder to bind it. if (!UseV7SwitchBinder) return base.BindSwitchExpressionAndSections(node, originalBinder, diagnostics); Debug.Assert(SwitchSyntax.Equals(node)); // Bind switch expression and set the switch governing type. var boundSwitchExpression = SwitchGoverningExpression; diagnostics.AddRange(SwitchGoverningDiagnostics); BoundPatternSwitchLabel defaultLabel; ImmutableArray<BoundPatternSwitchSection> switchSections = BindPatternSwitchSections(boundSwitchExpression, node.Sections, originalBinder, out defaultLabel, diagnostics); var locals = GetDeclaredLocalsForScope(node); var functions = GetDeclaredLocalFunctionsForScope(node); return new BoundPatternSwitchStatement( node, boundSwitchExpression, locals, functions, switchSections, defaultLabel, this.BreakLabel, this); }
internal static void BindFieldInitializers( CSharpCompilation compilation, SynthesizedInteractiveInitializerMethod scriptInitializerOpt, ImmutableArray<ImmutableArray<FieldOrPropertyInitializer>> fieldInitializers, DiagnosticBag diagnostics, bool setReturnType, // Remove once static fields are errors in submissions. ref ProcessedFieldInitializers processedInitializers) { if (setReturnType && ((object)scriptInitializerOpt != null)) { SetScriptInitializerReturnType(compilation, scriptInitializerOpt, fieldInitializers, diagnostics); } var diagsForInstanceInitializers = DiagnosticBag.GetInstance(); ImportChain firstImportChain; processedInitializers.BoundInitializers = BindFieldInitializers(compilation, scriptInitializerOpt, fieldInitializers, diagsForInstanceInitializers, out firstImportChain); processedInitializers.HasErrors = diagsForInstanceInitializers.HasAnyErrors(); processedInitializers.FirstImportChain = firstImportChain; diagnostics.AddRange(diagsForInstanceInitializers); diagsForInstanceInitializers.Free(); }
internal static void BindFieldInitializers( SourceMemberContainerTypeSymbol typeSymbol, MethodSymbol scriptCtor, ImmutableArray<ImmutableArray<FieldOrPropertyInitializer>> fieldInitializers, DiagnosticBag diagnostics, ref ProcessedFieldInitializers processedInitializers) //by ref so that we can store the results of lowering { DiagnosticBag diagsForInstanceInitializers = DiagnosticBag.GetInstance(); try { ImportChain firstImportChain; processedInitializers.BoundInitializers = BindFieldInitializers(typeSymbol, scriptCtor, fieldInitializers, diagsForInstanceInitializers, out firstImportChain); processedInitializers.HasErrors = diagsForInstanceInitializers.HasAnyErrors(); processedInitializers.FirstImportChain = firstImportChain; } finally { diagnostics.AddRange(diagsForInstanceInitializers); diagsForInstanceInitializers.Free(); } }
/// <summary> /// This method does the following set of operations in the specified order: /// (1) GetAttributesToBind: Merge attributes from the given attributesSyntaxLists and filter out attributes by attribute target. /// (2) BindAttributeTypes: Bind all the attribute types to enable early decode of certain well-known attributes by type. /// (3) EarlyDecodeWellKnownAttributes: Perform early decoding of certain well-known attributes that could be queried by the binder in subsequent steps. /// (NOTE: This step has the side effect of updating the symbol state based on the data extracted from well known attributes). /// (4) GetAttributes: Bind the attributes (attribute arguments and constructor) using bound attribute types. /// (5) DecodeWellKnownAttributes: Decode and validate bound well known attributes. /// (NOTE: This step has the side effect of updating the symbol state based on the data extracted from well known attributes). /// (6) StoreBoundAttributesAndDoPostValidation: /// (a) Store the bound attributes in lazyCustomAttributes in a thread safe manner. /// (b) Perform some additional post attribute validations, such as /// 1) Duplicate attributes, attribute usage target validation, etc. /// 2) Post validation for attributes dependent on other attributes /// These validations cannot be performed prior to step 6(a) as we might need to /// perform a GetAttributes() call on a symbol which can introduce a cycle in attribute binding. /// We avoid this cycle by performing such validations in PostDecodeWellKnownAttributes after lazyCustomAttributes have been set. /// NOTE: PostDecodeWellKnownAttributes SHOULD NOT change the symbol state. /// </summary> /// <remarks> /// Current design of early decoding well-known attributes doesn't permit decoding attribute arguments/constructor as this can lead to binding cycles. /// For well-known attributes used by the binder, where we need the decoded arguments, we must handle them specially in one of the following possible ways: /// (a) Avoid decoding the attribute arguments during binding and delay the corresponding binder tasks to a separate post-pass executed after binding. /// (b) As the cycles can be caused only when we are binding attribute arguments/constructor, special case the corresponding binder tasks based on the current BinderFlags. /// </remarks> /// <param name="attributesSyntaxLists"></param> /// <param name="lazyCustomAttributesBag"></param> /// <param name="symbolPart">Specific part of the symbol to which the attributes apply, or <see cref="AttributeLocation.None"/> if the attributes apply to the symbol itself.</param> /// <param name="earlyDecodingOnly">Indicates that only early decoding should be performed. WARNING: the resulting bag will not be sealed.</param> /// <param name="addToDiagnostics">Diagnostic bag to report into. If null, diagnostics will be reported into <see cref="AddDeclarationDiagnostics"/></param> /// <param name="binderOpt">Binder to use. If null, <see cref="DeclaringCompilation"/> GetBinderFactory will be used.</param> /// <returns>Flag indicating whether lazyCustomAttributes were stored on this thread. Caller should check for this flag and perform NotePartComplete if true.</returns> internal bool LoadAndValidateAttributes( OneOrMany<SyntaxList<AttributeListSyntax>> attributesSyntaxLists, ref CustomAttributesBag<CSharpAttributeData> lazyCustomAttributesBag, AttributeLocation symbolPart = AttributeLocation.None, bool earlyDecodingOnly = false, DiagnosticBag addToDiagnostics = null, Binder binderOpt = null) { var diagnostics = DiagnosticBag.GetInstance(); var compilation = this.DeclaringCompilation; ImmutableArray<Binder> binders; ImmutableArray<AttributeSyntax> attributesToBind = this.GetAttributesToBind(attributesSyntaxLists, symbolPart, diagnostics, compilation, binderOpt, out binders); Debug.Assert(!attributesToBind.IsDefault); ImmutableArray<CSharpAttributeData> boundAttributes; WellKnownAttributeData wellKnownAttributeData; if (attributesToBind.Any()) { Debug.Assert(!binders.IsDefault); Debug.Assert(binders.Length == attributesToBind.Length); // Initialize the bag so that data decoded from early attributes can be stored onto it. if (lazyCustomAttributesBag == null) { Interlocked.CompareExchange(ref lazyCustomAttributesBag, new CustomAttributesBag<CSharpAttributeData>(), null); } // Bind the attribute types and then early decode them. int totalAttributesCount = attributesToBind.Length; var attributeTypesBuilder = new NamedTypeSymbol[totalAttributesCount]; Binder.BindAttributeTypes(binders, attributesToBind, this, attributeTypesBuilder, diagnostics); ImmutableArray<NamedTypeSymbol> boundAttributeTypes = attributeTypesBuilder.AsImmutableOrNull(); this.EarlyDecodeWellKnownAttributeTypes(boundAttributeTypes, attributesToBind); this.PostEarlyDecodeWellKnownAttributeTypes(); // Bind the attribute in two stages - early and normal. var attributesBuilder = new CSharpAttributeData[totalAttributesCount]; // Early bind and decode some well-known attributes. EarlyWellKnownAttributeData earlyData = this.EarlyDecodeWellKnownAttributes(binders, boundAttributeTypes, attributesToBind, symbolPart, attributesBuilder); Debug.Assert(!attributesBuilder.Contains((attr) => attr != null && attr.HasErrors)); // Store data decoded from early bound well-known attributes. // TODO: what if this succeeds on another thread, not ours? lazyCustomAttributesBag.SetEarlyDecodedWellKnownAttributeData(earlyData); if (earlyDecodingOnly) { diagnostics.Free(); //NOTE: dropped. return false; } // Bind attributes. Binder.GetAttributes(binders, attributesToBind, boundAttributeTypes, attributesBuilder, diagnostics); boundAttributes = attributesBuilder.AsImmutableOrNull(); // All attributes must be bound by now. Debug.Assert(!boundAttributes.Any((attr) => attr == null)); // Validate attribute usage and Decode remaining well-known attributes. wellKnownAttributeData = this.ValidateAttributeUsageAndDecodeWellKnownAttributes(binders, attributesToBind, boundAttributes, diagnostics, symbolPart); // Store data decoded from remaining well-known attributes. // TODO: what if this succeeds on another thread but not this thread? lazyCustomAttributesBag.SetDecodedWellKnownAttributeData(wellKnownAttributeData); } else if (earlyDecodingOnly) { diagnostics.Free(); //NOTE: dropped. return false; } else { boundAttributes = ImmutableArray<CSharpAttributeData>.Empty; wellKnownAttributeData = null; Interlocked.CompareExchange(ref lazyCustomAttributesBag, CustomAttributesBag<CSharpAttributeData>.WithEmptyData(), null); this.PostEarlyDecodeWellKnownAttributeTypes(); } this.PostDecodeWellKnownAttributes(boundAttributes, attributesToBind, diagnostics, symbolPart, wellKnownAttributeData); // Store attributes into the bag. bool lazyAttributesStoredOnThisThread = false; if (lazyCustomAttributesBag.SetAttributes(boundAttributes)) { this.RecordPresenceOfBadAttributes(boundAttributes); if (addToDiagnostics == null) { this.AddDeclarationDiagnostics(diagnostics); } else { addToDiagnostics.AddRange(diagnostics); } lazyAttributesStoredOnThisThread = true; if (lazyCustomAttributesBag.IsEmpty) lazyCustomAttributesBag = CustomAttributesBag<CSharpAttributeData>.Empty; } Debug.Assert(lazyCustomAttributesBag.IsSealed); diagnostics.Free(); return lazyAttributesStoredOnThisThread; }
internal void GrabDiagnostics(DiagnosticBag addTo) { // force lazy init ComputeParameters(); ComputeReturnType(); var diags = ImmutableInterlocked.InterlockedExchange(ref _diagnostics, default(ImmutableArray<Diagnostic>)); if (!diags.IsDefault) { addTo.AddRange(diags); addTo.AddRange(_lazyParametersAndDiagnostics.Diagnostics); // Note _lazyParametersAndDiagnostics and _lazyReturnTypeAndDiagnostics // are computed always, but _lazyTypeParameterConstraintsAndDiagnostics // is only computed if there are constraints. if (_lazyTypeParameterConstraintsAndDiagnostics != null) { addTo.AddRange(_lazyTypeParameterConstraintsAndDiagnostics.Diagnostics); } addTo.AddRange(_lazyReturnTypeAndDiagnostics.Diagnostics); } }
public bool GenerateSummaryErrors(DiagnosticBag diagnostics) { // It is highly likely that "the same" error will be given for two different // bindings of the same lambda but with different values for the parameters // of the error. For example, if we have x=>x.Blah() where x could be int // or string, then the two errors will be "int does not have member Blah" and // "string does not have member Blah", but the locations and errors numbers // will be the same. // // We should first see if there is a set of errors that are "the same" by // this definition that occur in every lambda binding; if there are then // those are the errors we should report. // // If there are no errors that are common to *every* binding then we // can report the complete set of errors produced by every binding. However, // we still wish to avoid duplicates, so we will use the same logic for // building the union as the intersection; two errors with the same code // and location are to be treated as the same error and only reported once, // regardless of how that error is parameterized. // // The question then rears its head: when given two of "the same" error // to report that are nevertheless different in their arguments, which one // do we choose? To the user it hardly matters; either one points to the // right location in source code. But it surely matters to our testing team; // we do not want to be in a position where some small change to our internal // representation of lambdas causes tests to break because errors are reported // differently. // // What we need to do is find a *repeatable* arbitrary way to choose between // two errors; we can for example simply take the one that is lower in alphabetical // order when converted to a string. var equalityComparer = new CommonDiagnosticComparer(); Func<Diagnostic, Diagnostic, int> canonicalComparer = CanonicallyCompareDiagnostics; FirstAmongEqualsSet<Diagnostic> intersection = null; var convBags = from boundLambda in _bindingCache.Values select boundLambda.Diagnostics; var retBags = from boundLambda in _returnInferenceCache.Values select boundLambda.Diagnostics; var allBags = convBags.Concat(retBags); foreach (ImmutableArray<Diagnostic> bag in allBags) { if (intersection == null) { intersection = new FirstAmongEqualsSet<Diagnostic>(bag, equalityComparer, canonicalComparer); } else { intersection.IntersectWith(bag); } } if (intersection != null) { foreach (var diagnostic in intersection) { if (ErrorFacts.PreventsSuccessfulDelegateConversion((ErrorCode)diagnostic.Code)) { diagnostics.AddRange(intersection); return true; } } } FirstAmongEqualsSet<Diagnostic> union = null; foreach (ImmutableArray<Diagnostic> bag in allBags) { if (union == null) { union = new FirstAmongEqualsSet<Diagnostic>(bag, equalityComparer, canonicalComparer); } else { union.UnionWith(bag); } } if (union != null) { foreach (var diagnostic in union) { if (ErrorFacts.PreventsSuccessfulDelegateConversion((ErrorCode)diagnostic.Code)) { diagnostics.AddRange(union); return true; } } } return false; }
public static bool ReportDelegateMethodGroupDiagnostics(Binder binder, BoundMethodGroup expr, TypeSymbol targetType, DiagnosticBag diagnostics) { var invokeMethodOpt = GetDelegateInvokeMethodIfAvailable(targetType); HashSet <DiagnosticInfo> useSiteDiagnostics = null; var resolution = ResolveDelegateMethodGroup(binder, expr, invokeMethodOpt, ref useSiteDiagnostics); diagnostics.Add(expr.Syntax, useSiteDiagnostics); bool hasErrors = resolution.HasAnyErrors; diagnostics.AddRange(resolution.Diagnostics); // SPEC VIOLATION: Unfortunately, we cannot exactly implement the specification for // the scenario in which an extension method that extends a value type is converted // to a delegate. The code we generate that captures a delegate to a static method // that is "partially evaluated" with the bound-to-the-delegate first argument // requires that the first argument be of reference type. // // SPEC VIOLATION: Similarly, we cannot capture a method of Nullable<T>, because // boxing a Nullable<T> gives a T, not a boxed Nullable<T>. // // We give special error messages in these situations. if (resolution.MethodGroup != null) { var result = resolution.OverloadResolutionResult; if (result != null) { if (result.Succeeded) { var method = result.BestResult.Member; Debug.Assert((object)method != null); if (resolution.MethodGroup.IsExtensionMethodGroup) { Debug.Assert(method.IsExtensionMethod); var thisParameter = method.Parameters[0]; if (!thisParameter.Type.IsReferenceType) { // Extension method '{0}' defined on value type '{1}' cannot be used to create delegates diagnostics.Add( ErrorCode.ERR_ValueTypeExtDelegate, expr.Syntax.Location, method, thisParameter.Type); hasErrors = true; } } else if (method.OriginalDefinition.ContainingType.SpecialType == SpecialType.System_Nullable_T && !method.IsOverride) { // CS1728: Cannot bind delegate to '{0}' because it is a member of 'System.Nullable<T>' diagnostics.Add( ErrorCode.ERR_DelegateOnNullable, expr.Syntax.Location, method); hasErrors = true; } } else if (!hasErrors && !resolution.IsEmpty && resolution.ResultKind == LookupResultKind.Viable) { var overloadDiagnostics = DiagnosticBag.GetInstance(); result.ReportDiagnostics(binder, expr.Syntax.Location, overloadDiagnostics, expr.Name, resolution.MethodGroup.Receiver, resolution.AnalyzedArguments, resolution.MethodGroup.Methods.ToImmutable(), typeContainingConstructor: null, delegateTypeBeingInvoked: null, isMethodGroupConversion: true); if (!overloadDiagnostics.IsEmptyWithoutResolution) { hasErrors = overloadDiagnostics.HasAnyErrors(); diagnostics.AddRange(overloadDiagnostics); } overloadDiagnostics.Free(); } } } resolution.Free(); return(hasErrors); }
/// <summary> /// Builds a delegate that will execute just this scripts code. /// </summary> public Func<object[], object> Build( Script script, DiagnosticBag diagnostics, CancellationToken cancellationToken) { var compilation = script.GetCompilation(); var options = script.Options; DiagnosticBag emitDiagnostics = DiagnosticBag.GetInstance(); byte[] compiledAssemblyImage; MethodInfo entryPoint; bool success = compilation.Emit( GetOrCreateDynamicModule(options.IsCollectible), assemblyLoader: GetAssemblyLoader(options.IsCollectible), assemblySymbolMapper: symbol => MapAssemblySymbol(symbol, options.IsCollectible), recoverOnError: true, diagnostics: emitDiagnostics, cancellationToken: cancellationToken, entryPoint: out entryPoint, compiledAssemblyImage: out compiledAssemblyImage ); if (diagnostics != null) { diagnostics.AddRange(emitDiagnostics); } bool hadEmitErrors = emitDiagnostics.HasAnyErrors(); emitDiagnostics.Free(); // emit can fail due to compilation errors or because there is nothing to emit: if (!success) { return null; } Debug.Assert(entryPoint != null); if (compiledAssemblyImage != null) { // Ref.Emit wasn't able to emit the assembly _uncollectibleCodeManager.AddFallBackAssembly(entryPoint.DeclaringType.Assembly); } #if DEBUG if (SaveCompiledAssemblies) { _uncollectibleCodeManager.Save(UncollectibleModuleFileName); _collectibleCodeManager.Save(CollectibleModuleFileName); } #endif return (Func<object[], object>)Delegate.CreateDelegate(typeof(Func<object[], object>), entryPoint); }
private static BoundExpression CreateAnonymousFunctionConversion(CSharpSyntaxNode syntax, BoundExpression source, Conversion conversion, bool isCast, TypeSymbol destination, DiagnosticBag diagnostics) { // We have a successful anonymous function conversion; rather than producing a node // which is a conversion on top of an unbound lambda, replace it with the bound // lambda. // UNDONE: Figure out what to do about the error case, where a lambda // UNDONE: is converted to a delegate that does not match. What to surface then? var unboundLambda = (UnboundLambda)source; var boundLambda = unboundLambda.Bind((NamedTypeSymbol)destination); diagnostics.AddRange(boundLambda.Diagnostics); return new BoundConversion( syntax, boundLambda, conversion, @checked: false, explicitCastInCode: isCast, constantValueOpt: ConstantValue.NotAvailable, type: destination) { WasCompilerGenerated = source.WasCompilerGenerated }; }
private BoundExpression FinalTranslation(QueryTranslationState state, DiagnosticBag diagnostics) { Debug.Assert(state.clauses.IsEmpty()); switch (state.selectOrGroup.Kind()) { case SyntaxKind.SelectClause: { // A query expression of the form // from x in e select v // is translated into // ( e ) . Select ( x => v ) var selectClause = (SelectClauseSyntax)state.selectOrGroup; var x = state.rangeVariable; var e = state.fromExpression; var v = selectClause.Expression; var lambda = MakeQueryUnboundLambda(state.RangeVariableMap(), x, v); var result = MakeQueryInvocation(state.selectOrGroup, e, "Select", lambda, diagnostics); return(MakeQueryClause(selectClause, result, queryInvocation: result)); } case SyntaxKind.GroupClause: { // A query expression of the form // from x in e group v by k // is translated into // ( e ) . GroupBy ( x => k , x => v ) // except when v is the identifier x, the translation is // ( e ) . GroupBy ( x => k ) var groupClause = (GroupClauseSyntax)state.selectOrGroup; var x = state.rangeVariable; var e = state.fromExpression; var v = groupClause.GroupExpression; var k = groupClause.ByExpression; var vId = v as IdentifierNameSyntax; BoundCall result; var lambdaLeft = MakeQueryUnboundLambda(state.RangeVariableMap(), x, k); // this is the unoptimized form (when v is not the identifier x) var d = DiagnosticBag.GetInstance(); BoundExpression lambdaRight = MakeQueryUnboundLambda(state.RangeVariableMap(), x, v); result = MakeQueryInvocation(state.selectOrGroup, e, "GroupBy", ImmutableArray.Create(lambdaLeft, lambdaRight), d); // k and v appear reversed in the invocation, so we reorder their evaluation result = ReverseLastTwoParameterOrder(result); BoundExpression unoptimizedForm = null; if (vId != null && vId.Identifier.ValueText == x.Name) { // The optimized form. We store the unoptimized form for analysis unoptimizedForm = result; result = MakeQueryInvocation(state.selectOrGroup, e, "GroupBy", lambdaLeft, diagnostics); if (unoptimizedForm.HasAnyErrors && !result.HasAnyErrors) { unoptimizedForm = null; } } else { diagnostics.AddRange(d); } d.Free(); return(MakeQueryClause(groupClause, result, queryInvocation: result, unoptimizedForm: unoptimizedForm)); } default: { // there should have been a syntax error if we get here. return(new BoundBadExpression( state.selectOrGroup, LookupResultKind.OverloadResolutionFailure, ImmutableArray <Symbol> .Empty, ImmutableArray.Create(state.fromExpression), state.fromExpression.Type)); } } }
internal static bool EmitCompilation( Compilation c, IEnumerable<ResourceDescription> manifestResources, List<ModuleData> dependencies, DiagnosticBag diagnostics, bool emitPdb, CompilationTestData testData, out ImmutableArray<byte> assembly, out ImmutableArray<byte> pdb ) { assembly = default(ImmutableArray<byte>); pdb = default(ImmutableArray<byte>); EmitReferences(c, dependencies, diagnostics, emitPdb); using (var executableStream = new MemoryStream()) { EmitResult result; MemoryStream pdbStream; string pdbFilePath; if (emitPdb) { pdbStream = new MemoryStream(); pdbFilePath = c.AssemblyName + ".pdb"; } else { pdbStream = null; pdbFilePath = null; } try { result = c.Emit( executableStream, outputName: null, pdbFilePath: pdbFilePath, pdbStream: pdbStream, xmlDocStream: null, cancellationToken: default(CancellationToken), win32Resources: null, manifestResources: manifestResources, metadataOnly: false, testData: testData); } finally { if (pdbStream != null) { pdb = pdbStream.ToImmutable(); pdbStream.Dispose(); } } diagnostics.AddRange(result.Diagnostics); assembly = executableStream.ToImmutable(); return result.Success; } }
protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression receiver, string methodName, SeparatedSyntaxList <TypeSyntax> typeArgsSyntax, ImmutableArray <TypeWithAnnotations> typeArgs, ImmutableArray <BoundExpression> args, DiagnosticBag diagnostics) { // clean up the receiver var ultimateReceiver = receiver; while (ultimateReceiver.Kind == BoundKind.QueryClause) { ultimateReceiver = ((BoundQueryClause)ultimateReceiver).Value; } if ((object)ultimateReceiver.Type == null) { if (ultimateReceiver.HasAnyErrors || node.HasErrors) { // report no additional errors } else if (ultimateReceiver.IsLiteralNull()) { diagnostics.Add(ErrorCode.ERR_NullNotValid, node.Location); } else if (ultimateReceiver.IsLiteralDefault()) { diagnostics.Add(ErrorCode.ERR_DefaultLiteralNotValid, node.Location); } else if (ultimateReceiver.Kind == BoundKind.NamespaceExpression) { diagnostics.Add(ErrorCode.ERR_BadSKunknown, ultimateReceiver.Syntax.Location, ultimateReceiver.Syntax, MessageID.IDS_SK_NAMESPACE.Localize()); } else if (ultimateReceiver.Kind == BoundKind.Lambda || ultimateReceiver.Kind == BoundKind.UnboundLambda) { // Could not find an implementation of the query pattern for source type '{0}'. '{1}' not found. diagnostics.Add(ErrorCode.ERR_QueryNoProvider, node.Location, MessageID.IDS_AnonMethod.Localize(), methodName); } else if (ultimateReceiver.Kind == BoundKind.MethodGroup) { var methodGroup = (BoundMethodGroup)ultimateReceiver; HashSet <DiagnosticInfo> useSiteDiagnostics = null; var resolution = this.ResolveMethodGroup(methodGroup, analyzedArguments: null, isMethodGroupConversion: false, useSiteDiagnostics: ref useSiteDiagnostics); diagnostics.Add(node, useSiteDiagnostics); diagnostics.AddRange(resolution.Diagnostics); if (resolution.HasAnyErrors) { receiver = this.BindMemberAccessBadResult(methodGroup); } else { Debug.Assert(!resolution.IsEmpty); diagnostics.Add(ErrorCode.ERR_QueryNoProvider, node.Location, MessageID.IDS_SK_METHOD.Localize(), methodName); } resolution.Free(); } receiver = new BoundBadExpression(receiver.Syntax, LookupResultKind.NotAValue, ImmutableArray <Symbol> .Empty, ImmutableArray.Create(receiver), CreateErrorType()); } else if (receiver.Type.SpecialType == SpecialType.System_Void) { if (!receiver.HasAnyErrors && !node.HasErrors) { diagnostics.Add(ErrorCode.ERR_QueryNoProvider, node.Location, "void", methodName); } receiver = new BoundBadExpression(receiver.Syntax, LookupResultKind.NotAValue, ImmutableArray <Symbol> .Empty, ImmutableArray.Create(receiver), CreateErrorType()); } return((BoundCall)MakeInvocationExpression( node, receiver, methodName, args, diagnostics, typeArgsSyntax, typeArgs, queryClause: node, // Queries are syntactical rewrites, so we allow fields and properties of delegate types to be invoked, // although no well-known non-generic query method is used atm. allowFieldsAndProperties: true)); }
protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression receiver, string methodName, SeparatedSyntaxList<TypeSyntax> typeArgsSyntax, ImmutableArray<TypeSymbol> typeArgs, ImmutableArray<BoundExpression> args, DiagnosticBag diagnostics) { // clean up the receiver var ultimateReceiver = receiver; while (ultimateReceiver.Kind == BoundKind.QueryClause) ultimateReceiver = ((BoundQueryClause)ultimateReceiver).Value; if ((object)ultimateReceiver.Type == null) { if (ultimateReceiver.HasAnyErrors || node.HasErrors) { // report no additional errors } else if (ultimateReceiver.IsLiteralNull()) { diagnostics.Add(ErrorCode.ERR_NullNotValid, node.Location); } else if (ultimateReceiver.Kind == BoundKind.Lambda || ultimateReceiver.Kind == BoundKind.UnboundLambda) { // Could not find an implementation of the query pattern for source type '{0}'. '{1}' not found. diagnostics.Add(ErrorCode.ERR_QueryNoProvider, node.Location, MessageID.IDS_AnonMethod.Localize(), methodName); } else if (ultimateReceiver.Kind == BoundKind.MethodGroup) { var methodGroup = (BoundMethodGroup)ultimateReceiver; HashSet<DiagnosticInfo> useSiteDiagnostics = null; var resolution = this.ResolveMethodGroup(methodGroup, analyzedArguments: null, isMethodGroupConversion: false, useSiteDiagnostics: ref useSiteDiagnostics); diagnostics.Add(node, useSiteDiagnostics); diagnostics.AddRange(resolution.Diagnostics); if (resolution.HasAnyErrors) { receiver = this.BindMemberAccessBadResult(methodGroup); } else { Debug.Assert(!resolution.IsEmpty); diagnostics.Add(ErrorCode.ERR_QueryNoProvider, node.Location, MessageID.IDS_SK_METHOD.Localize(), methodName); } resolution.Free(); } receiver = new BoundBadExpression(receiver.Syntax, LookupResultKind.NotAValue, ImmutableArray<Symbol>.Empty, ImmutableArray.Create<BoundNode>(receiver), CreateErrorType()); } else if (receiver.Type.SpecialType == SpecialType.System_Void) { if (!receiver.HasAnyErrors && !node.HasErrors) { diagnostics.Add(ErrorCode.ERR_QueryNoProvider, node.Location, "void", methodName); } receiver = new BoundBadExpression(receiver.Syntax, LookupResultKind.NotAValue, ImmutableArray<Symbol>.Empty, ImmutableArray.Create<BoundNode>(receiver), CreateErrorType()); } return (BoundCall)MakeInvocationExpression( node, receiver, methodName, args, diagnostics, typeArgsSyntax, typeArgs, queryClause: node, // Queries are syntactical rewrites, so we allow fields and properties of delegate types to be invoked, // although no well-known non-generic query method is used atm. allowFieldsAndProperties: true); }
private BoundExpression FinalTranslation(QueryTranslationState state, DiagnosticBag diagnostics) { Debug.Assert(state.clauses.IsEmpty()); switch (state.selectOrGroup.Kind()) { case SyntaxKind.SelectClause: { // A query expression of the form // from x in e select v // is translated into // ( e ) . Select ( x => v ) var selectClause = (SelectClauseSyntax)state.selectOrGroup; var x = state.rangeVariable; var e = state.fromExpression; var v = selectClause.Expression; var lambda = MakeQueryUnboundLambda(state.RangeVariableMap(), x, v); var result = MakeQueryInvocation(state.selectOrGroup, e, "Select", lambda, diagnostics); return MakeQueryClause(selectClause, result, queryInvocation: result); } case SyntaxKind.GroupClause: { // A query expression of the form // from x in e group v by k // is translated into // ( e ) . GroupBy ( x => k , x => v ) // except when v is the identifier x, the translation is // ( e ) . GroupBy ( x => k ) var groupClause = (GroupClauseSyntax)state.selectOrGroup; var x = state.rangeVariable; var e = state.fromExpression; var v = groupClause.GroupExpression; var k = groupClause.ByExpression; var vId = v as IdentifierNameSyntax; BoundCall result; var lambdaLeft = MakeQueryUnboundLambda(state.RangeVariableMap(), x, k); // this is the unoptimized form (when v is not the identifier x) var d = DiagnosticBag.GetInstance(); BoundExpression lambdaRight = MakeQueryUnboundLambda(state.RangeVariableMap(), x, v); result = MakeQueryInvocation(state.selectOrGroup, e, "GroupBy", ImmutableArray.Create(lambdaLeft, lambdaRight), d); // k and v appear reversed in the invocation, so we reorder their evaluation result = ReverseLastTwoParameterOrder(result); BoundExpression unoptimizedForm = null; if (vId != null && vId.Identifier.ValueText == x.Name) { // The optimized form. We store the unoptimized form for analysis unoptimizedForm = result; result = MakeQueryInvocation(state.selectOrGroup, e, "GroupBy", lambdaLeft, diagnostics); if (unoptimizedForm.HasAnyErrors && !result.HasAnyErrors) unoptimizedForm = null; } else { diagnostics.AddRange(d); } d.Free(); return MakeQueryClause(groupClause, result, queryInvocation: result, unoptimizedForm: unoptimizedForm); } default: { // there should have been a syntax error if we get here. return new BoundBadExpression( state.selectOrGroup, LookupResultKind.OverloadResolutionFailure, ImmutableArray<Symbol>.Empty, ImmutableArray.Create<BoundNode>(state.fromExpression), state.fromExpression.Type); } } }
/// <summary> /// Find the Deconstruct method for the expression on the right, that will fit the number of assignable variables on the left. /// Returns an invocation expression if the Deconstruct method is found. /// If so, it outputs placeholders that were coerced to the output types of the resolved Deconstruct method. /// The overload resolution is similar to writing `receiver.Deconstruct(out var x1, out var x2, ...)`. /// </summary> private BoundExpression MakeDeconstructInvocationExpression( int numCheckedVariables, BoundExpression receiver, CSharpSyntaxNode syntax, DiagnosticBag diagnostics, out ImmutableArray<BoundDeconstructValuePlaceholder> outPlaceholders) { var receiverSyntax = receiver.Syntax; if (receiver.Type.IsDynamic()) { Error(diagnostics, ErrorCode.ERR_CannotDeconstructDynamic, receiverSyntax); outPlaceholders = default(ImmutableArray<BoundDeconstructValuePlaceholder>); return BadExpression(receiverSyntax, receiver); } var analyzedArguments = AnalyzedArguments.GetInstance(); var outVars = ArrayBuilder<OutDeconstructVarPendingInference>.GetInstance(numCheckedVariables); DiagnosticBag bag = null; try { for (int i = 0; i < numCheckedVariables; i++) { var variable = new OutDeconstructVarPendingInference(syntax); analyzedArguments.Arguments.Add(variable); analyzedArguments.RefKinds.Add(RefKind.Out); outVars.Add(variable); } const string methodName = "Deconstruct"; var memberAccess = BindInstanceMemberAccess( receiverSyntax, receiverSyntax, receiver, methodName, rightArity: 0, typeArgumentsSyntax: default(SeparatedSyntaxList<TypeSyntax>), typeArguments: default(ImmutableArray<TypeSymbol>), invoked: true, diagnostics: diagnostics); memberAccess = CheckValue(memberAccess, BindValueKind.RValueOrMethodGroup, diagnostics); memberAccess.WasCompilerGenerated = true; if (memberAccess.Kind != BoundKind.MethodGroup) { return MissingDeconstruct(receiver, syntax, numCheckedVariables, diagnostics, out outPlaceholders, receiver); } // After the overload resolution completes, the last step is to coerce the arguments with inferred types. // That step returns placeholder (of correct type) instead of the outVar nodes that were passed in as arguments. // So the generated invocation expression will contain placeholders instead of those outVar nodes. // Those placeholders are also recorded in the outVar for easy access below, by the `SetInferredType` call on the outVar nodes. bag = DiagnosticBag.GetInstance(); BoundExpression result = BindMethodGroupInvocation( receiverSyntax, receiverSyntax, methodName, (BoundMethodGroup)memberAccess, analyzedArguments, bag, queryClause: null, allowUnexpandedForm: true); result.WasCompilerGenerated = true; diagnostics.AddRange(bag); if (bag.HasAnyErrors()) { return MissingDeconstruct(receiver, syntax, numCheckedVariables, diagnostics, out outPlaceholders, result); } // Verify all the parameters (except "this" for extension methods) are out parameters if (result.Kind != BoundKind.Call) { return MissingDeconstruct(receiver, syntax, numCheckedVariables, diagnostics, out outPlaceholders, result); } var deconstructMethod = ((BoundCall)result).Method; var parameters = deconstructMethod.Parameters; for (int i = (deconstructMethod.IsExtensionMethod ? 1 : 0); i < parameters.Length; i++) { if (parameters[i].RefKind != RefKind.Out) { return MissingDeconstruct(receiver, syntax, numCheckedVariables, diagnostics, out outPlaceholders, result); } } if (outVars.Any(v => (object)v.Placeholder == null)) { return MissingDeconstruct(receiver, syntax, numCheckedVariables, diagnostics, out outPlaceholders, result); } outPlaceholders = outVars.SelectAsArray(v => v.Placeholder); return result; } finally { analyzedArguments.Free(); outVars.Free(); if (bag != null) { bag.Free(); } } }
/// <summary> /// Check the expression is of the required lvalue and rvalue specified by valueKind. /// The method returns the original expression if the expression is of the required /// type. Otherwise, an appropriate error is added to the diagnostics bag and the /// method returns a BoundBadExpression node. The method returns the original /// expression without generating any error if the expression has errors. /// </summary> private BoundExpression CheckValue(BoundExpression expr, BindValueKind valueKind, DiagnosticBag diagnostics) { switch (expr.Kind) { case BoundKind.PropertyGroup: expr = BindIndexedPropertyAccess((BoundPropertyGroup)expr, mustHaveAllOptionalParameters: false, diagnostics: diagnostics); break; } bool hasResolutionErrors = false; // If this a MethodGroup where an rvalue is not expected or where the caller will not explicitly handle // (and resolve) MethodGroups (in short, cases where valueKind != BindValueKind.RValueOrMethodGroup), // resolve the MethodGroup here to generate the appropriate errors, otherwise resolution errors (such as // "member is inaccessible") will be dropped. if (expr.Kind == BoundKind.MethodGroup && valueKind != BindValueKind.RValueOrMethodGroup) { var methodGroup = (BoundMethodGroup)expr; HashSet<DiagnosticInfo> useSiteDiagnostics = null; var resolution = this.ResolveMethodGroup(methodGroup, analyzedArguments: null, isMethodGroupConversion: false, useSiteDiagnostics: ref useSiteDiagnostics); diagnostics.Add(expr.Syntax, useSiteDiagnostics); Symbol otherSymbol = null; bool resolvedToMethodGroup = resolution.MethodGroup != null; if (!expr.HasAnyErrors) diagnostics.AddRange(resolution.Diagnostics); // Suppress cascading. hasResolutionErrors = resolution.HasAnyErrors; if (hasResolutionErrors) { otherSymbol = resolution.OtherSymbol; } resolution.Free(); // It's possible the method group is not a method group at all, but simply a // delayed lookup that resolved to a non-method member (perhaps an inaccessible // field or property), or nothing at all. In those cases, the member should not be exposed as a // method group, not even within a BoundBadExpression. Instead, the // BoundBadExpression simply refers to the receiver and the resolved symbol (if any). if (!resolvedToMethodGroup) { Debug.Assert(methodGroup.ResultKind != LookupResultKind.Viable); BoundNode receiver = methodGroup.ReceiverOpt; if ((object)otherSymbol != null && receiver?.Kind == BoundKind.TypeOrValueExpression) { // Since we're not accessing a method, this can't be a Color Color case, so TypeOrValueExpression should not have been used. // CAVEAT: otherSymbol could be invalid in some way (e.g. inaccessible), in which case we would have fallen back on a // method group lookup (to allow for extension methods), which would have required a TypeOrValueExpression. Debug.Assert(methodGroup.LookupError != null); // Since we have a concrete member in hand, we can resolve the receiver. var typeOrValue = (BoundTypeOrValueExpression)receiver; receiver = otherSymbol.IsStatic ? null // no receiver required : typeOrValue.Data.ValueExpression; } return new BoundBadExpression( expr.Syntax, methodGroup.ResultKind, (object)otherSymbol == null ? ImmutableArray<Symbol>.Empty : ImmutableArray.Create(otherSymbol), receiver == null ? ImmutableArray<BoundNode>.Empty : ImmutableArray.Create(receiver), GetNonMethodMemberType(otherSymbol)); } } if (!hasResolutionErrors && CheckValueKind(expr, valueKind, diagnostics) || expr.HasAnyErrors && valueKind == BindValueKind.RValueOrMethodGroup) { return expr; } var resultKind = (valueKind == BindValueKind.RValue || valueKind == BindValueKind.RValueOrMethodGroup) ? LookupResultKind.NotAValue : LookupResultKind.NotAVariable; return ToBadExpression(expr, resultKind); }
/// <summary> /// The spec describes an algorithm for finding the following types: /// 1) Collection type /// 2) Enumerator type /// 3) Element type /// /// The implementation details are a bit difference. If we're iterating over a string or an array, then we don't need to record anything /// but the inferredType (in case the iteration variable is implicitly typed). If we're iterating over anything else, then we want the /// inferred type plus a ForEachEnumeratorInfo.Builder with: /// 1) Collection type /// 2) Element type /// 3) GetEnumerator method of the collection type (return type will be the enumerator type from the spec) /// 4) Current property of the enumerator type /// 5) MoveNext method of the enumerator type /// /// The caller will have to do some extra conversion checks before creating a ForEachEnumeratorInfo for the BoundForEachStatement. /// </summary> /// <param name="builder">Builder to fill in (partially, all but conversions).</param> /// <param name="collectionExpr">The expression over which to iterate.</param> /// <param name="diagnostics">Populated with binding diagnostics.</param> /// <returns>Partially populated (all but conversions) or null if there was an error.</returns> private bool GetEnumeratorInfo(ref ForEachEnumeratorInfo.Builder builder, BoundExpression collectionExpr, DiagnosticBag diagnostics) { TypeSymbol collectionExprType = collectionExpr.Type; if (collectionExpr.ConstantValue != null && collectionExpr.ConstantValue.IsNull) { // Spec seems to refer to null literals, but Dev10 reports anything known to be null. Debug.Assert(collectionExpr.ConstantValue.IsNull); // only constant value with no type diagnostics.Add(ErrorCode.ERR_NullNotValid, _syntax.Expression.Location); return(false); } if ((object)collectionExprType == null) // There's no way to enumerate something without a type. { // The null literal was caught above, so anything else with a null type is a method group or anonymous function diagnostics.Add(ErrorCode.ERR_AnonMethGrpInForEach, _syntax.Expression.Location, collectionExpr.Display); // CONSIDER: dev10 also reports ERR_ForEachMissingMember (i.e. failed pattern match). return(false); } if (collectionExpr.ResultKind == LookupResultKind.NotAValue) { // Short-circuiting to prevent strange behavior in the case where the collection // expression is a type expression and the type is enumerable. Debug.Assert(collectionExpr.HasAnyErrors); // should already have been reported return(false); } // The spec specifically lists the collection, enumerator, and element types for arrays and dynamic. if (collectionExprType.Kind == SymbolKind.ArrayType || collectionExprType.Kind == SymbolKind.DynamicType) { builder = GetDefaultEnumeratorInfo(builder, diagnostics, collectionExprType); return(true); } bool foundMultipleGenericIEnumerableInterfaces; if (SatisfiesGetEnumeratorPattern(ref builder, collectionExprType, diagnostics)) { Debug.Assert((object)builder.GetEnumeratorMethod != null); builder.CollectionType = collectionExprType; if (SatisfiesForEachPattern(ref builder, diagnostics)) { builder.ElementType = ((PropertySymbol)builder.CurrentPropertyGetter.AssociatedSymbol).Type; // NOTE: if IDisposable is not available at all, no diagnostics will be reported - we will just assume that // the enumerator is not disposable. If it has IDisposable in its interface list, there will be a diagnostic there. // If IDisposable is available but its Dispose method is not, then diagnostics will be reported only if the enumerator // is potentially disposable. var useSiteDiagnosticBag = DiagnosticBag.GetInstance(); TypeSymbol enumeratorType = builder.GetEnumeratorMethod.ReturnType; HashSet <DiagnosticInfo> useSiteDiagnostics = null; if (!enumeratorType.IsSealed || this.Conversions.ClassifyImplicitConversion(enumeratorType, this.Compilation.GetSpecialType(SpecialType.System_IDisposable), ref useSiteDiagnostics).IsImplicit) { builder.NeedsDisposeMethod = true; diagnostics.AddRange(useSiteDiagnosticBag); } useSiteDiagnosticBag.Free(); diagnostics.Add(_syntax, useSiteDiagnostics); return(true); } MethodSymbol getEnumeratorMethod = builder.GetEnumeratorMethod; diagnostics.Add(ErrorCode.ERR_BadGetEnumerator, _syntax.Expression.Location, getEnumeratorMethod.ReturnType, getEnumeratorMethod); return(false); } if (IsIEnumerable(collectionExprType)) { // This indicates a problem with the special IEnumerable type - it should have satisfied the GetEnumerator pattern. diagnostics.Add(ErrorCode.ERR_ForEachMissingMember, _syntax.Expression.Location, collectionExprType, GetEnumeratorMethodName); return(false); } if (AllInterfacesContainsIEnumerable(ref builder, collectionExprType, diagnostics, out foundMultipleGenericIEnumerableInterfaces)) { CSharpSyntaxNode errorLocationSyntax = _syntax.Expression; if (foundMultipleGenericIEnumerableInterfaces) { diagnostics.Add(ErrorCode.ERR_MultipleIEnumOfT, errorLocationSyntax.Location, collectionExprType, this.Compilation.GetSpecialType(SpecialType.System_Collections_Generic_IEnumerable_T)); return(false); } Debug.Assert((object)builder.CollectionType != null); NamedTypeSymbol collectionType = (NamedTypeSymbol)builder.CollectionType; if (collectionType.IsGenericType) { // If the type is generic, we have to search for the methods Debug.Assert(collectionType.OriginalDefinition.SpecialType == SpecialType.System_Collections_Generic_IEnumerable_T); builder.ElementType = collectionType.TypeArgumentsNoUseSiteDiagnostics.Single(); MethodSymbol getEnumeratorMethod = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_Generic_IEnumerable_T__GetEnumerator, diagnostics, errorLocationSyntax); if ((object)getEnumeratorMethod != null) { builder.GetEnumeratorMethod = getEnumeratorMethod.AsMember(collectionType); TypeSymbol enumeratorType = builder.GetEnumeratorMethod.ReturnType; Debug.Assert(enumeratorType.OriginalDefinition.SpecialType == SpecialType.System_Collections_Generic_IEnumerator_T); MethodSymbol currentPropertyGetter = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_Generic_IEnumerator_T__get_Current, diagnostics, errorLocationSyntax); if ((object)currentPropertyGetter != null) { builder.CurrentPropertyGetter = currentPropertyGetter.AsMember((NamedTypeSymbol)enumeratorType); } } builder.MoveNextMethod = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__MoveNext, diagnostics, errorLocationSyntax); // NOTE: MoveNext is actually inherited from System.Collections.IEnumerator } else { // Non-generic - use special members to avoid re-computing Debug.Assert(collectionType.SpecialType == SpecialType.System_Collections_IEnumerable); builder.ElementType = GetSpecialType(SpecialType.System_Object, diagnostics, errorLocationSyntax); builder.GetEnumeratorMethod = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerable__GetEnumerator, diagnostics, errorLocationSyntax); builder.CurrentPropertyGetter = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__get_Current, diagnostics, errorLocationSyntax); builder.MoveNextMethod = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__MoveNext, diagnostics, errorLocationSyntax); Debug.Assert((object)builder.GetEnumeratorMethod == null || builder.GetEnumeratorMethod.ReturnType == GetSpecialType(SpecialType.System_Collections_IEnumerator, diagnostics, errorLocationSyntax)); } // We don't know the runtime type, so we will have to insert a runtime check for IDisposable (with a conditional call to IDisposable.Dispose). builder.NeedsDisposeMethod = true; return(true); } // COMPAT: // In some rare cases, like MicroFramework, System.String does not implement foreach pattern. // For compat reasons we must still treat System.String as valid to use in a foreach // Similarly to the cases with array and dynamic, we will default to IEnumerable for binding purposes. // Lowering will not use iterator info with strings, so it is ok. if (collectionExprType.SpecialType == SpecialType.System_String) { builder = GetDefaultEnumeratorInfo(builder, diagnostics, collectionExprType); return(true); } if (!string.IsNullOrEmpty(collectionExprType.Name) || !collectionExpr.HasErrors) { diagnostics.Add(ErrorCode.ERR_ForEachMissingMember, _syntax.Expression.Location, collectionExprType.ToDisplayString(), GetEnumeratorMethodName); } return(false); }
internal static MethodSymbol GetEntryPoint(CSharpCompilation compilation, PEModuleBuilder moduleBeingBuilt, bool hasDeclarationErrors, DiagnosticBag diagnostics, CancellationToken cancellationToken) { CSharpCompilationOptions options = compilation.Options; if (!options.OutputKind.IsApplication()) { Debug.Assert(compilation.GetEntryPointAndDiagnostics(cancellationToken) == null); return compilation.IsSubmission ? DefineScriptEntryPoint(compilation, moduleBeingBuilt, compilation.GetSubmissionReturnType(), hasDeclarationErrors, diagnostics) : null; } Debug.Assert(!compilation.IsSubmission); Debug.Assert(options.OutputKind.IsApplication()); CSharpCompilation.EntryPoint entryPoint = compilation.GetEntryPointAndDiagnostics(cancellationToken); Debug.Assert(entryPoint != null); Debug.Assert(!entryPoint.Diagnostics.IsDefault); diagnostics.AddRange(entryPoint.Diagnostics); if ((object)compilation.ScriptClass != null) { Debug.Assert((object)entryPoint.MethodSymbol == null); return DefineScriptEntryPoint(compilation, moduleBeingBuilt, compilation.GetSpecialType(SpecialType.System_Void), hasDeclarationErrors, diagnostics); } Debug.Assert((object)entryPoint.MethodSymbol != null || entryPoint.Diagnostics.HasAnyErrors() || !compilation.Options.Errors.IsDefaultOrEmpty); return entryPoint.MethodSymbol; }
internal SourceFieldLikeEventSymbol(SourceMemberContainerTypeSymbol containingType, Binder binder, SyntaxTokenList modifiers, VariableDeclaratorSyntax declaratorSyntax, DiagnosticBag diagnostics) : base(containingType, declaratorSyntax, modifiers, null, declaratorSyntax.Identifier, diagnostics) { this.name = declaratorSyntax.Identifier.ValueText; var declaratorDiagnostics = DiagnosticBag.GetInstance(); var declarationSyntax = (VariableDeclarationSyntax)declaratorSyntax.Parent; this.type = BindEventType(binder, declarationSyntax.Type, declaratorDiagnostics); // The runtime will not treat the accessors of this event as overrides or implementations // of those of another event unless both the signatures and the custom modifiers match. // Hence, in the case of overrides and *explicit* implementations (not possible for field-like // events), we need to copy the custom modifiers that are in the signatures of the // overridden/implemented event accessors. (From source, we know that there can only be one // overridden/implemented event, so there are no conflicts.) This is unnecessary for implicit // implementations because, if the custom modifiers don't match, we'll insert bridge methods // for the accessors (explicit implementations that delegate to the implicit implementations) // with the correct custom modifiers (see SourceNamedTypeSymbol.ImplementInterfaceMember). // If this event is an override, we may need to copy custom modifiers from // the overridden event (so that the runtime will recognize it as an override). // We check for this case here, while we can still modify the parameters and // return type without losing the appearance of immutability. if (this.IsOverride) { EventSymbol overriddenEvent = this.OverriddenEvent; if ((object)overriddenEvent != null) { CopyEventCustomModifiers(overriddenEvent, ref this.type); } } bool hasInitializer = declaratorSyntax.Initializer != null; bool inInterfaceType = containingType.IsInterfaceType(); if (hasInitializer) { if (inInterfaceType) { diagnostics.Add(ErrorCode.ERR_InterfaceEventInitializer, this.Locations[0], this); } else if (this.IsAbstract) { diagnostics.Add(ErrorCode.ERR_AbstractEventInitializer, this.Locations[0], this); } } // NOTE: if there's an initializer in source, we'd better create a backing field, regardless of // whether or not the initializer is legal. if (hasInitializer || !(inInterfaceType || this.IsExtern || this.IsAbstract)) { this.associatedField = MakeAssociatedField(declaratorSyntax); // Don't initialize this.type - we'll just use the type of the field (which is lazy and handles var) } // Accessors will assume that Type is available. this.addMethod = new SynthesizedFieldLikeEventAccessorSymbol(this, isAdder: true); this.removeMethod = new SynthesizedFieldLikeEventAccessorSymbol(this, isAdder: false); if (declarationSyntax.Variables[0] == declaratorSyntax) { // Don't report these diagnostics for every declarator in this declaration. diagnostics.AddRange(declaratorDiagnostics); } declaratorDiagnostics.Free(); }
internal override BoundStatement BindSwitchExpressionAndSections(SwitchStatementSyntax node, Binder originalBinder, DiagnosticBag diagnostics) { Debug.Assert(SwitchSyntax.Equals(node)); // Bind switch expression and set the switch governing type. var boundSwitchExpression = this.SwitchGoverningExpression; diagnostics.AddRange(this.SwitchGoverningDiagnostics); // Switch expression might be a constant expression. // For this scenario we can determine the target label of the switch statement // at compile time. LabelSymbol constantTargetOpt = null; var constantValue = boundSwitchExpression.ConstantValue; if (constantValue != null) { constantTargetOpt = BindConstantJumpTarget(constantValue, node); } else if (!node.Sections.Any()) { // empty switch block, set the break label as target constantTargetOpt = this.BreakLabel; } // Bind switch section ImmutableArray<BoundSwitchSection> boundSwitchSections = BindSwitchSections(node.Sections, originalBinder, diagnostics); return new BoundSwitchStatement(node, null, boundSwitchExpression, constantTargetOpt, GetDeclaredLocalsForScope(node), GetDeclaredLocalFunctionsForScope(node), boundSwitchSections, this.BreakLabel, null); }
private ImmutableArray<SynthesizedExplicitImplementationForwardingMethod> ComputeInterfaceImplementations( DiagnosticBag diagnostics, CancellationToken cancellationToken) { if (this.IsInterface) { return ImmutableArray<SynthesizedExplicitImplementationForwardingMethod>.Empty; } var synthesizedImplementations = ArrayBuilder<SynthesizedExplicitImplementationForwardingMethod>.GetInstance(); // NOTE: We can't iterator over this collection directly, since it is not ordered. Instead we // iterate over AllInterfaces and filter out the interfaces that are not in this set. This is // preferable to doing the DFS ourselves because both AllInterfaces and // InterfacesAndTheirBaseInterfaces are cached and used in multiple places. ImmutableHashSet<NamedTypeSymbol> interfacesAndTheirBases = this.InterfacesAndTheirBaseInterfacesNoUseSiteDiagnostics; foreach (var @interface in this.AllInterfacesNoUseSiteDiagnostics) { cancellationToken.ThrowIfCancellationRequested(); if (!interfacesAndTheirBases.Contains(@interface)) { continue; } bool? hasImportedBaseTypeDeclaringInterface = null; foreach (var interfaceMember in @interface.GetMembersUnordered()) { cancellationToken.ThrowIfCancellationRequested(); // Only require implementations for members that can be implemented in C#. SymbolKind interfaceMemberKind = interfaceMember.Kind; switch (interfaceMemberKind) { case SymbolKind.Method: case SymbolKind.Property: case SymbolKind.Event: if (interfaceMember.IsStatic) { continue; } break; default: continue; } var implementingMemberAndDiagnostics = this.FindImplementationForInterfaceMemberWithDiagnostics(interfaceMember); var implementingMember = implementingMemberAndDiagnostics.Symbol; var synthesizedImplementation = this.SynthesizeInterfaceMemberImplementation(implementingMemberAndDiagnostics, interfaceMember); bool wasImplementingMemberFound = (object)implementingMember != null; if ((object)synthesizedImplementation != null) { synthesizedImplementations.Add(synthesizedImplementation); } if (wasImplementingMemberFound && interfaceMemberKind == SymbolKind.Event) { // NOTE: unlike dev11, we're including a related location for the implementing type, because // otherwise the only error location will be in the containing type of the implementing event // (i.e. no indication of which type's interface list is actually problematic). EventSymbol interfaceEvent = (EventSymbol)interfaceMember; EventSymbol implementingEvent = (EventSymbol)implementingMember; EventSymbol maybeWinRTEvent; EventSymbol maybeRegularEvent; if (interfaceEvent.IsWindowsRuntimeEvent) { maybeWinRTEvent = interfaceEvent; // Definitely WinRT. maybeRegularEvent = implementingEvent; // Maybe regular. } else { maybeWinRTEvent = implementingEvent; // Maybe WinRT. maybeRegularEvent = interfaceEvent; // Definitely regular. } if (interfaceEvent.IsWindowsRuntimeEvent != implementingEvent.IsWindowsRuntimeEvent) { // At this point (and not before), we know that maybeWinRTEvent is definitely a WinRT event and maybeRegularEvent is definitely a regular event. var args = new object[] { implementingEvent, interfaceEvent, maybeWinRTEvent, maybeRegularEvent }; var info = new CSDiagnosticInfo(ErrorCode.ERR_MixingWinRTEventWithRegular, args, ImmutableArray<Symbol>.Empty, ImmutableArray.Create<Location>(this.Locations[0])); diagnostics.Add(info, implementingEvent.Locations[0]); } } // Dev10: If a whole property is missing, report the property. If the property is present, but an accessor // is missing, report just the accessor. var associatedPropertyOrEvent = interfaceMemberKind == SymbolKind.Method ? ((MethodSymbol)interfaceMember).AssociatedSymbol : null; if ((object)associatedPropertyOrEvent == null || ReportAccessorOfInterfacePropertyOrEvent(associatedPropertyOrEvent) || (wasImplementingMemberFound && !implementingMember.IsAccessor())) { //we're here because //(a) the interface member is not an accessor, or //(b) the interface member is an accessor of an interesting (see ReportAccessorOfInterfacePropertyOrEvent) property or event, or //(c) the implementing member exists and is not an accessor. if (implementingMemberAndDiagnostics.Diagnostics.Any()) { diagnostics.AddRange(implementingMemberAndDiagnostics.Diagnostics); } else if (!wasImplementingMemberFound) { // NOTE: An alternative approach would be to keep track of this while searching for the implementing member. // In some cases, we might even be able to stop looking and just accept that a base type has things covered // (though we'd have to be careful about losing diagnostics and we might produce fewer bridge methods). // However, this approach has the advantage that there is no cost unless we encounter a base type that // claims to implement an interface, but we can't figure out how (i.e. free in nearly all cases). hasImportedBaseTypeDeclaringInterface = hasImportedBaseTypeDeclaringInterface ?? HasImportedBaseTypeDeclaringInterface(@interface); // If a base type from metadata declares that it implements the interface, we'll just trust it. // (See fFoundImport in SymbolPreparer::CheckInterfaceMethodImplementation.) if (!hasImportedBaseTypeDeclaringInterface.GetValueOrDefault()) { // CONSIDER: Dev10 does not emit this diagnostic for interface properties if the // derived type attempts to implement an accessor directly as a method. // Suppress for bogus properties and events and for indexed properties. if (!interfaceMember.MustCallMethodsDirectly() && !interfaceMember.IsIndexedProperty()) { DiagnosticInfo useSiteDiagnostic = interfaceMember.GetUseSiteDiagnostic(); if (useSiteDiagnostic != null && useSiteDiagnostic.DefaultSeverity == DiagnosticSeverity.Error) { diagnostics.Add(useSiteDiagnostic, GetImplementsLocation(@interface)); } else { diagnostics.Add(ErrorCode.ERR_UnimplementedInterfaceMember, GetImplementsLocation(@interface) ?? this.Locations[0], this, interfaceMember); } } } } else if (interfaceMemberKind == SymbolKind.Method) { // Don't report use site errors on properties - we'll report them on each of their accessors. // Don't report use site errors for implementations in other types unless // a synthesized implementation is needed that invokes the base method. // We can do so only if there are no use-site errors. if ((object)synthesizedImplementation != null || implementingMember.ContainingType == this) { DiagnosticInfo useSiteDiagnostic = interfaceMember.GetUseSiteDiagnostic(); // CAVEAT: don't report ERR_ByRefReturnUnsupported since by-ref return types are // specifically allowed for the purposes of interface implementation (for C++ interop). // However, if there's a reference to the interface member in source, then we do want // to produce a use site error. if (useSiteDiagnostic != null && (ErrorCode)useSiteDiagnostic.Code != ErrorCode.ERR_ByRefReturnUnsupported) { // Don't report a use site error with a location in another compilation. For example, // if the error is that a base type in another assembly implemented an interface member // on our behalf and the use site error is that the current assembly does not reference // some required assembly, then we want to report the error in the current assembly - // not in the implementing assembly. Location location = implementingMember.IsFromCompilation(this.DeclaringCompilation) ? implementingMember.Locations[0] : this.Locations[0]; Symbol.ReportUseSiteDiagnostic(useSiteDiagnostic, diagnostics, location); } } } } } } return synthesizedImplementations.ToImmutableAndFree(); }
internal void GenerateAnonymousFunctionConversionError(DiagnosticBag diagnostics, CSharpSyntaxNode syntax, UnboundLambda anonymousFunction, TypeSymbol targetType) { Debug.Assert((object)targetType != null); Debug.Assert(anonymousFunction != null); // Is the target type simply bad? // If the target type is an error then we've already reported a diagnostic. Don't bother // reporting the conversion error. if (targetType.IsErrorType() || syntax.HasErrors) { return; } // CONSIDER: Instead of computing this again, cache the reason why the conversion failed in // CONSIDER: the Conversion result, and simply report that. var reason = Conversions.IsAnonymousFunctionCompatibleWithType(anonymousFunction, targetType); // It is possible that the conversion from lambda to delegate is just fine, and // that we ended up here because the target type, though itself is not an error // type, contains a type argument which is an error type. For example, converting // (Foo foo)=>{} to Action<Foo> is a perfectly legal conversion even if Foo is undefined! // In that case we have already reported an error that Foo is undefined, so just bail out. if (reason == LambdaConversionResult.Success) { return; } var id = anonymousFunction.MessageID.Localize(); if (reason == LambdaConversionResult.BadTargetType) { if (ReportDelegateInvokeUseSiteDiagnostic(diagnostics, targetType, node: syntax)) { return; } // Cannot convert {0} to type '{1}' because it is not a delegate type Error(diagnostics, ErrorCode.ERR_AnonMethToNonDel, syntax, id, targetType); return; } if (reason == LambdaConversionResult.ExpressionTreeMustHaveDelegateTypeArgument) { Debug.Assert(targetType.IsExpressionTree()); Error(diagnostics, ErrorCode.ERR_ExpressionTreeMustHaveDelegate, syntax, ((NamedTypeSymbol)targetType).TypeArgumentsNoUseSiteDiagnostics[0]); return; } if (reason == LambdaConversionResult.ExpressionTreeFromAnonymousMethod) { Debug.Assert(targetType.IsExpressionTree()); Error(diagnostics, ErrorCode.ERR_AnonymousMethodToExpressionTree, syntax); return; } // At this point we know that we have either a delegate type or an expression type for the target. var delegateType = targetType.GetDelegateType(); // The target type is a vaid delegate or expression tree type. Is there something wrong with the // parameter list? // First off, is there a parameter list at all? if (reason == LambdaConversionResult.MissingSignatureWithOutParameter) { // COMPATIBILITY: The C# 4 compiler produces two errors for: // // delegate void D (out int x); // ... // D d = delegate {}; // // error CS1676: Parameter 1 must be declared with the 'out' keyword // error CS1688: Cannot convert anonymous method block without a parameter list // to delegate type 'D' because it has one or more out parameters // // This seems redundant, (because there is no "parameter 1" in the source code) // and unnecessary. I propose that we eliminate the first error. Error(diagnostics, ErrorCode.ERR_CantConvAnonMethNoParams, syntax, targetType); return; } // There is a parameter list. Does it have the right number of elements? if (reason == LambdaConversionResult.BadParameterCount) { // Delegate '{0}' does not take {1} arguments Error(diagnostics, ErrorCode.ERR_BadDelArgCount, syntax, targetType, anonymousFunction.ParameterCount); return; } // The parameter list exists and had the right number of parameters. Were any of its types bad? // If any parameter type of the lambda is an error type then suppress // further errors. We've already reported errors on the bad type. if (anonymousFunction.HasExplicitlyTypedParameterList) { for (int i = 0; i < anonymousFunction.ParameterCount; ++i) { if (anonymousFunction.ParameterType(i).IsErrorType()) { return; } } } // The parameter list exists and had the right number of parameters. Were any of its types // mismatched with the delegate parameter types? // The simplest possible case is (x, y, z)=>whatever where the target type has a ref or out parameter. var delegateParameters = delegateType.DelegateParameters(); if (reason == LambdaConversionResult.RefInImplicitlyTypedLambda) { for (int i = 0; i < anonymousFunction.ParameterCount; ++i) { var delegateRefKind = delegateParameters[i].RefKind; if (delegateRefKind != RefKind.None) { // Parameter {0} must be declared with the '{1}' keyword Error(diagnostics, ErrorCode.ERR_BadParamRef, anonymousFunction.ParameterLocation(i), i + 1, delegateRefKind.ToDisplayString()); } } return; } // See the comments in IsAnonymousFunctionCompatibleWithDelegate for an explanation of this one. if (reason == LambdaConversionResult.StaticTypeInImplicitlyTypedLambda) { for (int i = 0; i < anonymousFunction.ParameterCount; ++i) { if (delegateParameters[i].Type.IsStatic) { // {0}: Static types cannot be used as parameter Error(diagnostics, ErrorCode.ERR_ParameterIsStaticClass, anonymousFunction.ParameterLocation(i), delegateParameters[i].Type); } } return; } // Otherwise, there might be a more complex reason why the parameter types are mismatched. if (reason == LambdaConversionResult.MismatchedParameterType) { // Cannot convert {0} to delegate type '{1}' because the parameter types do not match the delegate parameter types Error(diagnostics, ErrorCode.ERR_CantConvAnonMethParams, syntax, id, targetType); Debug.Assert(anonymousFunction.ParameterCount == delegateParameters.Length); for (int i = 0; i < anonymousFunction.ParameterCount; ++i) { var lambdaParameterType = anonymousFunction.ParameterType(i); if (lambdaParameterType.IsErrorType()) { continue; } var lambdaParameterLocation = anonymousFunction.ParameterLocation(i); var lambdaRefKind = anonymousFunction.RefKind(i); var delegateParameterType = delegateParameters[i].Type; var delegateRefKind = delegateParameters[i].RefKind; if (!lambdaParameterType.Equals(delegateParameterType, ignoreCustomModifiers: true, ignoreDynamic: true)) { SymbolDistinguisher distinguisher = new SymbolDistinguisher(this.Compilation, lambdaParameterType, delegateParameterType); // Parameter {0} is declared as type '{1}{2}' but should be '{3}{4}' Error(diagnostics, ErrorCode.ERR_BadParamType, lambdaParameterLocation, i + 1, lambdaRefKind.ToPrefix(), distinguisher.First, delegateRefKind.ToPrefix(), distinguisher.Second); } else if (lambdaRefKind != delegateRefKind) { if (delegateRefKind == RefKind.None) { // Parameter {0} should not be declared with the '{1}' keyword Error(diagnostics, ErrorCode.ERR_BadParamExtraRef, lambdaParameterLocation, i + 1, lambdaRefKind.ToDisplayString()); } else { // Parameter {0} must be declared with the '{1}' keyword Error(diagnostics, ErrorCode.ERR_BadParamRef, lambdaParameterLocation, i + 1, delegateRefKind.ToDisplayString()); } } } return; } if (reason == LambdaConversionResult.BindingFailed) { var bindingResult = anonymousFunction.Bind(delegateType); Debug.Assert(ErrorFacts.PreventsSuccessfulDelegateConversion(bindingResult.Diagnostics)); diagnostics.AddRange(bindingResult.Diagnostics); return; } // UNDONE: LambdaConversionResult.VoidExpressionLambdaMustBeStatementExpression: Debug.Assert(false, "Missing case in lambda conversion error reporting"); }
internal override void AddDeclarationDiagnostics(DiagnosticBag diagnostics) => _declarationDiagnostics.AddRange(diagnostics);
internal static MethodBody GenerateMethodBody(TypeCompilationState compilationState, MethodSymbol method, BoundStatement block, DiagnosticBag diagnostics, bool optimize, DebugDocumentProvider debugDocumentProvider, ImmutableArray<NamespaceScope> namespaceScopes) { // Note: don't call diagnostics.HasAnyErrors() in release; could be expensive if compilation has many warnings. Debug.Assert(!diagnostics.HasAnyErrors(), "Running code generator when errors exist might be dangerous; code generator not well hardened"); bool emitSequencePoints = !namespaceScopes.IsDefault && !method.IsAsync; var module = compilationState.ModuleBuilder; var compilation = module.Compilation; var localSlotManager = module.CreateLocalSlotManager(method); ILBuilder builder = new ILBuilder(module, localSlotManager, optimize); DiagnosticBag diagnosticsForThisMethod = DiagnosticBag.GetInstance(); try { AsyncMethodBodyDebugInfo asyncDebugInfo = null; if ((object)method.AsyncKickoffMethod == null) // is this the MoveNext of an async method? { CodeGen.CodeGenerator.Run( method, block, builder, module, diagnosticsForThisMethod, optimize, emitSequencePoints); } else { int asyncCatchHandlerOffset; ImmutableArray<int> asyncYieldPoints; ImmutableArray<int> asyncResumePoints; CodeGen.CodeGenerator.Run( method, block, builder, module, diagnosticsForThisMethod, optimize, emitSequencePoints, out asyncCatchHandlerOffset, out asyncYieldPoints, out asyncResumePoints); asyncDebugInfo = new AsyncMethodBodyDebugInfo(method.AsyncKickoffMethod, asyncCatchHandlerOffset, asyncYieldPoints, asyncResumePoints); } var localVariables = builder.LocalSlotManager.LocalsInOrder(); if (localVariables.Length > 0xFFFE) { diagnosticsForThisMethod.Add(ErrorCode.ERR_TooManyLocals, method.Locations.First()); } if (diagnosticsForThisMethod.HasAnyErrors()) { // we are done here. Since there were errors we should not emit anything. return null; } // We will only save the IL builders when running tests. if (module.SaveTestData) { module.SetMethodTestData(method, builder.GetSnapshot()); } // Only compiler-generated MoveNext methods have iterator scopes. See if this is one. bool hasIteratorScopes = method.Locations.IsEmpty && method.Name == "MoveNext" && (method.ExplicitInterfaceImplementations.Contains(compilation.GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__MoveNext) as MethodSymbol) || method.ExplicitInterfaceImplementations.Contains(compilation.GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_IAsyncStateMachine_MoveNext) as MethodSymbol)); var iteratorScopes = hasIteratorScopes ? builder.GetIteratorScopes() : ImmutableArray<LocalScope>.Empty; var iteratorOrAsyncImplementation = compilationState.GetIteratorOrAsyncImplementationClass(method); return new MethodBody( builder.RealizedIL, builder.MaxStack, method, localVariables, builder.RealizedSequencePoints, debugDocumentProvider, builder.RealizedExceptionHandlers, builder.GetAllScopes(), Microsoft.Cci.CustomDebugInfoKind.CSharpStyle, builder.HasDynamicLocal, namespaceScopes, (object)iteratorOrAsyncImplementation == null ? null : iteratorOrAsyncImplementation.MetadataName, iteratorScopes, asyncMethodDebugInfo: asyncDebugInfo ); } finally { // Basic blocks contain poolable builders for IL and sequence points. Free those back // to their pools. builder.FreeBasicBlocks(); // Remember diagnostics. diagnostics.AddRange(diagnosticsForThisMethod); diagnosticsForThisMethod.Free(); } }
/// <summary> /// Parse statement. Returns null if there are any errors. /// </summary> internal static StatementSyntax ParseStatement( this string expr, DiagnosticBag diagnostics) { var syntax = ParseDebuggerStatement(expr); diagnostics.AddRange(syntax.GetDiagnostics()); return diagnostics.HasAnyErrors() ? null : syntax; }
internal static EmitOutput? EmitCompilationCore( Compilation compilation, IEnumerable<ResourceDescription> manifestResources, DiagnosticBag diagnostics, CompilationTestData testData ) { using (var executableStream = new MemoryStream()) { var pdb = default(ImmutableArray<byte>); var assembly = default(ImmutableArray<byte>); var pdbStream = MonoHelpers.IsRunningOnMono() ? null : new MemoryStream(); EmitResult result; try { result = compilation.Emit( executableStream, pdbStream: pdbStream, xmlDocumentationStream: null, win32Resources: null, manifestResources: manifestResources, options: EmitOptions.Default, debugEntryPoint: null, testData: testData, cancellationToken: default(CancellationToken)); } finally { if (pdbStream != null) { pdb = pdbStream.ToImmutable(); pdbStream.Dispose(); } } diagnostics.AddRange(result.Diagnostics); assembly = executableStream.ToImmutable(); if (result.Success) { return new EmitOutput(assembly, pdb); } return null; } }
/// <summary> /// The spec describes an algorithm for finding the following types: /// 1) Collection type /// 2) Enumerator type /// 3) Element type /// /// The implementation details are a bit difference. If we're iterating over a string or an array, then we don't need to record anything /// but the inferredType (in case the iteration variable is implicitly typed). If we're iterating over anything else, then we want the /// inferred type plus a ForEachEnumeratorInfo.Builder with: /// 1) Collection type /// 2) Element type /// 3) GetEnumerator method of the collection type (return type will be the enumerator type from the spec) /// 4) Current property of the enumerator type /// 5) MoveNext method of the enumerator type /// /// The caller will have to do some extra conversion checks before creating a ForEachEnumeratorInfo for the BoundForEachStatement. /// </summary> /// <param name="builder">Builder to fill in (partially, all but conversions).</param> /// <param name="collectionExpr">The expression over which to iterate.</param> /// <param name="diagnostics">Populated with binding diagnostics.</param> /// <returns>Partially populated (all but conversions) or null if there was an error.</returns> private bool GetEnumeratorInfo(ref ForEachEnumeratorInfo.Builder builder, BoundExpression collectionExpr, DiagnosticBag diagnostics) { TypeSymbol collectionExprType = collectionExpr.Type; if (collectionExpr.ConstantValue != null && collectionExpr.ConstantValue.IsNull) { // Spec seems to refer to null literals, but Dev10 reports anything known to be null. Debug.Assert(collectionExpr.ConstantValue.IsNull); // only constant value with no type diagnostics.Add(ErrorCode.ERR_NullNotValid, _syntax.Expression.Location); return false; } if ((object)collectionExprType == null) // There's no way to enumerate something without a type. { // The null literal was caught above, so anything else with a null type is a method group or anonymous function diagnostics.Add(ErrorCode.ERR_AnonMethGrpInForEach, _syntax.Expression.Location, collectionExpr.Display); // CONSIDER: dev10 also reports ERR_ForEachMissingMember (i.e. failed pattern match). return false; } if (collectionExpr.ResultKind == LookupResultKind.NotAValue) { // Short-circuiting to prevent strange behavior in the case where the collection // expression is a type expression and the type is enumerable. Debug.Assert(collectionExpr.HasAnyErrors); // should already have been reported return false; } // The spec specifically lists the collection, enumerator, and element types for arrays and dynamic. if (collectionExprType.Kind == SymbolKind.ArrayType || collectionExprType.Kind == SymbolKind.DynamicType) { builder = GetDefaultEnumeratorInfo(builder, diagnostics, collectionExprType); return true; } bool foundMultipleGenericIEnumerableInterfaces; if (SatisfiesGetEnumeratorPattern(ref builder, collectionExprType, diagnostics)) { Debug.Assert((object)builder.GetEnumeratorMethod != null); builder.CollectionType = collectionExprType; if (SatisfiesForEachPattern(ref builder, diagnostics)) { builder.ElementType = ((PropertySymbol)builder.CurrentPropertyGetter.AssociatedSymbol).Type; // NOTE: if IDisposable is not available at all, no diagnostics will be reported - we will just assume that // the enumerator is not disposable. If it has IDisposable in its interface list, there will be a diagnostic there. // If IDisposable is available but its Dispose method is not, then diagnostics will be reported only if the enumerator // is potentially disposable. var useSiteDiagnosticBag = DiagnosticBag.GetInstance(); TypeSymbol enumeratorType = builder.GetEnumeratorMethod.ReturnType; HashSet<DiagnosticInfo> useSiteDiagnostics = null; if (!enumeratorType.IsSealed || this.Conversions.ClassifyImplicitConversion(enumeratorType, this.Compilation.GetSpecialType(SpecialType.System_IDisposable), ref useSiteDiagnostics).IsImplicit) { builder.NeedsDisposeMethod = true; diagnostics.AddRange(useSiteDiagnosticBag); } useSiteDiagnosticBag.Free(); diagnostics.Add(_syntax, useSiteDiagnostics); return true; } MethodSymbol getEnumeratorMethod = builder.GetEnumeratorMethod; diagnostics.Add(ErrorCode.ERR_BadGetEnumerator, _syntax.Expression.Location, getEnumeratorMethod.ReturnType, getEnumeratorMethod); return false; } if (IsIEnumerable(collectionExprType)) { // This indicates a problem with the special IEnumerable type - it should have satisfied the GetEnumerator pattern. diagnostics.Add(ErrorCode.ERR_ForEachMissingMember, _syntax.Expression.Location, collectionExprType, GetEnumeratorMethodName); return false; } if (AllInterfacesContainsIEnumerable(ref builder, collectionExprType, diagnostics, out foundMultipleGenericIEnumerableInterfaces)) { CSharpSyntaxNode errorLocationSyntax = _syntax.Expression; if (foundMultipleGenericIEnumerableInterfaces) { diagnostics.Add(ErrorCode.ERR_MultipleIEnumOfT, errorLocationSyntax.Location, collectionExprType, this.Compilation.GetSpecialType(SpecialType.System_Collections_Generic_IEnumerable_T)); return false; } Debug.Assert((object)builder.CollectionType != null); NamedTypeSymbol collectionType = (NamedTypeSymbol)builder.CollectionType; if (collectionType.IsGenericType) { // If the type is generic, we have to search for the methods Debug.Assert(collectionType.OriginalDefinition.SpecialType == SpecialType.System_Collections_Generic_IEnumerable_T); builder.ElementType = collectionType.TypeArgumentsNoUseSiteDiagnostics.Single(); MethodSymbol getEnumeratorMethod = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_Generic_IEnumerable_T__GetEnumerator, diagnostics, errorLocationSyntax); if ((object)getEnumeratorMethod != null) { builder.GetEnumeratorMethod = getEnumeratorMethod.AsMember(collectionType); TypeSymbol enumeratorType = builder.GetEnumeratorMethod.ReturnType; Debug.Assert(enumeratorType.OriginalDefinition.SpecialType == SpecialType.System_Collections_Generic_IEnumerator_T); MethodSymbol currentPropertyGetter = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_Generic_IEnumerator_T__get_Current, diagnostics, errorLocationSyntax); if ((object)currentPropertyGetter != null) { builder.CurrentPropertyGetter = currentPropertyGetter.AsMember((NamedTypeSymbol)enumeratorType); } } builder.MoveNextMethod = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__MoveNext, diagnostics, errorLocationSyntax); // NOTE: MoveNext is actually inherited from System.Collections.IEnumerator } else { // Non-generic - use special members to avoid re-computing Debug.Assert(collectionType.SpecialType == SpecialType.System_Collections_IEnumerable); builder.ElementType = GetSpecialType(SpecialType.System_Object, diagnostics, errorLocationSyntax); builder.GetEnumeratorMethod = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerable__GetEnumerator, diagnostics, errorLocationSyntax); builder.CurrentPropertyGetter = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__get_Current, diagnostics, errorLocationSyntax); builder.MoveNextMethod = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__MoveNext, diagnostics, errorLocationSyntax); Debug.Assert((object)builder.GetEnumeratorMethod == null || builder.GetEnumeratorMethod.ReturnType == GetSpecialType(SpecialType.System_Collections_IEnumerator, diagnostics, errorLocationSyntax)); } // We don't know the runtime type, so we will have to insert a runtime check for IDisposable (with a conditional call to IDisposable.Dispose). builder.NeedsDisposeMethod = true; return true; } // COMPAT: // In some rare cases, like MicroFramework, System.String does not implement foreach pattern. // For compat reasons we must still treat System.String as valid to use in a foreach // Similarly to the cases with array and dynamic, we will default to IEnumerable for binding purposes. // Lowering will not use iterator info with strings, so it is ok. if (collectionExprType.SpecialType == SpecialType.System_String) { builder = GetDefaultEnumeratorInfo(builder, diagnostics, collectionExprType); return true; } if (!string.IsNullOrEmpty(collectionExprType.Name) || !collectionExpr.HasErrors) { diagnostics.Add(ErrorCode.ERR_ForEachMissingMember, _syntax.Expression.Location, collectionExprType.ToDisplayString(), GetEnumeratorMethodName); } return false; }
public static bool ReportDelegateMethodGroupDiagnostics(Binder binder, BoundMethodGroup expr, TypeSymbol targetType, DiagnosticBag diagnostics) { var invokeMethodOpt = GetDelegateInvokeMethodIfAvailable(targetType); HashSet<DiagnosticInfo> useSiteDiagnostics = null; var resolution = ResolveDelegateMethodGroup(binder, expr, invokeMethodOpt, ref useSiteDiagnostics); diagnostics.Add(expr.Syntax, useSiteDiagnostics); bool hasErrors = resolution.HasAnyErrors; diagnostics.AddRange(resolution.Diagnostics); // SPEC VIOLATION: Unfortunately, we cannot exactly implement the specification for // the scenario in which an extension method that extends a value type is converted // to a delegate. The code we generate that captures a delegate to a static method // that is "partially evaluated" with the bound-to-the-delegate first argument // requires that the first argument be of reference type. // // SPEC VIOLATION: Similarly, we cannot capture a method of Nullable<T>, because // boxing a Nullable<T> gives a T, not a boxed Nullable<T>. // // We give special error messages in these situations. if (resolution.MethodGroup != null) { var result = resolution.OverloadResolutionResult; if (result != null) { if (result.Succeeded) { var method = result.BestResult.Member; Debug.Assert((object)method != null); if (resolution.MethodGroup.IsExtensionMethodGroup) { Debug.Assert(method.IsExtensionMethod); var thisParameter = method.Parameters[0]; if (!thisParameter.Type.IsReferenceType) { // Extension method '{0}' defined on value type '{1}' cannot be used to create delegates diagnostics.Add( ErrorCode.ERR_ValueTypeExtDelegate, expr.Syntax.Location, method, thisParameter.Type); hasErrors = true; } } else if (method.OriginalDefinition.ContainingType.SpecialType == SpecialType.System_Nullable_T && !method.IsOverride) { // CS1728: Cannot bind delegate to '{0}' because it is a member of 'System.Nullable<T>' diagnostics.Add( ErrorCode.ERR_DelegateOnNullable, expr.Syntax.Location, method); hasErrors = true; } } else if (!hasErrors && !resolution.IsEmpty && resolution.ResultKind == LookupResultKind.Viable) { var overloadDiagnostics = DiagnosticBag.GetInstance(); result.ReportDiagnostics(binder, expr.Syntax.Location, overloadDiagnostics, expr.Name, resolution.MethodGroup.Receiver, resolution.AnalyzedArguments, resolution.MethodGroup.Methods.ToImmutable(), typeContainingConstructor: null, delegateTypeBeingInvoked: null, isMethodGroupConversion: true); if (!overloadDiagnostics.IsEmptyWithoutResolution) { hasErrors = overloadDiagnostics.HasAnyErrors(); diagnostics.AddRange(overloadDiagnostics); } overloadDiagnostics.Free(); } } } resolution.Free(); return hasErrors; }
/// <remarks> /// This method boils down to Rewrite(XDocument.Load(fileAttrValue).XPathSelectElements(pathAttrValue)). /// Everything else is error handling. /// </remarks> private XNode[] RewriteIncludeElement(XElement includeElement, string currentXmlFilePath, CSharpSyntaxNode originatingSyntax, out string commentMessage) { Location location = GetIncludeElementLocation(includeElement, ref currentXmlFilePath, ref originatingSyntax); Debug.Assert(originatingSyntax != null); bool diagnose = originatingSyntax.SyntaxTree.ReportDocumentationCommentDiagnostics(); if (!EnterIncludeElement(location)) { // NOTE: these must exist since we're already processed this node elsewhere in the call stack. XAttribute fileAttr = includeElement.Attribute(XName.Get(DocumentationCommentXmlNames.FileAttributeName)); XAttribute pathAttr = includeElement.Attribute(XName.Get(DocumentationCommentXmlNames.PathAttributeName)); string filePathValue = fileAttr.Value; string xpathValue = pathAttr.Value; if (diagnose) { _diagnostics.Add(ErrorCode.WRN_FailedInclude, location, filePathValue, xpathValue, new LocalizableErrorArgument(MessageID.IDS_OperationCausedStackOverflow)); } commentMessage = ErrorFacts.GetMessage(MessageID.IDS_XMLNOINCLUDE, CultureInfo.CurrentUICulture); // Don't inspect the children - we're already in a cycle. return(new XNode[] { new XComment(commentMessage), includeElement.Copy(copyAttributeAnnotations: false) }); } DiagnosticBag includeDiagnostics = DiagnosticBag.GetInstance(); try { XAttribute fileAttr = includeElement.Attribute(XName.Get(DocumentationCommentXmlNames.FileAttributeName)); XAttribute pathAttr = includeElement.Attribute(XName.Get(DocumentationCommentXmlNames.PathAttributeName)); bool hasFileAttribute = fileAttr != null; bool hasPathAttribute = pathAttr != null; if (!hasFileAttribute || !hasPathAttribute) { var subMessage = hasFileAttribute ? MessageID.IDS_XMLMISSINGINCLUDEPATH.Localize() : MessageID.IDS_XMLMISSINGINCLUDEFILE.Localize(); includeDiagnostics.Add(ErrorCode.WRN_InvalidInclude, location, subMessage); commentMessage = MakeCommentMessage(location, MessageID.IDS_XMLBADINCLUDE); return(null); } string xpathValue = pathAttr.Value; string filePathValue = fileAttr.Value; var resolver = _compilation.Options.XmlReferenceResolver; if (resolver == null) { includeDiagnostics.Add(ErrorCode.WRN_FailedInclude, location, filePathValue, xpathValue, new CodeAnalysisResourcesLocalizableErrorArgument(nameof(CodeAnalysisResources.XmlReferencesNotSupported))); commentMessage = MakeCommentMessage(location, MessageID.IDS_XMLFAILEDINCLUDE); return(null); } string resolvedFilePath = resolver.ResolveReference(filePathValue, currentXmlFilePath); if (resolvedFilePath == null) { // NOTE: same behavior as IOException. includeDiagnostics.Add(ErrorCode.WRN_FailedInclude, location, filePathValue, xpathValue, new CodeAnalysisResourcesLocalizableErrorArgument(nameof(CodeAnalysisResources.FileNotFound))); commentMessage = MakeCommentMessage(location, MessageID.IDS_XMLFAILEDINCLUDE); return(null); } if (_includedFileCache == null) { _includedFileCache = new DocumentationCommentIncludeCache(resolver); } try { XDocument doc; try { doc = _includedFileCache.GetOrMakeDocument(resolvedFilePath); } catch (IOException e) { // NOTE: same behavior as resolvedFilePath == null. includeDiagnostics.Add(ErrorCode.WRN_FailedInclude, location, filePathValue, xpathValue, e.Message); commentMessage = MakeCommentMessage(location, MessageID.IDS_XMLFAILEDINCLUDE); return(null); } Debug.Assert(doc != null); string errorMessage; bool invalidXPath; XElement[] loadedElements = XmlUtilities.TrySelectElements(doc, xpathValue, out errorMessage, out invalidXPath); if (loadedElements == null) { includeDiagnostics.Add(ErrorCode.WRN_FailedInclude, location, filePathValue, xpathValue, errorMessage); commentMessage = MakeCommentMessage(location, MessageID.IDS_XMLFAILEDINCLUDE); if (invalidXPath) { // leave the include node as is return(null); } if (location.IsInSource) { // As in Dev11, return only the comment - drop the include element. return(new XNode[] { new XComment(commentMessage) }); } else { commentMessage = null; return(Array.Empty <XNode>()); } } if (loadedElements != null && loadedElements.Length > 0) { // change the current XML file path for nodes contained in the document: XNode[] result = RewriteMany(loadedElements, resolvedFilePath, originatingSyntax); // The elements could be rewritten away if they are includes that refer to invalid // (but existing and accessible) XML files. If this occurs, behave as if we // had failed to find any XPath results (as in Dev11). if (result.Length > 0) { // NOTE: in this case, we do NOT visit the children of the include element - // they are dropped. commentMessage = null; return(result); } } commentMessage = MakeCommentMessage(location, MessageID.IDS_XMLNOINCLUDE); return(null); } catch (XmlException e) { // NOTE: invalid XML is handled differently from other errors - we don't include the include element // in the results and the location is in the included (vs includING) file. Location errorLocation = XmlLocation.Create(e, resolvedFilePath); includeDiagnostics.Add(ErrorCode.WRN_XMLParseIncludeError, errorLocation, GetDescription(e)); //NOTE: location is in included file. if (location.IsInSource) { commentMessage = string.Format(ErrorFacts.GetMessage(MessageID.IDS_XMLIGNORED2, CultureInfo.CurrentUICulture), resolvedFilePath); // As in Dev11, return only the comment - drop the include element. return(new XNode[] { new XComment(commentMessage) }); } else { commentMessage = null; return(Array.Empty <XNode>()); } } } finally { if (diagnose) { _diagnostics.AddRange(includeDiagnostics); } includeDiagnostics.Free(); LeaveIncludeElement(location); } }