2004-07-18 Martin Baulig <martin@ximian.com>
[mono.git] / mcs / gmcs / attribute.cs
index b5025ab8f07063f614ac9d7b23e237c345d84455..396c46253db54cc8d59e698066f07cc3a922ea97 100644 (file)
@@ -2,6 +2,7 @@
 // attribute.cs: Attribute Handler
 //
 // Author: Ravi Pratap (ravi@ximian.com)
+//         Marek Safar (marek.safar@seznam.cz)
 //
 // Licensed under the terms of the GNU GPL
 //
@@ -44,6 +45,8 @@ namespace Mono.CSharp {
                        }
                        set {
                                attributes = value;
+                               if (attributes != null)
+                                       attributes.CheckTargets (ValidAttributeTargets);
                        }
                }
 
@@ -60,13 +63,14 @@ namespace Mono.CSharp {
                public abstract bool IsClsCompliaceRequired (DeclSpace ds);
 
                /// <summary>
-               /// Gets list of valid attribute targets for explicit target declaration
+               /// Gets list of valid attribute targets for explicit target declaration.
+               /// The first array item is default target. Don't break this rule.
                /// </summary>
                protected abstract string[] ValidAttributeTargets { get; }
        };
 
        public class Attribute {
-               public readonly string Target;
+               public string Target;
                public readonly string    Name;
                public readonly ArrayList Arguments;
 
@@ -194,6 +198,11 @@ namespace Mono.CSharp {
                        }
                }
 
+               string GetFullMemberName (string member)
+               {
+                       return Type.FullName + '.' + member;
+               }
+
                //
                // Given an expression, if the expression is a valid attribute-argument-expression
                // returns an object that can be used to encode it, or null on failure.
@@ -355,6 +364,17 @@ namespace Mono.CSharp {
                                        BindingFlags.Public | BindingFlags.Instance,
                                        Location);
 
