diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index cca707708..d723e68d9 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -227,7 +227,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } else if (typeWithElementType is ByReferenceType) { return ConvertType(typeWithElementType.ElementType).MakeRefType(); } else { - // e.g. ByReferenceType; not supported as type in C# + // not supported as type in C# return ConvertType(typeWithElementType.ElementType); } } @@ -324,9 +324,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax // Handle top-level types if (string.IsNullOrEmpty(genericType.Namespace)) { result.Target = new SimpleType("global"); + if (AddResolveResultAnnotations) + result.Target.AddAnnotation(new NamespaceResolveResult(resolver.Compilation.RootNamespace)); result.IsDoubleColon = true; } else { - result.Target = ConvertNamespace(genericType.Namespace); + result.Target = ConvertNamespace(genericType.Namespace, out _); } } result.MemberName = genericType.Name; @@ -377,16 +379,20 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } } - public AstType ConvertNamespace(string namespaceName) + public AstType ConvertNamespace(string namespaceName, out NamespaceResolveResult nrr) { if (resolver != null) { // Look if there's an alias to the target namespace if (UseAliases) { for (ResolvedUsingScope usingScope = resolver.CurrentUsingScope; usingScope != null; usingScope = usingScope.Parent) { foreach (var pair in usingScope.UsingAliases) { - NamespaceResolveResult nrr = pair.Value as NamespaceResolveResult; - if (nrr != null && nrr.NamespaceName == namespaceName) - return new SimpleType(pair.Key); + nrr = pair.Value as NamespaceResolveResult; + if (nrr != null && nrr.NamespaceName == namespaceName) { + var ns = new SimpleType(pair.Key); + if (AddResolveResultAnnotations) + ns.AddAnnotation(nrr); + return ns; + } } } } @@ -394,30 +400,52 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax int pos = namespaceName.LastIndexOf('.'); if (pos < 0) { - if (IsValidNamespace(namespaceName)) { - return new SimpleType(namespaceName); + if (IsValidNamespace(namespaceName, out nrr)) { + var ns = new SimpleType(namespaceName); + if (AddResolveResultAnnotations && nrr != null) + ns.AddAnnotation(nrr); + return ns; } else { - return new MemberType { - Target = new SimpleType("global"), + var target = new SimpleType("global"); + if (AddResolveResultAnnotations) + target.AddAnnotation(new NamespaceResolveResult(resolver.Compilation.RootNamespace)); + var ns = new MemberType { + Target = target, IsDoubleColon = true, MemberName = namespaceName }; + if (AddResolveResultAnnotations) { + var @namespace = resolver.Compilation.RootNamespace.GetChildNamespace(namespaceName); + if (@namespace != null) + ns.AddAnnotation(nrr = new NamespaceResolveResult(@namespace)); + } + return ns; } } else { string parentNamespace = namespaceName.Substring(0, pos); string localNamespace = namespaceName.Substring(pos + 1); - return new MemberType { - Target = ConvertNamespace(parentNamespace), + var parentNS = ConvertNamespace(parentNamespace, out var parentNRR); + var ns = new MemberType { + Target = parentNS, MemberName = localNamespace }; + nrr = null; + if (AddResolveResultAnnotations && parentNRR != null) { + var newNamespace = parentNRR.Namespace.GetChildNamespace(localNamespace); + if (newNamespace != null) { + ns.AddAnnotation(nrr = new NamespaceResolveResult(newNamespace)); + } + } + return ns; } } - bool IsValidNamespace(string firstNamespacePart) + bool IsValidNamespace(string firstNamespacePart, out NamespaceResolveResult nrr) { + nrr = null; if (resolver == null) return true; // just assume namespaces are valid if we don't have a resolver - NamespaceResolveResult nrr = resolver.ResolveSimpleName(firstNamespacePart, EmptyList.Instance) as NamespaceResolveResult; + nrr = resolver.ResolveSimpleName(firstNamespacePart, EmptyList.Instance) as NamespaceResolveResult; return nrr != null && !nrr.IsError && nrr.NamespaceName == firstNamespacePart; } #endregion @@ -447,6 +475,70 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } #endregion + #region Convert Attribute Type + public AstType ConvertAttributeType(IType type) + { + if (type == null) + throw new ArgumentNullException("type"); + AstType astType = ConvertTypeHelper(type); + + string shortName = null; + if (type.Name.Length > 9 && type.Name.EndsWith("Attribute", StringComparison.Ordinal)) { + shortName = type.Name.Remove(type.Name.Length - 9); + } + switch (astType) { + case SimpleType st: + ResolveResult shortRR = null; + ResolveResult withExtraAttrSuffix = resolver.LookupSimpleNameOrTypeName(type.Name + "Attribute", EmptyList.Instance, NameLookupMode.Type); + if (shortName != null) { + shortRR = resolver.LookupSimpleNameOrTypeName(shortName, EmptyList.Instance, NameLookupMode.Type); + } + // short type is either unknown or not an attribute type -> we can use the short name. + if (shortRR != null && (shortRR is UnknownIdentifierResolveResult || !IsAttributeType(shortRR))) { + st.Identifier = shortName; + } else if (IsAttributeType(withExtraAttrSuffix)) { + // typeName + "Attribute" is an attribute type -> we cannot use long type name, add '@' to disable implicit "Attribute" suffix. + st.Identifier = '@' + st.Identifier; + } + break; + case MemberType mt: + if (type.DeclaringType != null) { + var declaringTypeDef = type.DeclaringType.GetDefinition(); + if (declaringTypeDef != null) { + if (shortName != null && !declaringTypeDef.GetNestedTypes(t => t.TypeParameterCount == 0 && t.Name == shortName).Any(IsAttributeType)) { + mt.MemberName = shortName; + } else if (declaringTypeDef.GetNestedTypes(t => t.TypeParameterCount == 0 && t.Name == type.Name + "Attribute").Any(IsAttributeType)) { + mt.MemberName = '@' + mt.MemberName; + } + } + } else if (mt.Target.GetResolveResult() is NamespaceResolveResult nrr) { + if (shortName != null && !IsAttributeType(nrr.Namespace.GetTypeDefinition(shortName, 0))) { + mt.MemberName = shortName; + } else if (IsAttributeType(nrr.Namespace.GetTypeDefinition(type.Name + "Attribute", 0))) { + mt.MemberName = '@' + mt.MemberName; + } + } + break; + } + + if (AddTypeReferenceAnnotations) + astType.AddAnnotation(type); + if (AddResolveResultAnnotations) + astType.AddAnnotation(new TypeResolveResult(type)); + return astType; + } + + private bool IsAttributeType(IType type) + { + return type != null && type.GetNonInterfaceBaseTypes().Any(t => t.IsKnownType(KnownTypeCode.Attribute)); + } + + private bool IsAttributeType(ResolveResult rr) + { + return rr is TypeResolveResult trr && IsAttributeType(trr.Type); + } + #endregion + #region Convert Constant Value /// /// Creates an Expression for the given constant value. diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUsingDeclarations.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUsingDeclarations.cs index 595e4b497..6f635fdfd 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUsingDeclarations.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUsingDeclarations.cs @@ -200,11 +200,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms base.VisitSimpleType(simpleType); return; } - // HACK : ignore type names in attributes (TypeSystemAstBuilder doesn't handle them correctly) - if (simpleType.Parent is Syntax.Attribute) { - base.VisitSimpleType(simpleType); - return; - } astBuilder.NameLookupMode = simpleType.GetNameLookupMode(); if (astBuilder.NameLookupMode == NameLookupMode.Type) { AstType outermostType = simpleType; @@ -217,7 +212,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms astBuilder.NameLookupMode = NameLookupMode.Expression; } } - simpleType.ReplaceWith(astBuilder.ConvertType(rr.Type)); + if (simpleType.Parent is Syntax.Attribute) { + simpleType.ReplaceWith(astBuilder.ConvertAttributeType(rr.Type)); + } else { + simpleType.ReplaceWith(astBuilder.ConvertType(rr.Type)); + } } } }