// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team // // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons // to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or // substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using ICSharpCode.NRefactory.CSharp.TypeSystem; using ICSharpCode.NRefactory.CSharp.TypeSystem.ConstantValues; using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem.Implementation; namespace ICSharpCode.NRefactory.CSharp.TypeSystem { /// /// Produces type and member definitions from the DOM. /// public class TypeSystemConvertVisitor : DepthFirstAstVisitor { /// /// Version of the C# type system loader. /// Should be incremented when fixing bugs so that project contents cached on disk /// (which might be incorrect due to the bug) are re-created. /// internal const int version = 2; readonly CSharpUnresolvedFile unresolvedFile; UsingScope usingScope; CSharpUnresolvedTypeDefinition currentTypeDefinition; DefaultUnresolvedMethod currentMethod; InterningProvider interningProvider = new SimpleInterningProvider(); /// /// Gets/Sets the interning provider to use. /// The default value is a new instance. /// public InterningProvider InterningProvider { get { return interningProvider; } set { if (interningProvider == null) throw new ArgumentNullException(); interningProvider = value; } } /// /// Gets/Sets whether to ignore XML documentation. /// The default value is false. /// public bool SkipXmlDocumentation { get; set; } /// /// Creates a new TypeSystemConvertVisitor. /// /// The file name (used for DomRegions). public TypeSystemConvertVisitor(string fileName) { if (fileName == null) throw new ArgumentNullException("fileName"); this.unresolvedFile = new CSharpUnresolvedFile(); this.unresolvedFile.FileName = fileName; this.usingScope = unresolvedFile.RootUsingScope; } /// /// Creates a new TypeSystemConvertVisitor and initializes it with a given context. /// /// The parsed file to which members should be added. /// The current using scope. /// The current type definition. public TypeSystemConvertVisitor(CSharpUnresolvedFile unresolvedFile, UsingScope currentUsingScope = null, CSharpUnresolvedTypeDefinition currentTypeDefinition = null) { if (unresolvedFile == null) throw new ArgumentNullException("unresolvedFile"); this.unresolvedFile = unresolvedFile; this.usingScope = currentUsingScope ?? unresolvedFile.RootUsingScope; this.currentTypeDefinition = currentTypeDefinition; } public CSharpUnresolvedFile UnresolvedFile { get { return unresolvedFile; } } DomRegion MakeRegion(TextLocation start, TextLocation end) { return new DomRegion(unresolvedFile.FileName, start.Line, start.Column, end.Line, end.Column); } DomRegion MakeRegion(AstNode node) { if (node == null || node.IsNull) return DomRegion.Empty; else return MakeRegion(GetStartLocationAfterAttributes(node), node.EndLocation); } internal static TextLocation GetStartLocationAfterAttributes(AstNode node) { AstNode child = node.FirstChild; // Skip attributes and comments between attributes for the purpose of // getting a declaration's region. while (child != null && (child is AttributeSection || child.NodeType == NodeType.Whitespace)) child = child.NextSibling; return (child ?? node).StartLocation; } DomRegion MakeBraceRegion(AstNode node) { if (node == null || node.IsNull) return DomRegion.Empty; else return MakeRegion(node.GetChildByRole(Roles.LBrace).StartLocation, node.GetChildByRole(Roles.RBrace).EndLocation); } #region Compilation Unit public override IUnresolvedEntity VisitSyntaxTree (SyntaxTree unit) { unresolvedFile.Errors = unit.Errors; return base.VisitSyntaxTree (unit); } #endregion #region Using Declarations public override IUnresolvedEntity VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration) { usingScope.ExternAliases.Add(externAliasDeclaration.Name); return null; } public override IUnresolvedEntity VisitUsingDeclaration(UsingDeclaration usingDeclaration) { var u = ConvertTypeReference(usingDeclaration.Import, NameLookupMode.TypeInUsingDeclaration) as TypeOrNamespaceReference; if (u != null) { usingScope.Usings.Add(u); } return null; } public override IUnresolvedEntity VisitUsingAliasDeclaration(UsingAliasDeclaration usingDeclaration) { TypeOrNamespaceReference u = ConvertTypeReference(usingDeclaration.Import, NameLookupMode.TypeInUsingDeclaration) as TypeOrNamespaceReference; if (u != null) { usingScope.UsingAliases.Add(new KeyValuePair(usingDeclaration.Alias, u)); } return null; } #endregion #region Namespace Declaration public override IUnresolvedEntity VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) { DomRegion region = MakeRegion(namespaceDeclaration); UsingScope previousUsingScope = usingScope; foreach (var ident in namespaceDeclaration.Identifiers) { usingScope = new UsingScope(usingScope, ident); usingScope.Region = region; } base.VisitNamespaceDeclaration(namespaceDeclaration); unresolvedFile.UsingScopes.Add(usingScope); // add after visiting children so that nested scopes come first usingScope = previousUsingScope; return null; } #endregion #region Type Definitions CSharpUnresolvedTypeDefinition CreateTypeDefinition(string name) { CSharpUnresolvedTypeDefinition newType; if (currentTypeDefinition != null) { newType = new CSharpUnresolvedTypeDefinition(currentTypeDefinition, name); foreach (var typeParameter in currentTypeDefinition.TypeParameters) newType.TypeParameters.Add(typeParameter); currentTypeDefinition.NestedTypes.Add(newType); } else { newType = new CSharpUnresolvedTypeDefinition(usingScope, name); unresolvedFile.TopLevelTypeDefinitions.Add(newType); } newType.UnresolvedFile = unresolvedFile; newType.HasExtensionMethods = false; // gets set to true when an extension method is added return newType; } public override IUnresolvedEntity VisitTypeDeclaration(TypeDeclaration typeDeclaration) { var td = currentTypeDefinition = CreateTypeDefinition(typeDeclaration.Name); td.Region = MakeRegion(typeDeclaration); td.BodyRegion = MakeBraceRegion(typeDeclaration); AddXmlDocumentation(td, typeDeclaration); ApplyModifiers(td, typeDeclaration.Modifiers); switch (typeDeclaration.ClassType) { case ClassType.Enum: td.Kind = TypeKind.Enum; break; case ClassType.Interface: td.Kind = TypeKind.Interface; td.IsAbstract = true; // interfaces are implicitly abstract break; case ClassType.Struct: td.Kind = TypeKind.Struct; td.IsSealed = true; // enums/structs are implicitly sealed break; } ConvertAttributes(td.Attributes, typeDeclaration.Attributes); ConvertTypeParameters(td.TypeParameters, typeDeclaration.TypeParameters, typeDeclaration.Constraints, SymbolKind.TypeDefinition); foreach (AstType baseType in typeDeclaration.BaseTypes) { td.BaseTypes.Add(ConvertTypeReference(baseType, NameLookupMode.BaseTypeReference)); } foreach (EntityDeclaration member in typeDeclaration.Members) { member.AcceptVisitor(this); } currentTypeDefinition = (CSharpUnresolvedTypeDefinition)currentTypeDefinition.DeclaringTypeDefinition; td.ApplyInterningProvider(interningProvider); return td; } public override IUnresolvedEntity VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration) { var td = currentTypeDefinition = CreateTypeDefinition(delegateDeclaration.Name); td.Kind = TypeKind.Delegate; td.Region = MakeRegion(delegateDeclaration); td.BaseTypes.Add(KnownTypeReference.MulticastDelegate); AddXmlDocumentation(td, delegateDeclaration); ApplyModifiers(td, delegateDeclaration.Modifiers); td.IsSealed = true; // delegates are implicitly sealed ConvertTypeParameters(td.TypeParameters, delegateDeclaration.TypeParameters, delegateDeclaration.Constraints, SymbolKind.TypeDefinition); ITypeReference returnType = ConvertTypeReference(delegateDeclaration.ReturnType); List parameters = new List(); ConvertParameters(parameters, delegateDeclaration.Parameters); AddDefaultMethodsToDelegate(td, returnType, parameters); foreach (AttributeSection section in delegateDeclaration.Attributes) { if (section.AttributeTarget == "return") { List returnTypeAttributes = new List(); ConvertAttributes(returnTypeAttributes, section); IUnresolvedMethod invokeMethod = (IUnresolvedMethod)td.Members.Single(m => m.Name == "Invoke"); IUnresolvedMethod endInvokeMethod = (IUnresolvedMethod)td.Members.Single(m => m.Name == "EndInvoke"); foreach (IUnresolvedAttribute attr in returnTypeAttributes) { invokeMethod.ReturnTypeAttributes.Add(attr); endInvokeMethod.ReturnTypeAttributes.Add(attr); } } else { ConvertAttributes(td.Attributes, section); } } currentTypeDefinition = (CSharpUnresolvedTypeDefinition)currentTypeDefinition.DeclaringTypeDefinition; td.ApplyInterningProvider(interningProvider); return td; } static readonly IUnresolvedParameter delegateObjectParameter = MakeParameter(KnownTypeReference.Object, "object"); static readonly IUnresolvedParameter delegateIntPtrMethodParameter = MakeParameter(KnownTypeReference.IntPtr, "method"); static readonly IUnresolvedParameter delegateAsyncCallbackParameter = MakeParameter(typeof(AsyncCallback).ToTypeReference(), "callback"); static readonly IUnresolvedParameter delegateResultParameter = MakeParameter(typeof(IAsyncResult).ToTypeReference(), "result"); static IUnresolvedParameter MakeParameter(ITypeReference type, string name) { DefaultUnresolvedParameter p = new DefaultUnresolvedParameter(type, name); p.Freeze(); return p; } /// /// Adds the 'Invoke', 'BeginInvoke', 'EndInvoke' methods, and a constructor, to the . /// public static void AddDefaultMethodsToDelegate(DefaultUnresolvedTypeDefinition delegateType, ITypeReference returnType, IEnumerable parameters) { if (delegateType == null) throw new ArgumentNullException("delegateType"); if (returnType == null) throw new ArgumentNullException("returnType"); if (parameters == null) throw new ArgumentNullException("parameters"); DomRegion region = delegateType.Region; region = new DomRegion(region.FileName, region.BeginLine, region.BeginColumn); // remove end position DefaultUnresolvedMethod invoke = new DefaultUnresolvedMethod(delegateType, "Invoke"); invoke.Accessibility = Accessibility.Public; invoke.IsSynthetic = true; foreach (var p in parameters) invoke.Parameters.Add(p); invoke.ReturnType = returnType; invoke.Region = region; delegateType.Members.Add(invoke); DefaultUnresolvedMethod beginInvoke = new DefaultUnresolvedMethod(delegateType, "BeginInvoke"); beginInvoke.Accessibility = Accessibility.Public; beginInvoke.IsSynthetic = true; foreach (var p in parameters) beginInvoke.Parameters.Add(p); beginInvoke.Parameters.Add(delegateAsyncCallbackParameter); beginInvoke.Parameters.Add(delegateObjectParameter); beginInvoke.ReturnType = delegateResultParameter.Type; beginInvoke.Region = region; delegateType.Members.Add(beginInvoke); DefaultUnresolvedMethod endInvoke = new DefaultUnresolvedMethod(delegateType, "EndInvoke"); endInvoke.Accessibility = Accessibility.Public; endInvoke.IsSynthetic = true; endInvoke.Parameters.Add(delegateResultParameter); endInvoke.ReturnType = invoke.ReturnType; endInvoke.Region = region; delegateType.Members.Add(endInvoke); DefaultUnresolvedMethod ctor = new DefaultUnresolvedMethod(delegateType, ".ctor"); ctor.SymbolKind = SymbolKind.Constructor; ctor.Accessibility = Accessibility.Public; ctor.IsSynthetic = true; ctor.Parameters.Add(delegateObjectParameter); ctor.Parameters.Add(delegateIntPtrMethodParameter); ctor.ReturnType = delegateType; ctor.Region = region; delegateType.Members.Add(ctor); } #endregion #region Fields public override IUnresolvedEntity VisitFieldDeclaration(FieldDeclaration fieldDeclaration) { bool isSingleField = fieldDeclaration.Variables.Count == 1; Modifiers modifiers = fieldDeclaration.Modifiers; DefaultUnresolvedField field = null; foreach (VariableInitializer vi in fieldDeclaration.Variables) { field = new DefaultUnresolvedField(currentTypeDefinition, vi.Name); field.Region = isSingleField ? MakeRegion(fieldDeclaration) : MakeRegion(vi); field.BodyRegion = MakeRegion(vi); ConvertAttributes(field.Attributes, fieldDeclaration.Attributes); AddXmlDocumentation(field, fieldDeclaration); ApplyModifiers(field, modifiers); field.IsVolatile = (modifiers & Modifiers.Volatile) != 0; field.IsReadOnly = (modifiers & Modifiers.Readonly) != 0; field.ReturnType = ConvertTypeReference(fieldDeclaration.ReturnType); if ((modifiers & Modifiers.Const) != 0) { field.ConstantValue = ConvertConstantValue(field.ReturnType, vi.Initializer); field.IsStatic = true; } currentTypeDefinition.Members.Add(field); field.ApplyInterningProvider(interningProvider); } return isSingleField ? field : null; } public override IUnresolvedEntity VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration) { bool isSingleField = fixedFieldDeclaration.Variables.Count == 1; Modifiers modifiers = fixedFieldDeclaration.Modifiers; DefaultUnresolvedField field = null; foreach (var vi in fixedFieldDeclaration.Variables) { field = new DefaultUnresolvedField(currentTypeDefinition, vi.Name); field.Region = isSingleField ? MakeRegion(fixedFieldDeclaration) : MakeRegion(vi); field.BodyRegion = MakeRegion(vi); ConvertAttributes(field.Attributes, fixedFieldDeclaration.Attributes); AddXmlDocumentation(field, fixedFieldDeclaration); ApplyModifiers(field, modifiers); field.ReturnType = ConvertTypeReference(fixedFieldDeclaration.ReturnType); field.IsFixed = true; field.ConstantValue = ConvertConstantValue(field.ReturnType, vi.CountExpression); currentTypeDefinition.Members.Add(field); field.ApplyInterningProvider(interningProvider); } return isSingleField ? field : null; } public override IUnresolvedEntity VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration) { DefaultUnresolvedField field = new DefaultUnresolvedField(currentTypeDefinition, enumMemberDeclaration.Name); field.Region = field.BodyRegion = MakeRegion(enumMemberDeclaration); ConvertAttributes(field.Attributes, enumMemberDeclaration.Attributes); AddXmlDocumentation(field, enumMemberDeclaration); if (currentTypeDefinition.TypeParameters.Count == 0) { field.ReturnType = currentTypeDefinition; } else { ITypeReference[] typeArgs = new ITypeReference[currentTypeDefinition.TypeParameters.Count]; for (int i = 0; i < typeArgs.Length; i++) { typeArgs[i] = TypeParameterReference.Create(SymbolKind.TypeDefinition, i); } field.ReturnType = interningProvider.Intern(new ParameterizedTypeReference(currentTypeDefinition, typeArgs)); } field.Accessibility = Accessibility.Public; field.IsStatic = true; if (!enumMemberDeclaration.Initializer.IsNull) { field.ConstantValue = ConvertConstantValue(field.ReturnType, enumMemberDeclaration.Initializer); } else { DefaultUnresolvedField prevField = currentTypeDefinition.Members.LastOrDefault() as DefaultUnresolvedField; if (prevField == null || prevField.ConstantValue == null) { field.ConstantValue = ConvertConstantValue(field.ReturnType, new PrimitiveExpression(0)); } else { field.ConstantValue = interningProvider.Intern(new IncrementConstantValue(prevField.ConstantValue)); } } currentTypeDefinition.Members.Add(field); field.ApplyInterningProvider(interningProvider); return field; } #endregion #region Methods public override IUnresolvedEntity VisitMethodDeclaration(MethodDeclaration methodDeclaration) { DefaultUnresolvedMethod m = new DefaultUnresolvedMethod(currentTypeDefinition, methodDeclaration.Name); currentMethod = m; // required for resolving type parameters m.Region = MakeRegion(methodDeclaration); m.BodyRegion = MakeRegion(methodDeclaration.Body); AddXmlDocumentation(m, methodDeclaration); if (InheritsConstraints(methodDeclaration) && methodDeclaration.Constraints.Count == 0) { int index = 0; foreach (TypeParameterDeclaration tpDecl in methodDeclaration.TypeParameters) { var tp = new MethodTypeParameterWithInheritedConstraints(index++, tpDecl.Name); tp.Region = MakeRegion(tpDecl); ConvertAttributes(tp.Attributes, tpDecl.Attributes); tp.Variance = tpDecl.Variance; tp.ApplyInterningProvider(interningProvider); m.TypeParameters.Add(tp); } } else { ConvertTypeParameters(m.TypeParameters, methodDeclaration.TypeParameters, methodDeclaration.Constraints, SymbolKind.Method); } m.ReturnType = ConvertTypeReference(methodDeclaration.ReturnType); ConvertAttributes(m.Attributes, methodDeclaration.Attributes.Where(s => s.AttributeTarget != "return")); ConvertAttributes(m.ReturnTypeAttributes, methodDeclaration.Attributes.Where(s => s.AttributeTarget == "return")); ApplyModifiers(m, methodDeclaration.Modifiers); if (methodDeclaration.IsExtensionMethod) { m.IsExtensionMethod = true; currentTypeDefinition.HasExtensionMethods = true; } m.IsPartial = methodDeclaration.HasModifier(Modifiers.Partial); m.IsAsync = methodDeclaration.HasModifier(Modifiers.Async); m.HasBody = !methodDeclaration.Body.IsNull; ConvertParameters(m.Parameters, methodDeclaration.Parameters); if (!methodDeclaration.PrivateImplementationType.IsNull) { m.Accessibility = Accessibility.None; m.IsExplicitInterfaceImplementation = true; m.ExplicitInterfaceImplementations.Add( interningProvider.Intern(new DefaultMemberReference( m.SymbolKind, ConvertTypeReference(methodDeclaration.PrivateImplementationType), m.Name, m.TypeParameters.Count, GetParameterTypes(m.Parameters)))); } currentTypeDefinition.Members.Add(m); currentMethod = null; m.ApplyInterningProvider(interningProvider); return m; } IList GetParameterTypes(IList parameters) { if (parameters.Count == 0) return EmptyList.Instance; ITypeReference[] types = new ITypeReference[parameters.Count]; for (int i = 0; i < types.Length; i++) { types[i] = parameters[i].Type; } return interningProvider.InternList(types); } bool InheritsConstraints(MethodDeclaration methodDeclaration) { // overrides and explicit interface implementations inherit constraints if ((methodDeclaration.Modifiers & Modifiers.Override) == Modifiers.Override) return true; return !methodDeclaration.PrivateImplementationType.IsNull; } void ConvertTypeParameters(IList output, AstNodeCollection typeParameters, AstNodeCollection constraints, SymbolKind ownerType) { // output might be non-empty when type parameters were copied from an outer class int index = output.Count; List list = new List(); foreach (TypeParameterDeclaration tpDecl in typeParameters) { DefaultUnresolvedTypeParameter tp = new DefaultUnresolvedTypeParameter(ownerType, index++, tpDecl.Name); tp.Region = MakeRegion(tpDecl); ConvertAttributes(tp.Attributes, tpDecl.Attributes); tp.Variance = tpDecl.Variance; list.Add(tp); output.Add(tp); // tp must be added to list here so that it can be referenced by constraints } foreach (Constraint c in constraints) { foreach (var tp in list) { if (tp.Name == c.TypeParameter.Identifier) { foreach (AstType type in c.BaseTypes) { PrimitiveType primType = type as PrimitiveType; if (primType != null) { if (primType.Keyword == "new") { tp.HasDefaultConstructorConstraint = true; continue; } else if (primType.Keyword == "class") { tp.HasReferenceTypeConstraint = true; continue; } else if (primType.Keyword == "struct") { tp.HasValueTypeConstraint = true; continue; } } var lookupMode = (ownerType == SymbolKind.TypeDefinition) ? NameLookupMode.BaseTypeReference : NameLookupMode.Type; tp.Constraints.Add(ConvertTypeReference(type, lookupMode)); } break; } } } foreach (DefaultUnresolvedTypeParameter tp in list) { tp.ApplyInterningProvider(interningProvider); } } #endregion #region Operators public override IUnresolvedEntity VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) { DefaultUnresolvedMethod m = new DefaultUnresolvedMethod(currentTypeDefinition, operatorDeclaration.Name); m.SymbolKind = SymbolKind.Operator; m.Region = MakeRegion(operatorDeclaration); m.BodyRegion = MakeRegion(operatorDeclaration.Body); AddXmlDocumentation(m, operatorDeclaration); m.ReturnType = ConvertTypeReference(operatorDeclaration.ReturnType); ConvertAttributes(m.Attributes, operatorDeclaration.Attributes.Where(s => s.AttributeTarget != "return")); ConvertAttributes(m.ReturnTypeAttributes, operatorDeclaration.Attributes.Where(s => s.AttributeTarget == "return")); ApplyModifiers(m, operatorDeclaration.Modifiers); m.HasBody = !operatorDeclaration.Body.IsNull; ConvertParameters(m.Parameters, operatorDeclaration.Parameters); currentTypeDefinition.Members.Add(m); m.ApplyInterningProvider(interningProvider); return m; } #endregion #region Constructors public override IUnresolvedEntity VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) { Modifiers modifiers = constructorDeclaration.Modifiers; bool isStatic = (modifiers & Modifiers.Static) != 0; DefaultUnresolvedMethod ctor = new DefaultUnresolvedMethod(currentTypeDefinition, isStatic ? ".cctor" : ".ctor"); ctor.SymbolKind = SymbolKind.Constructor; ctor.Region = MakeRegion(constructorDeclaration); if (!constructorDeclaration.Initializer.IsNull) { ctor.BodyRegion = MakeRegion(constructorDeclaration.Initializer.StartLocation, constructorDeclaration.EndLocation); } else { ctor.BodyRegion = MakeRegion(constructorDeclaration.Body); } ctor.ReturnType = KnownTypeReference.Void; ConvertAttributes(ctor.Attributes, constructorDeclaration.Attributes); ConvertParameters(ctor.Parameters, constructorDeclaration.Parameters); AddXmlDocumentation(ctor, constructorDeclaration); ctor.HasBody = !constructorDeclaration.Body.IsNull; if (isStatic) ctor.IsStatic = true; else ApplyModifiers(ctor, modifiers); currentTypeDefinition.Members.Add(ctor); ctor.ApplyInterningProvider(interningProvider); return ctor; } #endregion #region Destructors public override IUnresolvedEntity VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) { DefaultUnresolvedMethod dtor = new DefaultUnresolvedMethod(currentTypeDefinition, "Finalize"); dtor.SymbolKind = SymbolKind.Destructor; dtor.Region = MakeRegion(destructorDeclaration); dtor.BodyRegion = MakeRegion(destructorDeclaration.Body); dtor.Accessibility = Accessibility.Protected; dtor.IsOverride = true; dtor.ReturnType = KnownTypeReference.Void; dtor.HasBody = !destructorDeclaration.Body.IsNull; ConvertAttributes(dtor.Attributes, destructorDeclaration.Attributes); AddXmlDocumentation(dtor, destructorDeclaration); currentTypeDefinition.Members.Add(dtor); dtor.ApplyInterningProvider(interningProvider); return dtor; } #endregion #region Properties / Indexers public override IUnresolvedEntity VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) { DefaultUnresolvedProperty p = new DefaultUnresolvedProperty(currentTypeDefinition, propertyDeclaration.Name); p.Region = MakeRegion(propertyDeclaration); p.BodyRegion = MakeBraceRegion(propertyDeclaration); ApplyModifiers(p, propertyDeclaration.Modifiers); p.ReturnType = ConvertTypeReference(propertyDeclaration.ReturnType); ConvertAttributes(p.Attributes, propertyDeclaration.Attributes); AddXmlDocumentation(p, propertyDeclaration); if (!propertyDeclaration.PrivateImplementationType.IsNull) { p.Accessibility = Accessibility.None; p.IsExplicitInterfaceImplementation = true; p.ExplicitInterfaceImplementations.Add(interningProvider.Intern(new DefaultMemberReference( p.SymbolKind, ConvertTypeReference(propertyDeclaration.PrivateImplementationType), p.Name))); } bool isExtern = propertyDeclaration.HasModifier(Modifiers.Extern); p.Getter = ConvertAccessor(propertyDeclaration.Getter, p, "get_", isExtern); p.Setter = ConvertAccessor(propertyDeclaration.Setter, p, "set_", isExtern); currentTypeDefinition.Members.Add(p); p.ApplyInterningProvider(interningProvider); return p; } public override IUnresolvedEntity VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) { DefaultUnresolvedProperty p = new DefaultUnresolvedProperty(currentTypeDefinition, "Item"); p.SymbolKind = SymbolKind.Indexer; p.Region = MakeRegion(indexerDeclaration); p.BodyRegion = MakeBraceRegion(indexerDeclaration); ApplyModifiers(p, indexerDeclaration.Modifiers); p.ReturnType = ConvertTypeReference(indexerDeclaration.ReturnType); ConvertAttributes(p.Attributes, indexerDeclaration.Attributes); AddXmlDocumentation(p, indexerDeclaration); ConvertParameters(p.Parameters, indexerDeclaration.Parameters); if (!indexerDeclaration.PrivateImplementationType.IsNull) { p.Accessibility = Accessibility.None; p.IsExplicitInterfaceImplementation = true; p.ExplicitInterfaceImplementations.Add(interningProvider.Intern(new DefaultMemberReference( p.SymbolKind, indexerDeclaration.PrivateImplementationType.ToTypeReference(), p.Name, 0, GetParameterTypes(p.Parameters)))); } bool isExtern = indexerDeclaration.HasModifier(Modifiers.Extern); p.Getter = ConvertAccessor(indexerDeclaration.Getter, p, "get_", isExtern); p.Setter = ConvertAccessor(indexerDeclaration.Setter, p, "set_", isExtern); currentTypeDefinition.Members.Add(p); p.ApplyInterningProvider(interningProvider); return p; } DefaultUnresolvedMethod ConvertAccessor(Accessor accessor, IUnresolvedMember p, string prefix, bool memberIsExtern) { if (accessor.IsNull) return null; var a = new DefaultUnresolvedMethod(currentTypeDefinition, prefix + p.Name); a.SymbolKind = SymbolKind.Accessor; a.AccessorOwner = p; a.Accessibility = GetAccessibility(accessor.Modifiers) ?? p.Accessibility; a.IsAbstract = p.IsAbstract; a.IsOverride = p.IsOverride; a.IsSealed = p.IsSealed; a.IsStatic = p.IsStatic; a.IsSynthetic = p.IsSynthetic; a.IsVirtual = p.IsVirtual; a.Region = MakeRegion(accessor); a.BodyRegion = MakeRegion(accessor.Body); // An accessor has no body if both are true: // a) there's no body in the code // b) the member is either abstract or extern a.HasBody = !(accessor.Body.IsNull && (p.IsAbstract || memberIsExtern)); if (p.SymbolKind == SymbolKind.Indexer) { foreach (var indexerParam in ((IUnresolvedProperty)p).Parameters) a.Parameters.Add(indexerParam); } DefaultUnresolvedParameter param = null; if (accessor.Role == PropertyDeclaration.GetterRole) { a.ReturnType = p.ReturnType; } else { param = new DefaultUnresolvedParameter(p.ReturnType, "value"); a.Parameters.Add(param); a.ReturnType = KnownTypeReference.Void; } foreach (AttributeSection section in accessor.Attributes) { if (section.AttributeTarget == "return") { ConvertAttributes(a.ReturnTypeAttributes, section); } else if (param != null && section.AttributeTarget == "param") { ConvertAttributes(param.Attributes, section); } else { ConvertAttributes(a.Attributes, section); } } if (p.IsExplicitInterfaceImplementation) { a.IsExplicitInterfaceImplementation = true; Debug.Assert(p.ExplicitInterfaceImplementations.Count == 1); a.ExplicitInterfaceImplementations.Add(interningProvider.Intern(new DefaultMemberReference( SymbolKind.Accessor, p.ExplicitInterfaceImplementations[0].DeclaringTypeReference, a.Name, 0, GetParameterTypes(a.Parameters) ))); } a.ApplyInterningProvider(interningProvider); return a; } #endregion #region Events public override IUnresolvedEntity VisitEventDeclaration(EventDeclaration eventDeclaration) { bool isSingleEvent = eventDeclaration.Variables.Count == 1; Modifiers modifiers = eventDeclaration.Modifiers; DefaultUnresolvedEvent ev = null; foreach (VariableInitializer vi in eventDeclaration.Variables) { ev = new DefaultUnresolvedEvent(currentTypeDefinition, vi.Name); ev.Region = isSingleEvent ? MakeRegion(eventDeclaration) : MakeRegion(vi); ev.BodyRegion = MakeRegion(vi); ApplyModifiers(ev, modifiers); AddXmlDocumentation(ev, eventDeclaration); ev.ReturnType = ConvertTypeReference(eventDeclaration.ReturnType); var valueParameter = new DefaultUnresolvedParameter(ev.ReturnType, "value"); ev.AddAccessor = CreateDefaultEventAccessor(ev, "add_" + ev.Name, valueParameter); ev.RemoveAccessor = CreateDefaultEventAccessor(ev, "remove_" + ev.Name, valueParameter); foreach (AttributeSection section in eventDeclaration.Attributes) { if (section.AttributeTarget == "method") { foreach (var attrNode in section.Attributes) { IUnresolvedAttribute attr = ConvertAttribute(attrNode); ev.AddAccessor.Attributes.Add(attr); ev.RemoveAccessor.Attributes.Add(attr); } } else if (section.AttributeTarget != "field") { ConvertAttributes(ev.Attributes, section); } } currentTypeDefinition.Members.Add(ev); ev.ApplyInterningProvider(interningProvider); } return isSingleEvent ? ev : null; } DefaultUnresolvedMethod CreateDefaultEventAccessor(IUnresolvedEvent ev, string name, IUnresolvedParameter valueParameter) { var a = new DefaultUnresolvedMethod(currentTypeDefinition, name); a.SymbolKind = SymbolKind.Accessor; a.AccessorOwner = ev; a.Region = ev.BodyRegion; a.BodyRegion = DomRegion.Empty; a.Accessibility = ev.Accessibility; a.IsAbstract = ev.IsAbstract; a.IsOverride = ev.IsOverride; a.IsSealed = ev.IsSealed; a.IsStatic = ev.IsStatic; a.IsSynthetic = ev.IsSynthetic; a.IsVirtual = ev.IsVirtual; a.HasBody = true; // even if it's compiler-generated; the body still exists a.ReturnType = KnownTypeReference.Void; a.Parameters.Add(valueParameter); return a; } public override IUnresolvedEntity VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration) { DefaultUnresolvedEvent e = new DefaultUnresolvedEvent(currentTypeDefinition, eventDeclaration.Name); e.Region = MakeRegion(eventDeclaration); e.BodyRegion = MakeBraceRegion(eventDeclaration); ApplyModifiers(e, eventDeclaration.Modifiers); e.ReturnType = ConvertTypeReference(eventDeclaration.ReturnType); ConvertAttributes(e.Attributes, eventDeclaration.Attributes); AddXmlDocumentation(e, eventDeclaration); if (!eventDeclaration.PrivateImplementationType.IsNull) { e.Accessibility = Accessibility.None; e.IsExplicitInterfaceImplementation = true; e.ExplicitInterfaceImplementations.Add(interningProvider.Intern(new DefaultMemberReference( e.SymbolKind, eventDeclaration.PrivateImplementationType.ToTypeReference(), e.Name))); } // custom events can't be extern; the non-custom event syntax must be used for extern events e.AddAccessor = ConvertAccessor(eventDeclaration.AddAccessor, e, "add_", false); e.RemoveAccessor = ConvertAccessor(eventDeclaration.RemoveAccessor, e, "remove_", false); currentTypeDefinition.Members.Add(e); e.ApplyInterningProvider(interningProvider); return e; } #endregion #region Modifiers static void ApplyModifiers(DefaultUnresolvedTypeDefinition td, Modifiers modifiers) { td.Accessibility = GetAccessibility(modifiers) ?? (td.DeclaringTypeDefinition != null ? Accessibility.Private : Accessibility.Internal); td.IsAbstract = (modifiers & (Modifiers.Abstract | Modifiers.Static)) != 0; td.IsSealed = (modifiers & (Modifiers.Sealed | Modifiers.Static)) != 0; td.IsShadowing = (modifiers & Modifiers.New) != 0; td.IsPartial = (modifiers & Modifiers.Partial) != 0; } static void ApplyModifiers(AbstractUnresolvedMember m, Modifiers modifiers) { // members from interfaces are always Public+Abstract. (NOTE: 'new' modifier is valid in interfaces as well.) if (m.DeclaringTypeDefinition.Kind == TypeKind.Interface) { m.Accessibility = Accessibility.Public; m.IsAbstract = true; m.IsShadowing = (modifiers & Modifiers.New) != 0; return; } m.Accessibility = GetAccessibility(modifiers) ?? Accessibility.Private; m.IsAbstract = (modifiers & Modifiers.Abstract) != 0; m.IsOverride = (modifiers & Modifiers.Override) != 0; m.IsSealed = (modifiers & Modifiers.Sealed) != 0; m.IsShadowing = (modifiers & Modifiers.New) != 0; m.IsStatic = (modifiers & Modifiers.Static) != 0; m.IsVirtual = (modifiers & Modifiers.Virtual) != 0; } static Accessibility? GetAccessibility(Modifiers modifiers) { switch (modifiers & Modifiers.VisibilityMask) { case Modifiers.Private: return Accessibility.Private; case Modifiers.Internal: return Accessibility.Internal; case Modifiers.Protected | Modifiers.Internal: return Accessibility.ProtectedOrInternal; case Modifiers.Protected: return Accessibility.Protected; case Modifiers.Public: return Accessibility.Public; default: return null; } } #endregion #region Attributes public override IUnresolvedEntity VisitAttributeSection(AttributeSection attributeSection) { // non-assembly attributes are handled by their parent entity if (attributeSection.AttributeTarget == "assembly") { ConvertAttributes(unresolvedFile.AssemblyAttributes, attributeSection); } else if (attributeSection.AttributeTarget == "module") { ConvertAttributes(unresolvedFile.ModuleAttributes, attributeSection); } return null; } void ConvertAttributes(IList outputList, IEnumerable attributes) { foreach (AttributeSection section in attributes) { ConvertAttributes(outputList, section); } } void ConvertAttributes(IList outputList, AttributeSection attributeSection) { foreach (CSharp.Attribute attr in attributeSection.Attributes) { outputList.Add(ConvertAttribute(attr)); } } internal static ITypeReference ConvertAttributeType(AstType type, InterningProvider interningProvider) { ITypeReference tr = type.ToTypeReference(NameLookupMode.Type, interningProvider); if (!type.GetChildByRole(Roles.Identifier).IsVerbatim) { // Try to add "Attribute" suffix, but only if the identifier // (=last identifier in fully qualified name) isn't a verbatim identifier. SimpleTypeOrNamespaceReference st = tr as SimpleTypeOrNamespaceReference; MemberTypeOrNamespaceReference mt = tr as MemberTypeOrNamespaceReference; if (st != null) return interningProvider.Intern(new AttributeTypeReference(st, interningProvider.Intern(st.AddSuffix("Attribute")))); else if (mt != null) return interningProvider.Intern(new AttributeTypeReference(mt, interningProvider.Intern(mt.AddSuffix("Attribute")))); } return tr; } CSharpAttribute ConvertAttribute(CSharp.Attribute attr) { DomRegion region = MakeRegion(attr); ITypeReference type = ConvertAttributeType(attr.Type, interningProvider); List positionalArguments = null; List> namedCtorArguments = null; List> namedArguments = null; foreach (Expression expr in attr.Arguments) { NamedArgumentExpression nae = expr as NamedArgumentExpression; if (nae != null) { string name = interningProvider.Intern(nae.Name); if (namedCtorArguments == null) namedCtorArguments = new List>(); namedCtorArguments.Add(new KeyValuePair(name, ConvertAttributeArgument(nae.Expression))); } else { NamedExpression namedExpression = expr as NamedExpression; if (namedExpression != null) { string name = interningProvider.Intern(namedExpression.Name); if (namedArguments == null) namedArguments = new List>(); namedArguments.Add(new KeyValuePair(name, ConvertAttributeArgument(namedExpression.Expression))); } else { if (positionalArguments == null) positionalArguments = new List(); positionalArguments.Add(ConvertAttributeArgument(expr)); } } } return new CSharpAttribute(type, region, interningProvider.InternList(positionalArguments), namedCtorArguments, namedArguments); } #endregion #region Types ITypeReference ConvertTypeReference(AstType type, NameLookupMode lookupMode = NameLookupMode.Type) { return type.ToTypeReference(lookupMode, interningProvider); } #endregion #region Constant Values IConstantValue ConvertConstantValue(ITypeReference targetType, AstNode expression) { return ConvertConstantValue(targetType, expression, currentTypeDefinition, currentMethod, usingScope, interningProvider); } internal static IConstantValue ConvertConstantValue( ITypeReference targetType, AstNode expression, IUnresolvedTypeDefinition parentTypeDefinition, IUnresolvedMethod parentMethodDefinition, UsingScope parentUsingScope, InterningProvider interningProvider) { ConstantValueBuilder b = new ConstantValueBuilder(false, interningProvider); ConstantExpression c = expression.AcceptVisitor(b); if (c == null) return new ErrorConstantValue(targetType); PrimitiveConstantExpression pc = c as PrimitiveConstantExpression; if (pc != null && pc.Type == targetType) { // Save memory by directly using a SimpleConstantValue. return interningProvider.Intern(new SimpleConstantValue(targetType, pc.Value)); } // cast to the desired type return interningProvider.Intern(new ConstantCast(targetType, c, true)); } IConstantValue ConvertAttributeArgument(Expression expression) { ConstantValueBuilder b = new ConstantValueBuilder(true, interningProvider); return expression.AcceptVisitor(b); } sealed class ConstantValueBuilder : DepthFirstAstVisitor { readonly InterningProvider interningProvider; readonly bool isAttributeArgument; public ConstantValueBuilder(bool isAttributeArgument, InterningProvider interningProvider) { this.interningProvider = interningProvider; this.isAttributeArgument = isAttributeArgument; } protected override ConstantExpression VisitChildren(AstNode node) { return null; } public override ConstantExpression VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression) { return interningProvider.Intern( new PrimitiveConstantExpression(KnownTypeReference.Object, null)); } public override ConstantExpression VisitSizeOfExpression(SizeOfExpression sizeOfExpression) { return new SizeOfConstantValue(sizeOfExpression.Type.ToTypeReference(NameLookupMode.Type, interningProvider)); } public override ConstantExpression VisitPrimitiveExpression(PrimitiveExpression primitiveExpression) { object val = interningProvider.InternValue(primitiveExpression.Value); TypeCode typeCode = (val == null ? TypeCode.Object : Type.GetTypeCode(val.GetType())); return interningProvider.Intern( new PrimitiveConstantExpression(typeCode.ToTypeReference(), val)); } ITypeReference ConvertTypeReference(AstType type) { return type.ToTypeReference(NameLookupMode.Type, interningProvider); } IList ConvertTypeArguments(AstNodeCollection types) { int count = types.Count; if (count == 0) return null; ITypeReference[] result = new ITypeReference[count]; int pos = 0; foreach (AstType type in types) { result[pos++] = ConvertTypeReference(type); } return interningProvider.InternList(result); } public override ConstantExpression VisitIdentifierExpression(IdentifierExpression identifierExpression) { string identifier = interningProvider.Intern(identifierExpression.Identifier); return new ConstantIdentifierReference(identifier, ConvertTypeArguments(identifierExpression.TypeArguments)); } public override ConstantExpression VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) { string memberName = interningProvider.Intern(memberReferenceExpression.MemberName); TypeReferenceExpression tre = memberReferenceExpression.Target as TypeReferenceExpression; if (tre != null) { // handle "int.MaxValue" return new ConstantMemberReference( ConvertTypeReference(tre.Type), memberName, ConvertTypeArguments(memberReferenceExpression.TypeArguments)); } ConstantExpression v = memberReferenceExpression.Target.AcceptVisitor(this); if (v == null) return null; return new ConstantMemberReference( v, memberName, ConvertTypeArguments(memberReferenceExpression.TypeArguments)); } public override ConstantExpression VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression) { return parenthesizedExpression.Expression.AcceptVisitor(this); } public override ConstantExpression VisitCastExpression(CastExpression castExpression) { ConstantExpression v = castExpression.Expression.AcceptVisitor(this); if (v == null) return null; var typeReference = ConvertTypeReference(castExpression.Type); return interningProvider.Intern(new ConstantCast(typeReference, v, false)); } public override ConstantExpression VisitCheckedExpression(CheckedExpression checkedExpression) { ConstantExpression v = checkedExpression.Expression.AcceptVisitor(this); if (v != null) return new ConstantCheckedExpression(true, v); else return null; } public override ConstantExpression VisitUncheckedExpression(UncheckedExpression uncheckedExpression) { ConstantExpression v = uncheckedExpression.Expression.AcceptVisitor(this); if (v != null) return new ConstantCheckedExpression(false, v); else return null; } public override ConstantExpression VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression) { return interningProvider.Intern( new ConstantDefaultValue(ConvertTypeReference(defaultValueExpression.Type))); } public override ConstantExpression VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression) { ConstantExpression v = unaryOperatorExpression.Expression.AcceptVisitor(this); if (v == null) return null; switch (unaryOperatorExpression.Operator) { case UnaryOperatorType.Not: case UnaryOperatorType.BitNot: case UnaryOperatorType.Minus: case UnaryOperatorType.Plus: return new ConstantUnaryOperator(unaryOperatorExpression.Operator, v); default: return null; } } public override ConstantExpression VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) { ConstantExpression left = binaryOperatorExpression.Left.AcceptVisitor(this); ConstantExpression right = binaryOperatorExpression.Right.AcceptVisitor(this); if (left == null || right == null) return null; return new ConstantBinaryOperator(left, binaryOperatorExpression.Operator, right); } public override ConstantExpression VisitTypeOfExpression(TypeOfExpression typeOfExpression) { if (isAttributeArgument) { return new TypeOfConstantExpression(ConvertTypeReference(typeOfExpression.Type)); } else { return null; } } public override ConstantExpression VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression) { if (!objectCreateExpression.Arguments.Any()) { // built in primitive type constants can be created with new // Todo: correctly resolve the type instead of doing the string approach switch (objectCreateExpression.Type.ToString()) { case "System.Boolean": case "bool": return new PrimitiveConstantExpression(KnownTypeReference.Boolean, new bool()); case "System.Char": case "char": return new PrimitiveConstantExpression(KnownTypeReference.Char, new char()); case "System.SByte": case "sbyte": return new PrimitiveConstantExpression(KnownTypeReference.SByte, new sbyte()); case "System.Byte": case "byte": return new PrimitiveConstantExpression(KnownTypeReference.Byte, new byte()); case "System.Int16": case "short": return new PrimitiveConstantExpression(KnownTypeReference.Int16, new short()); case "System.UInt16": case "ushort": return new PrimitiveConstantExpression(KnownTypeReference.UInt16, new ushort()); case "System.Int32": case "int": return new PrimitiveConstantExpression(KnownTypeReference.Int32, new int()); case "System.UInt32": case "uint": return new PrimitiveConstantExpression(KnownTypeReference.UInt32, new uint()); case "System.Int64": case "long": return new PrimitiveConstantExpression(KnownTypeReference.Int64, new long()); case "System.UInt64": case "ulong": return new PrimitiveConstantExpression(KnownTypeReference.UInt64, new ulong()); case "System.Single": case "float": return new PrimitiveConstantExpression(KnownTypeReference.Single, new float()); case "System.Double": case "double": return new PrimitiveConstantExpression(KnownTypeReference.Double, new double()); case "System.Decimal": case "decimal": return new PrimitiveConstantExpression(KnownTypeReference.Decimal, new decimal()); } } return null; } public override ConstantExpression VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression) { var initializer = arrayCreateExpression.Initializer; // Attributes only allow one-dimensional arrays if (isAttributeArgument && !initializer.IsNull && arrayCreateExpression.Arguments.Count < 2) { ITypeReference type; if (arrayCreateExpression.Type.IsNull) { type = null; } else { type = ConvertTypeReference(arrayCreateExpression.Type); foreach (var spec in arrayCreateExpression.AdditionalArraySpecifiers.Reverse()) { type = interningProvider.Intern(new ArrayTypeReference(type, spec.Dimensions)); } } ConstantExpression[] elements = new ConstantExpression[initializer.Elements.Count]; int pos = 0; foreach (Expression expr in initializer.Elements) { ConstantExpression c = expr.AcceptVisitor(this); if (c == null) return null; elements[pos++] = c; } return new ConstantArrayCreation(type, elements); } else { return null; } } } #endregion #region Parameters void ConvertParameters(IList outputList, IEnumerable parameters) { foreach (ParameterDeclaration pd in parameters) { DefaultUnresolvedParameter p = new DefaultUnresolvedParameter(ConvertTypeReference(pd.Type), interningProvider.Intern(pd.Name)); p.Region = MakeRegion(pd); ConvertAttributes(p.Attributes, pd.Attributes); switch (pd.ParameterModifier) { case ParameterModifier.Ref: p.IsRef = true; p.Type = interningProvider.Intern(new ByReferenceTypeReference(p.Type)); break; case ParameterModifier.Out: p.IsOut = true; p.Type = interningProvider.Intern(new ByReferenceTypeReference(p.Type)); break; case ParameterModifier.Params: p.IsParams = true; break; } if (!pd.DefaultExpression.IsNull) p.DefaultValue = ConvertConstantValue(p.Type, pd.DefaultExpression); outputList.Add(interningProvider.Intern(p)); } } internal static IList GetParameterTypes(IEnumerable parameters, InterningProvider interningProvider) { List result = new List(); foreach (ParameterDeclaration pd in parameters) { ITypeReference type = pd.Type.ToTypeReference(NameLookupMode.Type, interningProvider); if (pd.ParameterModifier == ParameterModifier.Ref || pd.ParameterModifier == ParameterModifier.Out) type = interningProvider.Intern(new ByReferenceTypeReference(type)); result.Add(type); } return result; } #endregion #region XML Documentation void AddXmlDocumentation(IUnresolvedEntity entity, AstNode entityDeclaration) { if (this.SkipXmlDocumentation) return; StringBuilder documentation = null; // traverse children until the first non-whitespace node for (AstNode node = entityDeclaration.FirstChild; node != null && node.NodeType == NodeType.Whitespace; node = node.NextSibling) { Comment c = node as Comment; if (c != null && c.IsDocumentation) { if (documentation == null) documentation = new StringBuilder(); if (c.CommentType == CommentType.MultiLineDocumentation) { PrepareMultilineDocumentation(c.Content, documentation); } else { if (documentation.Length > 0) documentation.AppendLine(); if (c.Content.Length > 0 && c.Content[0] == ' ') documentation.Append(c.Content.Substring(1)); else documentation.Append(c.Content); } } } if (documentation != null) { unresolvedFile.AddDocumentation(entity, documentation.ToString()); } } void PrepareMultilineDocumentation(string content, StringBuilder b) { using (var reader = new StringReader(content)) { string firstLine = reader.ReadLine(); // Add first line only if it's not empty: if (!string.IsNullOrWhiteSpace(firstLine)) { if (firstLine[0] == ' ') b.Append(firstLine, 1, firstLine.Length - 1); else b.Append(firstLine); } // Read lines into list: List lines = new List(); string line; while ((line = reader.ReadLine()) != null) lines.Add(line); // If the last line (the line with '*/' delimiter) is white space only, ignore it. if (lines.Count > 0 && string.IsNullOrWhiteSpace(lines[lines.Count - 1])) lines.RemoveAt(lines.Count - 1); if (lines.Count > 0) { // Extract pattern from lines[0]: whitespace, asterisk, whitespace int patternLength = 0; string secondLine = lines[0]; while (patternLength < secondLine.Length && char.IsWhiteSpace(secondLine[patternLength])) patternLength++; if (patternLength < secondLine.Length && secondLine[patternLength] == '*') { patternLength++; while (patternLength < secondLine.Length && char.IsWhiteSpace(secondLine[patternLength])) patternLength++; } else { // no asterisk patternLength = 0; } // Now reduce pattern length to the common pattern: for (int i = 1; i < lines.Count; i++) { line = lines[i]; if (line.Length < patternLength) patternLength = line.Length; for (int j = 0; j < patternLength; j++) { if (secondLine[j] != line[j]) patternLength = j; } } // Append the lines to the string builder: for (int i = 0; i < lines.Count; i++) { if (b.Length > 0 || i > 0) b.Append(Environment.NewLine); b.Append(lines[i], patternLength, lines[i].Length - patternLength); } } } } #endregion } }