+                               if (member == null) {
+                                       member = Expression.MemberLookup (ec, Type, member_name,
+                                               MemberTypes.Field | MemberTypes.Property, BindingFlags.NonPublic | BindingFlags.Instance,
+                                               Location);
+
+                                       if (member != null) {
+                                               Report.Error_T (122, Location, GetFullMemberName (member_name));
+                                               return null;
+                                       }
+                               }
+
                                if (member == null || !(member is PropertyExpr || member is FieldExpr)) {
                                        Error_InvalidNamedArgument (member_name);
                                        return null;
@@ -375,15 +395,18 @@ namespace Mono.CSharp {
                                                return null;
                                        }
 
-                                       if (e is Constant) {
-                                               Constant c;
-                                               
-                                               if (e.Type != pi.PropertyType){
-                                                       c = Const.ChangeType (Location, (Constant) e, pi.PropertyType);
+                                       Type type = e.Type;
+                                       EmptyCast ecast = e as EmptyCast;
+                                       if ((ecast != null) && (ecast.Child is Constant))
+                                               e = ecast.Child;
+
+                                       Constant c = e as Constant;
+                                       if (c != null) {
+                                               if (type != pi.PropertyType) {
+                                                       c = Const.ChangeType (Location, c, pi.PropertyType);
                                                        if (c == null)
                                                                return null;
-                                               } else
-                                                       c = (Constant) e;
+                                               }
                                                
                                                object o = c.GetValue ();
                                                prop_values.Add (o);
@@ -415,14 +438,17 @@ namespace Mono.CSharp {
                                                return null;
                                        }
 
+                                       Type type = e.Type;
+                                       EmptyCast ecast = e as EmptyCast;
+                                       if ((ecast != null) && (ecast.Child is Constant))
+                                               e = ecast.Child;
                                        //
                                        // Handle charset here, and set the TypeAttributes
                                        
-                                       if (e is Constant){
-                                               Constant c = (Constant) e;;
-                                               
-                                               if (c.Type != fi.FieldType){
-                                                       c = Const.ChangeType (Location, (Constant) e, fi.FieldType);
+                                       Constant c = e as Constant;
+                                       if (c != null) {
+                                               if (type != fi.FieldType) {
+                                                       c = Const.ChangeType (Location, c, fi.FieldType);
                                                        if (c == null)
                                                                return null;
                                                } 
@@ -431,6 +457,8 @@ namespace Mono.CSharp {
                                                field_values.Add (value);
                                        } else if (e is TypeOf) {
                                                field_values.Add (((TypeOf) e).TypeArg);
+                                       } else if (e is ArrayCreation) {
+                                               field_values.Add (((ArrayCreation) e).EncodeAsAttribute());
                                        } else {
                                                Error_AttributeArgumentNotValid (Location);
                                                return null;
@@ -454,7 +482,6 @@ namespace Mono.CSharp {
                                ec, (MethodGroupExpr) mg, pos_args, false, Location);
 
                        if (constructor == null) {
-                               Error_AttributeConstructorMismatch ();
                                return null;
                        }
 
@@ -544,9 +571,7 @@ namespace Mono.CSharp {
                                // class X { static void Main () {} }
                                //
                                Report.Warning (
-                                       -23, Location,
-                                       "The compiler can not encode this attribute in .NET due to\n" +
-                                       "\ta bug in the .NET runtime.  Try the Mono runtime.\nThe error was: " + e.Message);
+                                       -23, Location, "The compiler can not encode this attribute in .NET due to a bug in the .NET runtime. Try the Mono runtime. The exception was: " + e.Message);
                        }
                        
                        return cb;
@@ -555,10 +580,10 @@ namespace Mono.CSharp {
                /// <summary>
                ///   Get a string containing a list of valid targets for the attribute 'attr'
                /// </summary>
-               static string GetValidTargets (Attribute attr)
+               public string GetValidTargets ()
                {
                        StringBuilder sb = new StringBuilder ();
-                       AttributeTargets targets = attr.GetAttributeUsage ().ValidOn;
+                       AttributeTargets targets = GetAttributeUsage ().ValidOn;
 
                        if ((targets & AttributeTargets.Assembly) != 0)
                                sb.Append ("'assembly' ");
@@ -606,15 +631,6 @@ namespace Mono.CSharp {
 
                }
 
-               public static void Error_AttributeNotValidForElement (Attribute a, Location loc)
-               {
-                       Report.Error (
-                               592, loc, "Attribute '" + a.Name +
-                               "' is not valid on this declaration type. " +
-                               "It is valid on " + GetValidTargets (a) + "declarations only.");
-               }
-
-
                /// <summary>
                /// Returns AttributeUsage attribute for this type
                /// </summary>
@@ -636,96 +652,64 @@ namespace Mono.CSharp {
                        return attr_class.AttributeUsage;
                }
 
-
-               //
-               // This pulls the condition name out of a Conditional attribute
-               //
-               public string Conditional_GetConditionName ()
+               /// <summary>
+               /// Returns custom name of indexer
+               /// </summary>
+               public string GetIndexerAttributeValue (EmitContext ec)
                {
-                       //
-                       // So we have a Conditional, pull the data out.
-                       //
-                       if (Arguments == null || Arguments [0] == null){
-                               Error_AttributeConstructorMismatch ();
-                               return null;
+                       if (pos_values == null) {
+                               // TODO: It is not neccessary to call whole Resolve (ApplyAttribute does it now) we need only ctor args.
+                               // But because a lot of attribute class code must be rewritten will be better to wait...
+                               Resolve (ec);
                        }
+                       
+                       return pos_values [0] as string;
+               }
 
-                       ArrayList pos_args = (ArrayList) Arguments [0];
-                       if (pos_args.Count != 1){
-                               Error_AttributeConstructorMismatch ();
-                               return null;
+               /// <summary>
+               /// Returns condition of ConditionalAttribute
+               /// </summary>
+               public string GetConditionalAttributeValue (DeclSpace ds)
+               {
+                       if (pos_values == null) {
+                               EmitContext ec = new EmitContext (ds, ds, Location, null, null, 0, false);
+
+                               // TODO: It is not neccessary to call whole Resolve (ApplyAttribute does it now) we need only ctor args.
+                               // But because a lot of attribute class code must be rewritten will be better to wait...
+                               Resolve (ec);
                        }
 
-                       Argument arg = (Argument) pos_args [0]; 
-                       if (!(arg.Expr is StringConstant)){
-                               Error_AttributeConstructorMismatch ();
+                       // Some error occurred
+                       if (pos_values [0] == null)
                                return null;
-                       }
 
-                       return ((StringConstant) arg.Expr).Value;
+                       return (string)pos_values [0];
                }
 
-               //
-               // This pulls the obsolete message and error flag out of an Obsolete attribute
-               //
-               public string Obsolete_GetObsoleteMessage (out bool is_error)
+               /// <summary>
+               /// Creates the instance of ObsoleteAttribute from this attribute instance
+               /// </summary>
+               public ObsoleteAttribute GetObsoleteAttribute (DeclSpace ds)
                {
-                       is_error = false;
-                       //
-                       // So we have an Obsolete, pull the data out.
-                       //
-                       if (Arguments == null || Arguments [0] == null)
-                               return "";
+                       if (pos_values == null) {
+                               EmitContext ec = new EmitContext (ds, ds, Location, null, null, 0, false);
 
-                       ArrayList pos_args = (ArrayList) Arguments [0];
-                       if (pos_args.Count == 0)
-                               return "";
-                       else if (pos_args.Count > 2){
-                               Error_AttributeConstructorMismatch ();
-                               return null;
+                               // TODO: It is not neccessary to call whole Resolve (ApplyAttribute does it now) we need only ctor args.
+                               // But because a lot of attribute class code must be rewritten will be better to wait...
+                               Resolve (ec);
                        }
 
-                       Argument arg = (Argument) pos_args [0]; 
-                       if (!(arg.Expr is StringConstant)){
-                               Error_AttributeConstructorMismatch ();
+                       // Some error occurred
+                       if (pos_values == null)
                                return null;
-                       }
 
-                       if (pos_args.Count == 2){
-                               Argument arg2 = (Argument) pos_args [1];
-                               if (!(arg2.Expr is BoolConstant)){
-                                       Error_AttributeConstructorMismatch ();
-                                       return null;
-                               }
-                               is_error = ((BoolConstant) arg2.Expr).Value;
-                       }
+                       if (pos_values.Length == 0)
+                               return new ObsoleteAttribute ();
 
-                       return ((StringConstant) arg.Expr).Value;
-               }
+                       if (pos_values.Length == 1)
+                               return new ObsoleteAttribute ((string)pos_values [0]);
 
-               public string IndexerName_GetIndexerName (EmitContext ec)
-               {
-                       if (Arguments == null || Arguments [0] == null){
-                               Error_AttributeConstructorMismatch ();
-                               return null;
-                       }
-                       ArrayList pos_args = (ArrayList) Arguments [0];
-                       if (pos_args.Count != 1) {
-                               Error_AttributeConstructorMismatch ();
-                               return null;
-                       }
-                       
-                       Argument arg = (Argument) pos_args [0];
-                       if (!arg.Resolve (ec, Location))
-                               return null;
-                       
-                       StringConstant sc = arg.Expr as StringConstant;
-                       if (sc == null){
-                               Error_AttributeConstructorMismatch ();
-                               return null;
-                       }
-                       
-                       return sc.Value;
+                       return new ObsoleteAttribute ((string)pos_values [0], (bool)pos_values [1]);
                }
 
                /// <summary>
@@ -810,11 +794,6 @@ namespace Mono.CSharp {
                        get { return ImplOptions == MethodImplOptions.InternalCall; }
                }
 
-               protected virtual bool CanIgnoreInvalidAttribute (Attributable ias)
-               {
-                       return false;
-               }
-
                /// <summary>
                /// Emit attribute for Attributable symbol
                /// </summary>
@@ -826,25 +805,25 @@ namespace Mono.CSharp {
 
                        AttributeUsageAttribute usage_attr = GetAttributeUsage ();
                        if ((usage_attr.ValidOn & ias.AttributeTargets) == 0) {
-                               // The parser applies toplevel attributes both to the assembly and
-                               // to a top-level class, if any.  So, be silent about them.
-                               if (! CanIgnoreInvalidAttribute (ias))
-                                       Error_AttributeNotValidForElement (this, Location);
+                               // "Attribute '{0}' is not valid on this declaration type. It is valid on {1} declarations only.";
+                               Report.Error_T (592, Location, Name, GetValidTargets ());
                                return;
                        }
 
                        ias.ApplyAttributeBuilder (this, cb);
 
-                       // Because default target is null (we save some space). We need to transform it here
-                       // for distinction between "default" and "doesn't exist"
-                       string target = Target == null ? "default" : Target;
-                       string emitted = emitted_attr [Type] as string;
-                       if (emitted == target && !usage_attr.AllowMultiple) {
+                       if (!usage_attr.AllowMultiple) {
+                               ArrayList emitted_targets = (ArrayList)emitted_attr [Type];
+                               if (emitted_targets == null) {
+                                       emitted_targets = new ArrayList ();
+                                       emitted_attr.Add (Type, emitted_targets);
+                               } else if (emitted_targets.Contains (Target)) {
                                Report.Error (579, Location, "Duplicate '" + Name + "' attribute");
+                                       return;
+                               }
+                               emitted_targets.Add (Target);
                        }
 
-                       emitted_attr [Type] = target;
-
                        // Here we are testing attribute arguments for array usage (error 3016)
                        if (ias.IsClsCompliaceRequired (ec.DeclSpace)) {
                                if (Arguments == null)
@@ -1073,16 +1052,6 @@ namespace Mono.CSharp {
                                ec.DeclSpace.NamespaceEntry = ns;
                        return base.CheckAttributeType (ec, complain);
                }
-
-               protected override bool CanIgnoreInvalidAttribute (Attributable ias)
-               {
-                       // Ignore error if this attribute is shared between the Assembly
-                       // and a top-level class.  The parser couldn't figure out which entity
-                       // this attribute belongs to.  If this attribute is erroneous, it should
-                       // be caught when it is processed by the top-level class.
-
-                       return (Target == null && ias is CommonAssemblyModulClass);
-               }
        }
 
        public class Attributes {
@@ -1110,8 +1079,10 @@ namespace Mono.CSharp {
                public void CheckTargets (string[] possible_targets)
                {
                        foreach (Attribute a in Attrs) {
-                               if (a.Target == null)
+                               if (a.Target == null) {
+                                       a.Target = possible_targets [0];
                                        continue;
+                               }
 
                                if (((IList) possible_targets).Contains (a.Target))
                                        continue;
@@ -1140,6 +1111,24 @@ namespace Mono.CSharp {
                        return Search (t, ec, true);
                }
 
+               /// <summary>
+               /// Returns all attributes of type 't'. Use it when attribute is AllowMultiple = true
+               /// </summary>
+               public Attribute[] SearchMulti (Type t, EmitContext ec)
+               {
+                       ArrayList ar = null;
+
+                       foreach (Attribute a in Attrs) {
+                               if (a.ResolveType (ec, false) == t) {
+                                       if (ar == null)
+                                               ar = new ArrayList ();
+                                       ar.Add (a);
+                               }
+                       }
+
+                       return ar == null ? null : ar.ToArray (typeof (Attribute)) as Attribute[];
+               }
+
                public void Emit (EmitContext ec, Attributable ias)
                {
                        ListDictionary ld = new ListDictionary ();
@@ -1161,17 +1150,15 @@ namespace Mono.CSharp {
                /// <summary>
                /// Pulls the IndexerName attribute from an Indexer if it exists.
                /// </summary>
-               public string ScanForIndexerName (EmitContext ec)
+               public Attribute GetIndexerNameAttribute (EmitContext ec)
                {
-                       Attribute a = Search (TypeManager.indexer_name_type, ec);
+                       Attribute a = Search (TypeManager.indexer_name_type, ec, false);
                        if (a == null)
                                return null;
 
-                       // Remove the attribute from the list
-                       //TODO: It is very close to hack and it can crash here
+                       // Remove the attribute from the list because it is not emitted
                        Attrs.Remove (a);
-
-                       return a.IndexerName_GetIndexerName (ec);
+                       return a;
                }
 
        }
@@ -1182,6 +1169,9 @@ namespace Mono.CSharp {
        sealed class AttributeTester
        {
                static PtrHashtable analyzed_types = new PtrHashtable ();
+               static PtrHashtable analyzed_types_obsolete = new PtrHashtable ();
+               static PtrHashtable analyzed_member_obsolete = new PtrHashtable ();
+               static PtrHashtable analyzed_method_excluded = new PtrHashtable ();
 
                private AttributeTester ()
                {
@@ -1344,5 +1334,113 @@ namespace Mono.CSharp {
 
                        return ((CLSCompliantAttribute)CompliantAttribute[0]).IsCompliant;
                }
+
+               /// <summary>
+               /// Returns instance of ObsoleteAttribute when type is obsolete
+               /// </summary>
+               public static ObsoleteAttribute GetObsoleteAttribute (Type type)
+               {
+                       object type_obsolete = analyzed_types_obsolete [type];
+                       if (type_obsolete == FALSE)
+                               return null;
+
+                       if (type_obsolete != null)
+                               return (ObsoleteAttribute)type_obsolete;
+
+                       ObsoleteAttribute result = null;
+                       if (type.IsByRef || type.IsArray || type.IsPointer) {
+                               result = GetObsoleteAttribute (TypeManager.GetElementType (type));
+                       } else if (type.IsGenericParameter || type.IsGenericInstance)
+                               return null;
+                       else {
+                               DeclSpace type_ds = TypeManager.LookupDeclSpace (type);
+
+                               // Type is external, we can get attribute directly
+                               if (type_ds == null) {
+                                       object[] attribute = type.GetCustomAttributes (TypeManager.obsolete_attribute_type, false);
+                                       if (attribute.Length == 1)
+                                               result = (ObsoleteAttribute)attribute [0];
+                               } else {
+                                       result = type_ds.GetObsoleteAttribute (type_ds);
+                               }
+                       }
+
+                       analyzed_types_obsolete.Add (type, result == null ? FALSE : result);
+                       return result;
+               }
+
+               /// <summary>
+               /// Returns instance of ObsoleteAttribute when method is obsolete
+               /// </summary>
+               public static ObsoleteAttribute GetMethodObsoleteAttribute (MethodBase mb)
+               {
+                       IMethodData mc = TypeManager.GetMethod (mb);
+                       if (mc != null) 
+                               return mc.GetObsoleteAttribute ();
+
+                       return GetMemberObsoleteAttribute (mb);
+               }
+
+               /// <summary>
+               /// Returns instance of ObsoleteAttribute when member is obsolete
+               /// </summary>
+               public static ObsoleteAttribute GetMemberObsoleteAttribute (MemberInfo mi)
+               {
+                       object type_obsolete = analyzed_member_obsolete [mi];
+                       if (type_obsolete == FALSE)
+                               return null;
+
+                       if (type_obsolete != null)
+                               return (ObsoleteAttribute)type_obsolete;
+
+                       if ((mi.DeclaringType is TypeBuilder) || mi.DeclaringType.IsGenericInstance)
+                               return null;
+
+                       ObsoleteAttribute oa = System.Attribute.GetCustomAttribute (mi, TypeManager.obsolete_attribute_type, false) as ObsoleteAttribute;
+                       analyzed_member_obsolete.Add (mi, oa == null ? FALSE : oa);
+                       return oa;
+               }
+
+               /// <summary>
+               /// Common method for Obsolete error/warning reporting.
+               /// </summary>
+               public static void Report_ObsoleteMessage (ObsoleteAttribute oa, string member, Location loc)
+               {
+                       if (oa.IsError) {
+                               Report.Error_T (619, loc, member, oa.Message);
+                               return;
+                       }
+
+                       if (oa.Message == null) {
+                               Report.Warning_T (612, loc, member);
+                               return;
+                       }
+                       Report.Warning_T (618, loc, member, oa.Message);
+               }
+
+               public static bool IsConditionalMethodExcluded (MethodBase mb)
+               {
+                       object excluded = analyzed_method_excluded [mb];
+                       if (excluded != null)
+                               return excluded == TRUE ? true : false;
+
+                       if (mb.Mono_IsInflatedMethod)
+                               return false;
+                       
+                       ConditionalAttribute[] attrs = mb.GetCustomAttributes (TypeManager.conditional_attribute_type, true) as ConditionalAttribute[];
+                       if (attrs.Length == 0) {
+                               analyzed_method_excluded.Add (mb, FALSE);
+                               return false;
+                       }
+
+                       foreach (ConditionalAttribute a in attrs) {
+                               if (RootContext.AllDefines.Contains (a.ConditionString)) {
+                                       analyzed_method_excluded.Add (mb, FALSE);
+                                       return false;
+                               }
+                       }
+                       analyzed_method_excluded.Add (mb, TRUE);
+                       return true;
+               }
        }
 }