//
// Processes "see" or "seealso" elements.
// Checks cref attribute.
//
private static void HandleXrefCommon(MemberCore mc,
DeclSpace ds, XmlElement xref, Report Report)
{
string cref = xref.GetAttribute("cref").Trim(wsChars);
// when, XmlReader, "if (cref == null)"
if (!xref.HasAttribute("cref"))
{
return;
}
if (cref.Length == 0)
{
Report.Warning(1001, 1, mc.Location, "Identifier expected");
}
// ... and continue until CS1584.
string signature; // "x:" are stripped
string name; // method invokation "(...)" are removed
string parameters; // method parameter list
// When it found '?:' ('T:' 'M:' 'F:' 'P:' 'E:' etc.),
// MS ignores not only its member kind, but also
// the entire syntax correctness. Nor it also does
// type fullname resolution i.e. "T:List(int)" is kept
// as T:List(int), not
// T:System.Collections.Generic.List<System.Int32>
if (cref.Length > 2 && cref [1] == ':')
{
return;
}
else
{
signature = cref;
}
// Also note that without "T:" any generic type
// indication fails.
int parens_pos = signature.IndexOf('(');
int brace_pos = parens_pos >= 0 ? -1 :
signature.IndexOf('[');
if (parens_pos > 0 && signature [signature.Length - 1] == ')')
{
name = signature.Substring(0, parens_pos).Trim(wsChars);
parameters = signature.Substring(parens_pos + 1, signature.Length - parens_pos - 2).Trim(wsChars);
}
else if (brace_pos > 0 && signature [signature.Length - 1] == ']')
{
name = signature.Substring(0, brace_pos).Trim(wsChars);
parameters = signature.Substring(brace_pos + 1, signature.Length - brace_pos - 2).Trim(wsChars);
}
else
{
name = signature;
parameters = null;
}
Normalize(mc, ref name, Report);
string identifier = GetBodyIdentifierFromName(name);
// Check if identifier is valid.
// This check is not necessary to mark as error, but
// csc specially reports CS1584 for wrong identifiers.
string [] name_elems = identifier.Split('.');
for (int i = 0; i < name_elems.Length; i++)
{
string nameElem = GetBodyIdentifierFromName(name_elems [i]);
if (i > 0)
{
Normalize(mc, ref nameElem, Report);
}
if (!Tokenizer.IsValidIdentifier(nameElem) &&
nameElem.IndexOf("operator") < 0)
{
Report.Warning(1584, 1, mc.Location, "XML comment on `{0}' has syntactically incorrect cref attribute `{1}'",
mc.GetSignatureForError(), cref);
xref.SetAttribute("cref", "!:" + signature);
return;
}
}
// check if parameters are valid
AParametersCollection parameter_types;
if (parameters == null)
{
parameter_types = null;
}
else if (parameters.Length == 0)
{
parameter_types = ParametersCompiled.EmptyReadOnlyParameters;
}
else
{
string [] param_list = parameters.Split(',');
var plist = new List <TypeSpec> ();
for (int i = 0; i < param_list.Length; i++)
{
string param_type_name = param_list [i].Trim(wsChars);
Normalize(mc, ref param_type_name, Report);
TypeSpec param_type = FindDocumentedType(mc, param_type_name, ds, cref, Report);
if (param_type == null)
{
Report.Warning(1580, 1, mc.Location, "Invalid type for parameter `{0}' in XML comment cref attribute `{1}'",
(i + 1).ToString(), cref);
return;
}
plist.Add(param_type);
}
parameter_types = ParametersCompiled.CreateFullyResolved(plist.ToArray());
}
TypeSpec type = FindDocumentedType(mc, name, ds, cref, Report);
if (type != null
// delegate must not be referenced with args
&& (!type.IsDelegate ||
parameter_types == null))
{
string result = GetSignatureForDoc(type)
+ (brace_pos < 0 ? String.Empty : signature.Substring(brace_pos));
xref.SetAttribute("cref", "T:" + result);
return; // a type
}
int period = name.LastIndexOf('.');
if (period > 0)
{
string typeName = name.Substring(0, period);
string member_name = name.Substring(period + 1);
string lookup_name = member_name == "this" ? MemberCache.IndexerNameAlias : member_name;
Normalize(mc, ref lookup_name, Report);
Normalize(mc, ref member_name, Report);
type = FindDocumentedType(mc, typeName, ds, cref, Report);
int warn_result;
if (type != null)
{
var mi = FindDocumentedMember(mc, type, lookup_name, parameter_types, ds, out warn_result, cref, true, name, Report);
if (warn_result > 0)
{
return;
}
if (mi != null)
{
// we cannot use 'type' directly
// to get its name, since mi
// could be from DeclaringType
// for nested types.
xref.SetAttribute("cref", GetMemberDocHead(mi) + GetSignatureForDoc(mi.DeclaringType) + "." + member_name + GetParametersFormatted(mi));
return; // a member of a type
}
}
}
else
{
int warn_result;
var mi = FindDocumentedMember(mc, ds.PartialContainer.Definition, name, parameter_types, ds, out warn_result, cref, true, name, Report);
if (warn_result > 0)
{
return;
}
if (mi != null)
{
// we cannot use 'type' directly
// to get its name, since mi
// could be from DeclaringType
// for nested types.
xref.SetAttribute("cref", GetMemberDocHead(mi) + GetSignatureForDoc(mi.DeclaringType) + "." + name + GetParametersFormatted(mi));
return; // local member name
}
}
// It still might be part of namespace name.
Namespace ns = ds.NamespaceEntry.NS.GetNamespace(name, false);
if (ns != null)
{
xref.SetAttribute("cref", "N:" + ns.GetSignatureForError());
return; // a namespace
}
if (mc.Module.GlobalRootNamespace.IsNamespace(name))
{
xref.SetAttribute("cref", "N:" + name);
return; // a namespace
}
Report.Warning(1574, 1, mc.Location, "XML comment on `{0}' has cref attribute `{1}' that could not be resolved",
mc.GetSignatureForError(), cref);
xref.SetAttribute("cref", "!:" + name);
}