private (List <string> strings, int count) GetSubPropertyOutput(PropertyDetails property, SemanticModel semModel) { var result = new List <string>(); var subProperties = this.GetAllPublicProperties(property.Symbol, semModel); var numericSubstitute = 0; if (subProperties.Any()) { Logger?.RecordInfo(StringRes.Info_SubpropertyCount.WithParams(property.Name, subProperties.Count)); foreach (var subprop in subProperties) { Logger?.RecordInfo(StringRes.Info_GettingSubPropertyOutput.WithParams(subprop.Name)); var(output, counter) = this.GetSubPropertyOutputAndCounter(subprop.Name, numericSubstitute: numericSubstitute); numericSubstitute = counter; result.Add(output); } } else { Logger?.RecordInfo(StringRes.Info_PropertyTypeHasNoSubProperties.WithParams(property.Name, property.PropertyType)); // There are no subproperties so leave blank var(output, counter) = this.GetSubPropertyOutputAndCounter(string.Empty, numericSubstitute: numericSubstitute); numericSubstitute = counter; result.Add(output); } return(result, numericSubstitute); }
private ITypeSymbol GetTypeSymbol(SemanticModel semModel, PropertyDeclarationSyntax prop, PropertyDetails propDetails) { ITypeSymbol typeSymbol = null; ITypeSymbol GetWithFallback(TypeSyntax ts, SemanticModel sm, SyntaxTree tree) { ITypeSymbol result; try { result = sm.GetTypeInfo(ts).Type; } catch (Exception) { // By default, the semanticmodel passed into this method is the one for the active document. // If the type is in another file, generate a new model to use to look up the typeinfo. Don't do this by default as it's expensive. var localSemModel = CSharpCompilation.Create(string.Empty).AddSyntaxTrees(tree).GetSemanticModel(tree, ignoreAccessibility: true); result = localSemModel.GetTypeInfo(ts).Type; } return(result); } if (propDetails.PropertyType.IsGenericTypeName()) { Logger?.RecordInfo(StringRes.Info_GettingGenericType); if (prop.Type is GenericNameSyntax gns) { var t = gns.TypeArgumentList.Arguments.First(); typeSymbol = GetWithFallback(t, semModel, prop.SyntaxTree); } else if (prop.Type is QualifiedNameSyntax qns) { var t = ((GenericNameSyntax)qns.Right).TypeArgumentList.Arguments.First(); typeSymbol = GetWithFallback(t, semModel, prop.SyntaxTree); } else { Logger?.RecordInfo(StringRes.Info_PropertyTypeNotRecognizedAsGeneric.WithParams(propDetails.PropertyType)); } } if (typeSymbol == null) { typeSymbol = GetWithFallback(prop.Type, semModel, prop.SyntaxTree); } if (typeSymbol == null) { Logger?.RecordInfo(StringRes.Info_PropertyCannotBeAnalyzed.WithParams(prop.ToString())); } return(typeSymbol); }
private (string output, string name, int counter) GetOutputToAdd(SemanticModel semModel, PropertyDetails prop, int numericCounter = 0) { var(output, counter) = this.GetPropertyOutputAndCounter(prop, numericCounter, semModel, () => this.GetSubPropertyOutput(prop, semModel)); return(output, prop.Name, counter); }
private PropertyDetails GetPropertyDetails(PropertyDeclarationSyntax propertyDeclaration, SemanticModel semModel) { var propertyType = Unknown; switch (propertyDeclaration.Type) { case GenericNameSyntax gns: propertyType = gns.ToString(); // Lazy way to get generic types break; case PredefinedTypeSyntax pds: propertyType = pds.Keyword.ValueText; break; case IdentifierNameSyntax ins: propertyType = ins.Identifier.ValueText; break; case QualifiedNameSyntax qns: propertyType = qns.Right.Identifier.ValueText; if (qns.Right is GenericNameSyntax qgns) { propertyType += qgns.TypeArgumentList.ToString(); } break; case NullableTypeSyntax nts: propertyType = ((PredefinedTypeSyntax)nts.ElementType).Keyword.Text; if (!propertyType.ToLowerInvariant().Contains("nullable")) { propertyType += nts.QuestionToken.Text; } break; case ArrayTypeSyntax ats: propertyType = ats.ToString(); break; } var propertyName = this.GetIdentifier(propertyDeclaration); bool?propIsReadOnly; var setter = propertyDeclaration?.AccessorList?.Accessors .FirstOrDefault(a => a.RawKind == (ushort)SyntaxKind.SetAccessorDeclaration); if (setter == null) { propIsReadOnly = true; } else { var setterModifiers = setter.Modifiers; propIsReadOnly = setterModifiers.Any(m => m.Kind() == SyntaxKind.PrivateKeyword); } var pd = new PropertyDetails { Name = propertyName, PropertyType = propertyType, IsReadOnly = propIsReadOnly ?? false, }; foreach (var attribList in propertyDeclaration.AttributeLists) { foreach (var attrib in attribList.Attributes) { var att = new AttributeDetails { Name = attrib.Name.ToString(), }; var count = 1; foreach (var arg in attrib.ArgumentList.Arguments) { string name = null; string value = null; if (arg?.NameColon != null) { name = arg.NameColon.Name.ToString(); value = ((arg.NameColon.Parent as AttributeArgumentSyntax).Expression as IdentifierNameSyntax).Identifier.Value.ToString(); } else if (arg?.NameEquals != null) { name = arg.NameEquals.Name.ToString(); value = ((arg.NameEquals.Parent as AttributeArgumentSyntax).Expression as IdentifierNameSyntax).Identifier.Value.ToString(); } else { value = arg.ToString(); } att.Arguments.Add(new AttributeArgumentDetails { Index = count++, Name = name, Value = value, }); } pd.Attributes.Add(att); } } Logger?.RecordInfo(StringRes.Info_IdentifiedPropertySummary.WithParams(pd.Name, pd.PropertyType, pd.IsReadOnly)); ITypeSymbol typeSymbol = this.GetTypeSymbol(semModel, propertyDeclaration, pd); pd.Symbol = typeSymbol; return(pd); }
private ITypeSymbol GetTypeSymbol(SemanticModel semModel, BasePropertyDeclarationSyntax prop, PropertyDetails propDetails) { ITypeSymbol typeSymbol = null; if (propDetails.PropertyType.IsGenericTypeName()) { Logger?.RecordInfo(StringRes.Info_GettingGenericType); if (prop.Type is GenericNameSyntax gns) { typeSymbol = this.GetTypeSymbolWithFallback(gns, semModel, prop.SyntaxTree); } else if (prop.Type is QualifiedNameSyntax qns) { var t = ((GenericNameSyntax)qns.Right).TypeArgumentList.Arguments.First(); typeSymbol = this.GetTypeSymbolWithFallback(t, semModel, prop.SyntaxTree); } else { Logger?.RecordInfo(StringRes.Info_PropertyTypeNotRecognizedAsGeneric.WithParams(propDetails.PropertyType)); } } if (typeSymbol == null) { typeSymbol = this.GetTypeSymbolWithFallback(prop.Type, semModel, prop.SyntaxTree); } if (typeSymbol == null) { Logger?.RecordInfo(StringRes.Info_PropertyCannotBeAnalyzed.WithParams(prop.ToString())); } return(typeSymbol); }
protected override PropertyDetails GetPropertyDetails(SyntaxNode propertyDeclaration, SemanticModel semModel) { var propertyType = Unknown; string propertyName = null; AccessorDeclarationSyntax setter = null; SyntaxList <AttributeListSyntax> attributeList; if (propertyDeclaration is PropertyDeclarationSyntax propDecSyntax) { switch (propDecSyntax.Type) { case GenericNameSyntax gns: propertyType = gns.ToString(); // Lazy way to get generic types break; case PredefinedTypeSyntax pds: propertyType = pds.Keyword.ValueText; break; case IdentifierNameSyntax ins: propertyType = ins.Identifier.ValueText; break; case QualifiedNameSyntax qns: propertyType = qns.ToString(); break; case NullableTypeSyntax nts: switch (nts.ElementType) { case PredefinedTypeSyntax npts: propertyType = npts.Keyword.Text; break; case IdentifierNameSyntax nins: propertyType = nins.Identifier.ValueText; break; case QualifiedNameSyntax nqns: propertyType = nqns.ToString(); if (nqns.Right is GenericNameSyntax qngns) { propertyType += qngns.TypeArgumentList.ToString(); } break; } if (!propertyType.ToLowerInvariant().Contains("nullable")) { propertyType += nts.QuestionToken.Text; } break; case ArrayTypeSyntax ats: propertyType = ats.ToString(); break; case TupleTypeSyntax tts: propertyType = "Tuple"; break; } propertyName = this.GetIdentifier(propertyDeclaration); setter = propDecSyntax?.AccessorList?.Accessors.FirstOrDefault(a => a.RawKind == (ushort)SyntaxKind.SetAccessorDeclaration); attributeList = propDecSyntax.AttributeLists; } else { Logger?.RecordInfo(StringRes.Info_UnexpectedPropertyType.WithParams(propertyDeclaration.GetType())); } bool?propIsReadOnly; if (setter == null) { propIsReadOnly = true; } else { var setterModifiers = setter.Modifiers; propIsReadOnly = setterModifiers.Any(m => m.Kind() == SyntaxKind.PrivateKeyword); } var pd = new PropertyDetails { Name = propertyName, PropertyType = propertyType, IsReadOnly = propIsReadOnly ?? false, }; pd.Attributes.AddRange(this.GetAttributeDetails(attributeList)); Logger?.RecordInfo(StringRes.Info_IdentifiedPropertySummary.WithParams(pd.Name, pd.PropertyType, pd.IsReadOnly)); ITypeSymbol typeSymbol = this.GetTypeSymbol(semModel, propertyDeclaration as BasePropertyDeclarationSyntax, pd); pd.Symbol = typeSymbol; return(pd); }