/// <summary> /// This function tells whether one of our base classes implements /// the given method (which turns out, it is valid to have an interface /// implementation in a base /// </summary> bool BaseImplements(TypeSpec iface_type, MethodSpec mi, out MethodSpec base_method) { base_method = null; var base_type = container.BaseType; // // Setup filter with no return type to give better error message // about mismatch at return type when the check bellow rejects them // var parameters = mi.Parameters; MethodSpec close_match = null; while (true) { var candidates = MemberCache.FindMembers(base_type, mi.Name, false); if (candidates == null) { base_method = close_match; return(false); } MethodSpec similar_candidate = null; foreach (var candidate in candidates) { if (candidate.Kind != MemberKind.Method) { continue; } if (candidate.Arity != mi.Arity) { continue; } var candidate_param = ((MethodSpec)candidate).Parameters; if (!TypeSpecComparer.Override.IsEqual(parameters.Types, candidate_param.Types)) { continue; } bool modifiers_match = true; for (int i = 0; i < parameters.Count; ++i) { // // First check exact ref/out match // if ((parameters.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask) == (candidate_param.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask)) { continue; } modifiers_match = false; // // Different in ref/out only // if ((parameters.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask) != (candidate_param.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask)) { if (similar_candidate == null) { if (!candidate.IsPublic) { break; } if (!TypeSpecComparer.Override.IsEqual(mi.ReturnType, ((MethodSpec)candidate).ReturnType)) { break; } // It's used for ref/out ambiguity overload check similar_candidate = (MethodSpec)candidate; } continue; } similar_candidate = null; break; } if (!modifiers_match) { continue; } // // From this point the candidate is used for detailed error reporting // because it's very close match to what we are looking for // var m = (MethodSpec)candidate; if (!m.IsPublic) { if (close_match == null) { close_match = m; } continue; } if (!TypeSpecComparer.Override.IsEqual(mi.ReturnType, m.ReturnType)) { if (close_match == null) { close_match = m; } continue; } base_method = m; if (mi.IsGeneric && !Method.CheckImplementingMethodConstraints(container, m, mi)) { return(true); } } if (base_method != null) { if (similar_candidate != null) { Report.SymbolRelatedToPreviousError(similar_candidate); Report.SymbolRelatedToPreviousError(mi); Report.SymbolRelatedToPreviousError(container); Report.Warning(1956, 1, ((MemberCore)base_method.MemberDefinition).Location, "The interface method `{0}' implementation is ambiguous between following methods: `{1}' and `{2}' in type `{3}'", mi.GetSignatureForError(), base_method.GetSignatureForError(), similar_candidate.GetSignatureForError(), container.GetSignatureForError()); } break; } base_type = candidates[0].DeclaringType.BaseType; if (base_type == null) { base_method = close_match; return(false); } } if (!base_method.IsVirtual) { #if STATIC var base_builder = base_method.GetMetaInfo() as MethodBuilder; if (base_builder != null) { // // We can avoid creating a proxy if base_method can be marked 'final virtual'. This can // be done for all methods from compiled assembly // base_builder.__SetAttributes(base_builder.Attributes | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.NewSlot); return(true); } #endif DefineProxy(iface_type, base_method, mi); } return(true); }
private static MemberSpec FindDocumentedMemberNoNest( MemberCore mc, TypeSpec type, string member_name, AParametersCollection param_list, DeclSpace ds, out int warning_type, string cref, bool warn419, string name_for_error, Report Report) { warning_type = 0; // var filter = new MemberFilter (member_name, 0, MemberKind.All, param_list, null); IList <MemberSpec> found = null; while (type != null && found == null) { found = MemberCache.FindMembers(type, member_name, false); type = type.DeclaringType; } if (found == null) { return(null); } if (warn419 && found.Count > 1) { Report419(mc, name_for_error, found.ToArray(), Report); } return(found [0]); /* * if (param_list == null) { * // search for fields/events etc. * mis = TypeManager.MemberLookup (type, null, * type, MemberKind.All, * BindingRestriction.None, * member_name, null); * mis = FilterOverridenMembersOut (mis); * if (mis == null || mis.Length == 0) * return null; * if (warn419 && IsAmbiguous (mis)) * Report419 (mc, name_for_error, mis, Report); * return mis [0]; * } * * MethodSignature msig = new MethodSignature (member_name, null, param_list); * mis = FindMethodBase (type, * BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, * msig); * * if (warn419 && mis.Length > 0) { * if (IsAmbiguous (mis)) * Report419 (mc, name_for_error, mis, Report); * return mis [0]; * } * * // search for operators (whose parameters exactly * // matches with the list) and possibly report CS1581. * string oper = null; * string return_type_name = null; * if (member_name.StartsWith ("implicit operator ")) { * Operator.GetMetadataName (Operator.OpType.Implicit); * return_type_name = member_name.Substring (18).Trim (wsChars); * } * else if (member_name.StartsWith ("explicit operator ")) { * oper = Operator.GetMetadataName (Operator.OpType.Explicit); * return_type_name = member_name.Substring (18).Trim (wsChars); * } * else if (member_name.StartsWith ("operator ")) { * oper = member_name.Substring (9).Trim (wsChars); * switch (oper) { * // either unary or binary * case "+": * oper = param_list.Length == 2 ? * Operator.GetMetadataName (Operator.OpType.Addition) : * Operator.GetMetadataName (Operator.OpType.UnaryPlus); * break; * case "-": * oper = param_list.Length == 2 ? * Operator.GetMetadataName (Operator.OpType.Subtraction) : * Operator.GetMetadataName (Operator.OpType.UnaryNegation); * break; * default: * oper = Operator.GetMetadataName (oper); * if (oper != null) * break; * * warning_type = 1584; * Report.Warning (1020, 1, mc.Location, "Overloadable {0} operator is expected", param_list.Length == 2 ? "binary" : "unary"); * Report.Warning (1584, 1, mc.Location, "XML comment on `{0}' has syntactically incorrect cref attribute `{1}'", * mc.GetSignatureForError (), cref); * return null; * } * } * // here we still don't consider return type (to * // detect CS1581 or CS1002+CS1584). * msig = new MethodSignature (oper, null, param_list); * * mis = FindMethodBase (type, * BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, * msig); * if (mis.Length == 0) * return null; // CS1574 * var mi = mis [0]; * TypeSpec expected = mi is MethodSpec ? * ((MethodSpec) mi).ReturnType : * mi is PropertySpec ? * ((PropertySpec) mi).PropertyType : * null; * if (return_type_name != null) { * TypeSpec returnType = FindDocumentedType (mc, return_type_name, ds, cref, Report); * if (returnType == null || returnType != expected) { * warning_type = 1581; * Report.Warning (1581, 1, mc.Location, "Invalid return type in XML comment cref attribute `{0}'", cref); * return null; * } * } * return mis [0]; */ }
// // Processes "see" or "seealso" elements from cref attribute. // void HandleXrefCommon(MemberCore mc, XmlElement xref) { string cref = xref.GetAttribute("cref"); // when, XmlReader, "if (cref == null)" if (!xref.HasAttribute("cref")) { return; } // Nothing to be resolved the reference is marked explicitly if (cref.Length > 2 && cref [1] == ':') { return; } // Additional symbols for < and > are allowed for easier XML typing cref = cref.Replace('{', '<').Replace('}', '>'); var encoding = module.Compiler.Settings.Encoding; var s = new MemoryStream(encoding.GetBytes(cref)); var source_file = new CompilationSourceFile(doc_module, mc.Location.SourceFile); var report = new Report(doc_module.Compiler, new NullReportPrinter()); if (session == null) { session = new ParserSession { UseJayGlobalArrays = true } } ; SeekableStreamReader seekable = new SeekableStreamReader(s, encoding, session.StreamReaderBuffer); var parser = new CSharpParser(seekable, source_file, report, session); ParsedParameters = null; ParsedName = null; ParsedBuiltinType = null; ParsedOperator = null; parser.Lexer.putback_char = Tokenizer.DocumentationXref; parser.Lexer.parsing_generic_declaration_doc = true; parser.parse(); if (report.Errors > 0) { Report.Warning(1584, 1, mc.Location, "XML comment on `{0}' has syntactically incorrect cref attribute `{1}'", mc.GetSignatureForError(), cref); xref.SetAttribute("cref", "!:" + cref); return; } MemberSpec member; string prefix = null; FullNamedExpression fne = null; // // Try built-in type first because we are using ParsedName as identifier of // member names on built-in types // if (ParsedBuiltinType != null && (ParsedParameters == null || ParsedName != null)) { member = ParsedBuiltinType.Type; } else { member = null; } if (ParsedName != null || ParsedOperator.HasValue) { TypeSpec type = null; string member_name = null; if (member == null) { if (ParsedOperator.HasValue) { type = mc.CurrentType; } else if (ParsedName.Left != null) { fne = ResolveMemberName(mc, ParsedName.Left); if (fne != null) { var ns = fne as NamespaceExpression; if (ns != null) { fne = ns.LookupTypeOrNamespace(mc, ParsedName.Name, ParsedName.Arity, LookupMode.Probing, Location.Null); if (fne != null) { member = fne.Type; } } else { type = fne.Type; } } } else { fne = ResolveMemberName(mc, ParsedName); if (fne == null) { type = mc.CurrentType; } else if (ParsedParameters == null) { member = fne.Type; } else if (fne.Type.MemberDefinition == mc.CurrentType.MemberDefinition) { member_name = Constructor.ConstructorName; type = fne.Type; } } } else { type = (TypeSpec)member; member = null; } if (ParsedParameters != null) { var old_printer = mc.Module.Compiler.Report.SetPrinter(new NullReportPrinter()); try { var context = new DocumentationMemberContext(mc, ParsedName ?? MemberName.Null); foreach (var pp in ParsedParameters) { pp.Resolve(context); } } finally { mc.Module.Compiler.Report.SetPrinter(old_printer); } } if (type != null) { if (member_name == null) { member_name = ParsedOperator.HasValue ? Operator.GetMetadataName(ParsedOperator.Value) : ParsedName.Name; } int parsed_param_count; if (ParsedOperator == Operator.OpType.Explicit || ParsedOperator == Operator.OpType.Implicit) { parsed_param_count = ParsedParameters.Count - 1; } else if (ParsedParameters != null) { parsed_param_count = ParsedParameters.Count; } else { parsed_param_count = 0; } int parameters_match = -1; do { var members = MemberCache.FindMembers(type, member_name, true); if (members != null) { foreach (var m in members) { if (ParsedName != null && m.Arity != ParsedName.Arity) { continue; } if (ParsedParameters != null) { IParametersMember pm = m as IParametersMember; if (pm == null) { continue; } if (m.Kind == MemberKind.Operator && !ParsedOperator.HasValue) { continue; } var pm_params = pm.Parameters; int i; for (i = 0; i < parsed_param_count; ++i) { var pparam = ParsedParameters[i]; if (i >= pm_params.Count || pparam == null || pparam.TypeSpec == null || !TypeSpecComparer.Override.IsEqual(pparam.TypeSpec, pm_params.Types[i]) || (pparam.Modifier & Parameter.Modifier.RefOutMask) != (pm_params.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask)) { if (i > parameters_match) { parameters_match = i; } i = -1; break; } } if (i < 0) { continue; } if (ParsedOperator == Operator.OpType.Explicit || ParsedOperator == Operator.OpType.Implicit) { if (pm.MemberType != ParsedParameters[parsed_param_count].TypeSpec) { parameters_match = parsed_param_count + 1; continue; } } else { if (parsed_param_count != pm_params.Count) { continue; } } } if (member != null) { Report.Warning(419, 3, mc.Location, "Ambiguous reference in cref attribute `{0}'. Assuming `{1}' but other overloads including `{2}' have also matched", cref, member.GetSignatureForError(), m.GetSignatureForError()); break; } member = m; } } // Continue with parent type for nested types if (member == null) { type = type.DeclaringType; } else { type = null; } } while (type != null); if (member == null && parameters_match >= 0) { for (int i = parameters_match; i < parsed_param_count; ++i) { Report.Warning(1580, 1, mc.Location, "Invalid type for parameter `{0}' in XML comment cref attribute `{1}'", (i + 1).ToString(), cref); } if (parameters_match == parsed_param_count + 1) { Report.Warning(1581, 1, mc.Location, "Invalid return type in XML comment cref attribute `{0}'", cref); } } } } if (member == null) { Report.Warning(1574, 1, mc.Location, "XML comment on `{0}' has cref attribute `{1}' that could not be resolved", mc.GetSignatureForError(), cref); cref = "!:" + cref; } else if (member == InternalType.Namespace) { cref = "N:" + fne.GetSignatureForError(); } else { prefix = GetMemberDocHead(member); cref = prefix + member.GetSignatureForDocumentation(); } xref.SetAttribute("cref", cref); }
/// <summary> /// This function tells whether one of our base classes implements /// the given method (which turns out, it is valid to have an interface /// implementation in a base /// </summary> bool BaseImplements(TypeSpec iface_type, MethodSpec mi, out MethodSpec base_method) { base_method = null; bool base_can_implement = true; TypeSpec lookup_type; // // Special handling for properties/indexers which cannot have accessors // implementing an interface found in different types (e.g. current and base) // if (mi.IsAccessor && container.Interfaces != null) { bool new_implementation = false; foreach (var iface in container.Interfaces) { if (TypeSpecComparer.IsEqual(iface, iface_type)) { new_implementation = true; break; } } if (new_implementation) { MemberFilter filter; if (mi.Parameters.Count > 1) { var indexer_params = mi.Name [0] == 'g' ? mi.Parameters : IndexerSpec.CreateParametersFromSetter(mi, mi.Parameters.Count - 1); filter = new MemberFilter(MemberCache.IndexerNameAlias, 0, MemberKind.Indexer, indexer_params, null); } else { var pname = mi.Name.Substring(4); filter = MemberFilter.Property(pname, null); } var prop = MemberCache.FindMember(container.CurrentType, filter, BindingRestriction.DeclaredOnly | BindingRestriction.InstanceOnly); if (prop != null && (prop.Modifiers & Modifiers.NEW) != 0) { base_can_implement = false; } } } if (base_can_implement) { lookup_type = container.BaseType; if (lookup_type.ImplementsInterface(iface_type, false)) { return(true); } } else { lookup_type = container.CurrentType; } // // Setup filter with no return type to give better error message // about mismatch at return type when the check bellow rejects them // var parameters = mi.Parameters; MethodSpec close_match = null; while (true) { var candidates = MemberCache.FindMembers(lookup_type, mi.Name, !base_can_implement); if (candidates == null) { base_method = close_match; return(false); } MethodSpec similar_candidate = null; foreach (var candidate in candidates) { if (candidate.Kind != MemberKind.Method) { continue; } if (candidate.Arity != mi.Arity) { continue; } var candidate_param = ((MethodSpec)candidate).Parameters; if (!TypeSpecComparer.Override.IsEqual(parameters.Types, candidate_param.Types)) { continue; } bool modifiers_match = true; for (int i = 0; i < parameters.Count; ++i) { // // First check exact ref/out match // if ((parameters.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask) == (candidate_param.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask)) { continue; } modifiers_match = false; // // Different in ref/out only // if ((parameters.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask) != (candidate_param.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask)) { if (similar_candidate == null) { if (!candidate.IsPublic) { break; } if (!TypeSpecComparer.Override.IsEqual(mi.ReturnType, ((MethodSpec)candidate).ReturnType)) { break; } // It's used for ref/out ambiguity overload check similar_candidate = (MethodSpec)candidate; } continue; } similar_candidate = null; break; } if (!modifiers_match) { continue; } // // From this point the candidate is used for detailed error reporting // because it's very close match to what we are looking for // var m = (MethodSpec)candidate; if (!m.IsPublic) { if (close_match == null) { close_match = m; } continue; } if (!TypeSpecComparer.Override.IsEqual(mi.ReturnType, m.ReturnType)) { if (close_match == null) { close_match = m; } continue; } base_method = m; if (mi.IsGeneric && !Method.CheckImplementingMethodConstraints(container, m, mi)) { return(true); } } if (base_method != null) { if (similar_candidate != null) { Report.SymbolRelatedToPreviousError(similar_candidate); Report.SymbolRelatedToPreviousError(mi); Report.SymbolRelatedToPreviousError(container); Report.Warning(1956, 1, ((MemberCore)base_method.MemberDefinition).Location, "The interface method `{0}' implementation is ambiguous between following methods: `{1}' and `{2}' in type `{3}'", mi.GetSignatureForError(), base_method.GetSignatureForError(), similar_candidate.GetSignatureForError(), container.GetSignatureForError()); } break; } if (!base_can_implement) { return(false); } lookup_type = candidates[0].DeclaringType.BaseType; if (lookup_type == null) { base_method = close_match; return(false); } } if (!base_method.IsVirtual) { #if STATIC var base_builder = base_method.GetMetaInfo() as MethodBuilder; if (base_builder != null) { // // We can avoid creating a proxy if base_method can be marked 'final virtual'. This can // be done for all methods from compiled assembly // base_builder.__SetAttributes(base_builder.Attributes | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.NewSlot); return(true); } #endif DefineProxy(iface_type, base_method, mi); } return(true); }