internal void RemoveNamespacesFromResult(LookupResult result) { var correctSymbols = ArrayBuilder <Symbol> .GetInstance(); try { foreach (var s in result.Symbols) { if (s.Kind != SymbolKind.Namespace) { correctSymbols.Add(s); } } if (correctSymbols.Count != result.Symbols.Count) { result.Clear(); result.Symbols.AddRange(correctSymbols); } return; } finally { correctSymbols.Free(); } }
/// <summary> /// Look for any symbols in scope with the given name and arity. /// </summary> /// <remarks> /// Makes a second attempt if the results are not viable, in order to produce more detailed failure information (symbols and diagnostics). /// </remarks> private Binder XSLookupSymbolsWithFallback(LookupResult result, string name, int arity, ref HashSet <DiagnosticInfo> useSiteDiagnostics, ConsList <Symbol> basesBeingResolved = null, LookupOptions options = LookupOptions.Default) { Debug.Assert(options.AreValid()); // don't create diagnosis instances unless lookup fails var binder = this.LookupSymbolsInternal(result, name, arity, basesBeingResolved, options, diagnose: false, useSiteDiagnostics: ref useSiteDiagnostics); FilterResults(result, options); if (result.Kind != LookupResultKind.Viable && result.Kind != LookupResultKind.Empty) { result.Clear(); // retry to get diagnosis var otherBinder = this.LookupSymbolsInternal(result, name, arity, basesBeingResolved, options, diagnose: true, useSiteDiagnostics: ref useSiteDiagnostics); Debug.Assert(binder == otherBinder); } Debug.Assert(result.IsMultiViable || result.IsClear || result.Error != null); return(binder); }
/// <summary> /// Report appropriate diagnostics when lookup of a pattern member (i.e. GetEnumerator, Current, or MoveNext) fails. /// </summary> /// <param name="lookupResult">Failed lookup result.</param> /// <param name="patternType">Type in which member was looked up.</param> /// <param name="memberName">Name of looked up member.</param> /// <param name="warningsOnly">True if failures should result in warnings; false if they should result in errors.</param> /// <param name="diagnostics">Populated appropriately.</param> private void ReportPatternMemberLookupDiagnostics(LookupResult lookupResult, TypeSymbol patternType, string memberName, bool warningsOnly, DiagnosticBag diagnostics) { if (lookupResult.Symbols.Any()) { if (warningsOnly) { ReportEnumerableWarning(diagnostics, patternType, lookupResult.Symbols.First()); } else { lookupResult.Clear(); HashSet <DiagnosticInfo> useSiteDiagnostics = null; this.LookupMembersInType( lookupResult, patternType, memberName, arity: 0, basesBeingResolved: null, options: LookupOptions.Default, originalBinder: this, diagnose: true, useSiteDiagnostics: ref useSiteDiagnostics); diagnostics.Add(this.syntax.Expression, useSiteDiagnostics); if (lookupResult.Error != null) { diagnostics.Add(lookupResult.Error, this.syntax.Expression.Location); } } } else if (!warningsOnly) { diagnostics.Add(ErrorCode.ERR_NoSuchMember, this.syntax.Expression.Location, patternType, memberName); } }
/// <summary> /// Called after it is determined that the expression being enumerated is of a type that /// has a GetEnumerator method. Checks to see if the return type of the GetEnumerator /// method is suitable (i.e. has Current and MoveNext). /// </summary> /// <param name="builder">Must be non-null and contain a non-null GetEnumeratorMethod.</param> /// <param name="diagnostics">Will be populated with pattern diagnostics.</param> /// <returns>True if the return type has suitable members.</returns> /// <remarks> /// It seems that every failure path reports the same diagnostics, so that is left to the caller. /// </remarks> private bool SatisfiesForEachPattern(ref ForEachEnumeratorInfo.Builder builder, DiagnosticBag diagnostics) { Debug.Assert((object)builder.GetEnumeratorMethod != null); MethodSymbol getEnumeratorMethod = builder.GetEnumeratorMethod; TypeSymbol enumeratorType = getEnumeratorMethod.ReturnType; switch (enumeratorType.TypeKind) { case TypeKind.Class: case TypeKind.Struct: case TypeKind.Interface: case TypeKind.TypeParameter: // Not specifically mentioned in the spec, but consistent with Dev10. case TypeKind.DynamicType: // Not specifically mentioned in the spec, but consistent with Dev10. break; case TypeKind.Submission: // submission class is synthesized and should never appear in a foreach: throw ExceptionUtilities.UnexpectedValue(enumeratorType.TypeKind); default: return(false); } // Use a try-finally since there are many return points LookupResult lookupResult = LookupResult.GetInstance(); try { // If we searched for the accessor directly, we could reuse FindForEachPatternMethod and we // wouldn't have to mangle CurrentPropertyName. However, Dev10 searches for the property and // then extracts the accessor, so we should do the same (in case of accessors with non-standard // names). HashSet <DiagnosticInfo> useSiteDiagnostics = null; this.LookupMembersInType( lookupResult, enumeratorType, CurrentPropertyName, arity: 0, basesBeingResolved: null, options: LookupOptions.Default, // properties are not invocable - their accessors are originalBinder: this, diagnose: false, useSiteDiagnostics: ref useSiteDiagnostics); diagnostics.Add(this.syntax.Expression, useSiteDiagnostics); useSiteDiagnostics = null; if (!lookupResult.IsSingleViable) { ReportPatternMemberLookupDiagnostics(lookupResult, enumeratorType, CurrentPropertyName, warningsOnly: false, diagnostics: diagnostics); return(false); } // lookupResult.IsSingleViable above guaranteed there is exactly one symbol. Symbol lookupSymbol = lookupResult.SingleSymbolOrDefault; Debug.Assert((object)lookupSymbol != null); if (lookupSymbol.IsStatic || lookupSymbol.DeclaredAccessibility != Accessibility.Public || lookupSymbol.Kind != SymbolKind.Property) { return(false); } // NOTE: accessor can be inherited from overridden property MethodSymbol currentPropertyGetterCandidate = ((PropertySymbol)lookupSymbol).GetOwnOrInheritedGetMethod(); if ((object)currentPropertyGetterCandidate == null) { return(false); } else { bool isAccessible = this.IsAccessible(currentPropertyGetterCandidate, ref useSiteDiagnostics); diagnostics.Add(this.syntax.Expression, useSiteDiagnostics); if (!isAccessible) { // NOTE: per Dev10 and the spec, the property has to be public, but the accessor just has to be accessible return(false); } } builder.CurrentPropertyGetter = currentPropertyGetterCandidate; lookupResult.Clear(); // Reuse the same LookupResult MethodSymbol moveNextMethodCandidate = FindForEachPatternMethod(enumeratorType, MoveNextMethodName, lookupResult, warningsOnly: false, diagnostics: diagnostics); // SPEC VIOLATION: Dev10 checks the return type of the original definition, rather than the return type of the actual method. if ((object)moveNextMethodCandidate == null || moveNextMethodCandidate.IsStatic || moveNextMethodCandidate.DeclaredAccessibility != Accessibility.Public || ((MethodSymbol)moveNextMethodCandidate.OriginalDefinition).ReturnType.SpecialType != SpecialType.System_Boolean) { return(false); } builder.MoveNextMethod = moveNextMethodCandidate; return(true); } finally { lookupResult.Free(); } }
/// <summary> /// Report appropriate diagnostics when lookup of a pattern member (i.e. GetEnumerator, Current, or MoveNext) fails. /// </summary> /// <param name="lookupResult">Failed lookup result.</param> /// <param name="patternType">Type in which member was looked up.</param> /// <param name="memberName">Name of looked up member.</param> /// <param name="warningsOnly">True if failures should result in warnings; false if they should result in errors.</param> /// <param name="diagnostics">Populated appropriately.</param> private void ReportPatternMemberLookupDiagnostics(LookupResult lookupResult, TypeSymbol patternType, string memberName, bool warningsOnly, DiagnosticBag diagnostics) { if (lookupResult.Symbols.Any()) { if (warningsOnly) { ReportEnumerableWarning(diagnostics, patternType, lookupResult.Symbols.First()); } else { lookupResult.Clear(); HashSet<DiagnosticInfo> useSiteDiagnostics = null; this.LookupMembersInType( lookupResult, patternType, memberName, arity: 0, basesBeingResolved: null, options: LookupOptions.Default, originalBinder: this, diagnose: true, useSiteDiagnostics: ref useSiteDiagnostics); diagnostics.Add(_syntax.Expression, useSiteDiagnostics); if (lookupResult.Error != null) { diagnostics.Add(lookupResult.Error, _syntax.Expression.Location); } } } else if (!warningsOnly) { diagnostics.Add(ErrorCode.ERR_NoSuchMember, _syntax.Expression.Location, patternType, memberName); } }
private Binder XSLookupSymbolsInternal( LookupResult result, string name, int arity, ConsList <Symbol> basesBeingResolved, LookupOptions options, bool diagnose, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { Debug.Assert(result.IsClear); Debug.Assert(options.AreValid()); // X# looks for functions first //if (Compilation.Options.HasRuntime) { // check for function calls method calls outside the current class bool check = (options.HasFlag(LookupOptions.MustNotBeInstance) && !options.HasFlag(LookupOptions.MustNotBeMethod)); if (check) { var funcOptions = options; funcOptions |= LookupOptions.MustBeInvocableIfMember; Binder scope = this; while (scope != null) { if (scope is InContainerBinder && scope.ContainingType == null) // at the namespace level, so outside of all types { scope.LookupSymbolsInSingleBinder(result, name, arity, basesBeingResolved, funcOptions, this, diagnose, ref useSiteDiagnostics); FilterResults(result, options); if (!result.IsClear) { break; } } scope = scope.Next; } } } LookupResult functionResults = LookupResult.GetInstance(); if (!result.IsClear) { foreach (var symbol in result.Symbols) { if (symbol is MethodSymbol) { var ms = symbol as MethodSymbol; if (ms.IsStatic && ms.ContainingType.Name.EndsWith("Functions", XSharpString.Comparison)) { SingleLookupResult single = new SingleLookupResult(LookupResultKind.Viable, ms, null); functionResults.MergeEqual(single); } } } result.Clear(); } Binder binder = null; for (var scope = this; scope != null && !result.IsMultiViable; scope = scope.Next) { if (binder != null) { var tmp = LookupResult.GetInstance(); scope.LookupSymbolsInSingleBinder(tmp, name, arity, basesBeingResolved, options, this, diagnose, ref useSiteDiagnostics); FilterResults(tmp, options); result.MergeEqual(tmp); tmp.Free(); } else { scope.LookupSymbolsInSingleBinder(result, name, arity, basesBeingResolved, options, this, diagnose, ref useSiteDiagnostics); FilterResults(result, options); if (!result.IsClear) { binder = scope; } } } if (!functionResults.IsClear) { // compare the function results with the overall results found // create a list of functions and methods // function first and then the methods LookupResult mergedResults = LookupResult.GetInstance(); mergedResults.MergeEqual(functionResults); // now add the symbols from result that do not exist for (int j = 0; j < result.Symbols.Count; j++) { var sym = result.Symbols[j]; var found = false; for (int i = 0; i < mergedResults.Symbols.Count; i++) { if (sym == mergedResults.Symbols[i]) { found = true; break; } } if (!found) { SingleLookupResult single = new SingleLookupResult(LookupResultKind.Viable, sym, null); mergedResults.MergeEqual(single); } } result.Clear(); result.MergeEqual(mergedResults); } // C563 Make sure the error is generated for Inaccessible types. if (!result.IsClear && result.Kind == LookupResultKind.Inaccessible && result.Error != null) { // we only want to add this for internal fields (globals) if (result.Symbols[0].Kind == SymbolKind.Field) { if (useSiteDiagnostics == null) { useSiteDiagnostics = new HashSet <DiagnosticInfo>(); } useSiteDiagnostics.Add(result.Error); } } return(binder); }