]> source.dussan.org Git - aspectj.git/commitdiff
deow @AJ + @AfterXX annotation warnings + test + JDWP sample for LTW tests + some...
authoravasseur <avasseur>
Thu, 19 May 2005 13:39:20 +0000 (13:39 +0000)
committeravasseur <avasseur>
Thu, 19 May 2005 13:39:20 +0000 (13:39 +0000)
aspectj5rt/java5-src/org/aspectj/lang/annotation/DeclareError.java
aspectj5rt/java5-src/org/aspectj/lang/annotation/DeclareWarning.java
docs/adk15ProgGuideDB/ataspectj.xml
docs/adk15ProgGuideDB/ltw.xml
tests/java5/ataspectj/ajc-ant.xml
tests/java5/ataspectj/ataspectj/misuse/Test014.java
tests/src/org/aspectj/systemtest/ajc150/AllTestsAspectJ150.java
tests/src/org/aspectj/systemtest/ajc150/ataspectj/misuse.xml
weaver/src/org/aspectj/weaver/ataspectj/Aj5Attributes.java

index 803118de1d2a0cdb6408cd0520bf2b2761d3b4dd..9f3a045ee6ea7ce066b1747930b387ce14187362 100644 (file)
@@ -20,12 +20,14 @@ import java.lang.annotation.Target;
  * @author colyer
  * Annotation for declare error...
  * 
- * usage:
- * @DeclareError("somePcut()")
- * private static final String "a message";
+ * usage: @DeclareError("somePcut()")
+ *        private static final String "a message";
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.FIELD)
 public @interface DeclareError {
+    /**
+     * The pointcut expression where to bind the error (don't use if, formal bindings, cflow etc)
+     */
        String value();
 }
index ba3d11eb768f6642f556833f11d26e866065dbd5..446ac786a76c070f9872f32681f3c3df70e2e261 100644 (file)
@@ -20,12 +20,14 @@ import java.lang.annotation.Target;
  * @author colyer
  * Annotation for declare warning...
  * 
- * usage:
- * @DeclareWarning("somePcut()")
- * private static final String "a message";
+ * usage: @DeclareWarning("somePcut()")
+ *        private static final String "a message";
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.FIELD)
 public @interface DeclareWarning {
+    /**
+     * The pointcut expression where to bind the error (don't use if, formal bindings, cflow etc)
+     */
        String value();
 }
index 0801990549ef53a3cca21981d9e4a668de9d74a3..b3986ef903b8c6a1c54a38378c24d0e6d24f1655 100644 (file)
@@ -51,8 +51,8 @@
      public aspect Foo {}
       ]]></programlisting>
 
-    <para>Privileged aspects are declared as:</para>
-
+    <para>Privileged aspects are not supported by the annotation style</para>
+    <!--
      <programlisting><![CDATA[
      @Aspect(isPrivileged=true)
      public class Foo {}
      
      public privileged aspect Foo {}
       ]]></programlisting>
-
+    -->
     <para>To specify an aspect an aspect instantiation model (the default is
-    singleton), use the <literal>instantionModel</literal> and
-    <literal>perClausePattern</literal> attributes. For example:</para>
+    singleton), provide the perclause as the <literal>@Aspect</literal> value.
+    For example:</para>
     
      <programlisting><![CDATA[
-     @Aspect(instantiationModel=AspectInstantiationModel.PERTHIS,
-             perClausePattern="execution(* abc..*(..))")
+     @Aspect("perthis(execution(* abc..*(..)))")
      public class Foo {}
      
      is equivalent to...
      
      public aspect Foo perthis(execution(* abc..*(..))) {}
       ]]></programlisting>
-    
-    <para>The full definitions of the Aspect annotation type and the
-        AspectInstantiationModel enumerated type are:</para>
-
-     <programlisting><![CDATA[
-     /**
-      * Use to indicate that a class should be treated as an aspect by
-      * AspectJ's weaver.
-      */
-     @Target({ElementType.TYPE})
-     public @interface Aspect {
-       AspectInstantiationModel instantiationModel() default AspectInstantiationModel.SINGLETON;
-       String perClausePattern() default "";
-               boolean isPrivileged() default false;
-     }
-     
-     /**
-      * The different aspect instantiation models supported by AspectJ
-      */
-     public enum AspectInstantiationModel {
-      SINGLETON,
-      PERTHIS,
-      PERTARGET,
-      PERCFLOW,
-      PERCFLOWBELOW,
-      PERTYPEWITHIN
-     }
-      ]]></programlisting>
 
   </sect1>
   
           <literal>thisJoinPointStaticPart</literal>, 
           <literal>thisEnclosingJoinPointStaticPart</literal> then these need to 
           be declared as additional method parameters when using the annotation
-          style. In AspectJ 1.5.0 we require that these parameters be declared
+          style. <!-- TODO AV - not any more -- In AspectJ 1.5.0 we require that these parameters be declared
           first in the parameter list, in later releases we may relax this
-          requirement.</para>
+          requirement.--></para>
 
      <programlisting><![CDATA[
      @AdviceName("callFromFoo")
       
      <programlisting><![CDATA[
      public interface ProceedingJoinPoint extends JoinPoint {
-       public Object proceed(Object... args);
+       public Object proceed(Object[] args);
      }
       ]]></programlisting>
       
      <programlisting><![CDATA[
      public aspect ProceedAspect {
        pointcut setAge(int i): call(* setAge(..)) && args(i);
-    
+
        Object around(int i): setAge(i) {
          return proceed(i*2);
        }
      }
-     
+
      can be written as...
-     
+
      @Aspect
      public class ProceedAspect {
-     
+
        @Pointcut("call(* setAge(..)) && args(i)")
        void setAge(int i) {}
-     
+
        @Around("setAge(i)")
        public Object twiceAsOld(ProceedingJoinPoint thisJoinPoint, int i) {
-         return thisJoinPoint.proceed(i*2);
+         return thisJoinPoint.proceed(new Object[]{i*2}); //using Java 5 autoboxing
        }
-     
-     }          
+
+     }
+
+     Note that the ProceedingJoinPoint does not need to be passed as the proceed(..) arguments.
       ]]></programlisting>
       
       </sect2>
      (This is the same behaviour as when using declare warning or error with the
      code style). Declare warning and error declarations are made by annotating
      a string constant whose value is the message to be issued.</para>
-     
+
+     <para>Note that the String must be a constant and not the result of the invocation
+     of a static method for example.</para>
+
      <programlisting><![CDATA[
        declare warning : call(* javax.sql..*(..)) && !within(org.xyz.daos..*)
                        : "Only DAOs should be calling JDBC.";
-                       
+
        declare error : execution(* IFoo+.*(..)) && !within(org.foo..*)
                      : "Only foo types can implement IFoo";
-                     
+
        can be written as...
-       
+
        @DeclareWarning("call(* javax.sql..*(..)) && !within(org.xyz.daos..*)")
        static final String aMessage = "Only DAOs should be calling JDBC.";
-       
+
        @DeclareError("execution(* IFoo+.*(..)) && !within(org.foo..*)")
        static final String badIFooImplementors = "Only foo types can implement IFoo";
-     
+
+       // the following is not valid since the message is not a String constant
+       @DeclareError("execution(* IFoo+.*(..)) && !within(org.foo..*)")
+       static final String badIFooImplementorsCorrupted = getMessage();
+       static String getMessage() {
+           return "Only foo types can implement IFoo " + System.currentTimeMillis();
+       }
+
       ]]></programlisting>
      
        
         public static boolean hasAspect(Object anAspect, Class forType) {...}
       }
       ]]></programlisting>
-      
+
+      <!-- TODO AV - stuff below is not done -->
+      <!--
       <para>When the AspectJ weaver sees calls to these methods, it will convert
       them into the most efficient form possible (to get performance equivalent
-      to a direct <literal>MyAspect.aspectOf()</literal> call).</para>      
+      to a direct <literal>MyAspect.aspectOf()</literal> call).</para>
+      -->
   </sect1>
 </chapter>
 
index 1f0bb9d8a813042928b396587e1dc22cfb29fb95..8b6f027bb312d77df90e0385866a2154584238f3 100644 (file)
@@ -38,6 +38,8 @@
                 need to specify the <literal>-Xreweavable</literal> compiler option when building
                 them. This causes AspectJ to save additional state in the class files that is used
                 to support subsequent reweaving. </para>
+            <para><!-- FIXME AV -->As per AspectJ 1.5.0 M3 aspects (code style or annotation style) are
+            reweavable by default, and weaved classes may be as well in 1.5.0 final.</para>
         </sect2>
     </sect1>
     
@@ -92,6 +94,7 @@
                 <varlistentry>
                     <term>Command line</term>
                     <listitem>
+                        <!-- FIXME AV - wondering what is the status of this one as per aop.xml etc.. -->
                         <para> AspectJ includes a script "aj" that allows programs executed at
                             the command line to take advantage of load-time weaving. 
                             The script is customized when AspectJ is installed depending on the chosen 
                 found on the search path (regular <literal>getResourceAsStream</literal> lookup)
                 according to the following rules: </para>
             <itemizedlist>
+                <!-- FIXME AV - looks like we can refine conf in a child CL - not good -->
                 <listitem> The set of available aspects is the set of all
                     declared and defined aspects (<literal>aspect</literal> and
                     <literal>concrete-aspect</literal> elements of the <literal>aspects</literal>
                             <entry>Performance optimization for aspects making use
                             of thisJoinPoint (non-static parts)</entry>
                         </row>
+                        <row>
+                            <entry>
+                                <literal>-Xlint, -Xlint:ignore, ...</literal>
+                            </entry>
+                            <entry>Configure lint messages</entry><!--FIXME AV - default to blabla, see link X -->
+                        </row>
                         <row>
                             <entry>
                                 <literal>-nowarn, -warn:none</literal>
                         </row>
                         <row>
                             <entry>
-                                <literal>-Xnoinline</literal>
+                                <literal>-XnoInline</literal>
                             </entry>
                             <entry>Don't inline around advice.</entry>
                         </row>
                         </row>
                         <row>
                             <entry>
-                                <literal>-XmessageHolderClass</literal>
+                                <literal>-XmessageHolderClass:...</literal>
                             </entry>
-                            <entry>Provide alternative output destination to stderr for all weaver messages</entry>
+                            <entry>Provide alternative output destination to stdout/stderr for all weaver messages.
+                            The given value must be the full qualified class name of a class that implements
+                            <literal>org.aspectj.weaver.loadtime</literal>
+                            and that is visible from where the <literal>aop.xml</literal> is packed.
+                            If more than one such options are used,
+                            the first occurence only is taken into account.</entry>
                         </row>
                     </tbody>
                 </tgroup>
         <sect2>
             <title>JVMTI</title>
             <para> When using JDK 1.5 the JVMTI agent can be used by starting the JVM with the
-                following option: </para>
+                following option (adapt according to the path to aspectjweaver.jar): </para>
             <programlisting><![CDATA[
                        -javaagent=aspectjweaver.jar          
                  ]]></programlisting>
index ab0e52437060085f65eb592b471fe76523f6c6e9..ce5351563e767334f83b32120345de6715322cf8 100644 (file)
@@ -1,9 +1,10 @@
 <!-- ajc-ant script, not to be used from Ant commant line - see AntSpec -->
 <project name="foo" default="javac.ltw">
 
+    <!-- using this we can debug the forked VM -->
     <property
         name="jdwp"
-        value=""/>
+        value="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005"/>
 
     <target name="compile:javac">
         <!-- compile only javac compilable stuff -->
@@ -35,6 +36,7 @@
             <classpath refid="aj.path"/>
             <jvmarg value="-javaagent:${aj.root}/lib/test/loadtime5.jar"/>
             <jvmarg value="-Daj5.def=ataspectj/aop-aroundinlinemungertest.xml"/>
+            <!--<jvmarg line="${jdwp}"/>--><!-- uncomment to debug with JDWP -->
         </java>
     </target>
 
index 9e135a93c175e7624218679a49a5c32d33f9d2f0..d3d468fb727757ca8cd211576714b98d22f78de6 100644 (file)
@@ -5,7 +5,36 @@ import org.aspectj.lang.annotation.*;
 
 @Aspect
 public class Test014{
-  @Pointcut("call%dddd\n\n\n\n\n\n\n\n\n\n\n%dwdwudwdwbuill817pe;][{\ngrgrgnjk78877&&<:{{{+=``\"")
-  void somecall(){
-  }
+    @Pointcut("call%dddd\n\n\n\n\n\n\n\n\n\n\n%dwdwudwdwbuill817pe;][{\ngrgrgnjk78877&&<:{{{+=``\"")
+    public void somecall(){
+    }
+
+    @Before("fhfh()")
+    public void beforeA() {}
+
+    @After("fhfh()")
+    public void afterA() {}
+
+    @Around("fhfh()")
+    public Object aroundA() {return null;}
+
+    @AfterThrowing(value = "fhfh()", pointcut = "wups()")
+    public void afterAT2() {}
+
+    @AfterThrowing("fhfh()")
+    public void afterAT() {}
+
+    @AfterReturning(value = "fhfh()", pointcut = "wups()")
+    public void afterAR2() {}
+
+    @AfterReturning("fhfh()")
+    public void afterAR() {}
+
+    @DeclareError("execution(* Foo.bar())")
+    private int X;
+
+    @DeclareWarning("execution(* Foo.bar())")
+    private final static String X2 = getX2();
+
+    static String getX2() {return "not supported";}
 }
index 691a77a191e6e2fb6c32d633a8db4f3f9c52bc4c..6069f845a33d7ebfc8657f4ea5f6dcab4ea16178 100644 (file)
@@ -48,8 +48,7 @@ public class AllTestsAspectJ150 {
                suite.addTest(GenericsTests.suite());
                suite.addTest(AtAjSyntaxTests.suite());
         suite.addTest(AtAjMisuseTests.suite());
-        //FIXME AV - #75442
-        //suite.addTest(AtAjLTWTests.suite());
+        suite.addTest(AtAjLTWTests.suite());
                //$JUnit-END$
                return suite;
        }
index 5427a4a2cd4a373c363f6562e6f242ed352c90c9..3b0731189a0c6ae339583ed2b79b80e4c4d8b110 100644 (file)
     <comment>line is enclosing class - TBD</comment>
     <ajc-test dir="java5/ataspectj"
         pr="" title="@Pointcut with garbage string">
-        <compile files="ataspectj/misuse/Test014.java" options="-1.5 -Xdev:NoAtAspectJProcessing">
+        <compile files="ataspectj/misuse/Test014.java" options="-1.5 -Xdev:NoAtAspectJProcessing -Xlint:ignore">
             <message kind="error" line="7" text="Cannot parse @Pointcut 'call%dddd"/>
+            <message kind="error" text="can't find referenced pointcut"/>
+            <message kind="error" text="can't find pointcut"/>
+            <message kind="error" text="@AfterThrowing: either 'value' or 'poincut' must be provided, not both"/>
+            <message kind="error" text="@AfterReturning: either 'value' or 'poincut' must be provided, not both"/>
+            <message kind="error" text="@DeclareWarning used on a non String constant field"/>
+            <message kind="error" text="@DeclareError used on a non String constant field"/>
         </compile>
     </ajc-test>
 
index a9502994a959c196801719a28cbab6609b5a2da2..15896cbc62639464968d206dfc864f67fb462630 100644 (file)
@@ -54,6 +54,8 @@ import org.aspectj.weaver.patterns.PerTypeWithin;
 import org.aspectj.weaver.patterns.Pointcut;
 import org.aspectj.weaver.patterns.SimpleScope;
 import org.aspectj.weaver.patterns.ParserException;
+import org.aspectj.weaver.patterns.Declare;
+import org.aspectj.weaver.patterns.DeclareErrorOrWarning;
 
 /**
  * Annotation defined aspect reader.
@@ -127,6 +129,21 @@ public class Aj5Attributes {
         }
     }
 
+    /**
+     * A struct when we read @AJ on field
+     *
+     * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
+     */
+    private static class AjAttributeFieldStruct extends AjAttributeStruct {
+
+        final Field field;
+
+        public AjAttributeFieldStruct(Field field, ResolvedTypeX type, ISourceContext sourceContext, IMessageHandler messageHandler) {
+            super(type, sourceContext, messageHandler);
+            this.field = field;
+        }
+    }
+
     /**
      * Annotations are RuntimeVisible only. This allow us to not visit RuntimeInvisible ones.
      *
@@ -206,15 +223,15 @@ public class Aj5Attributes {
         // in order to be able to resolve the pointcut references later on
         //FIXME alex loop over class super class
         //FIXME alex can that be too slow ?
-        for (int m = 0; m < javaClass.getMethods().length; m++) {
-            Method method = javaClass.getMethods()[m];
+        for (int i = 0; i < javaClass.getMethods().length; i++) {
+            Method method = javaClass.getMethods()[i];
                        if (method.getName().startsWith(NameMangler.PREFIX)) continue;  // already dealt with by ajc...
             //FIXME alex optimize, this method struct will gets recreated for advice extraction
             AjAttributeMethodStruct mstruct = new AjAttributeMethodStruct(method, type, context, msgHandler);
             Attribute[] mattributes = method.getAttributes();
 
-            for (int i = 0; i < mattributes.length; i++) {
-                Attribute mattribute = mattributes[i];
+            for (int j = 0; j < mattributes.length; j++) {
+                Attribute mattribute = mattributes[j];
                 if (acceptAttribute(mattribute)) {
                     RuntimeAnnotations mrvs = (RuntimeAnnotations) mattribute;
                     handlePointcutAnnotation(mrvs, mstruct);
@@ -224,6 +241,40 @@ public class Aj5Attributes {
             }
             struct.ajAttributes.addAll(mstruct.ajAttributes);
         }
+
+
+        // code style declare error / warning are class attributes
+        for (int i = 0; i < javaClass.getFields().length; i++) {
+            Field field = javaClass.getFields()[i];
+                       if (field.getName().startsWith(NameMangler.PREFIX)) continue;  // already dealt with by ajc...
+            //FIXME alex optimize, this method struct will gets recreated for advice extraction
+            AjAttributeFieldStruct fstruct = new AjAttributeFieldStruct(field, type, context, msgHandler);
+            Attribute[] fattributes = field.getAttributes();
+
+            for (int j = 0; j < fattributes.length; j++) {
+                Attribute fattribute = fattributes[j];
+                if (acceptAttribute(fattribute)) {
+                    RuntimeAnnotations frvs = (RuntimeAnnotations) fattribute;
+                    if (handleDeclareErrorOrWarningAnnotation(frvs, fstruct)) {
+                        // semantic check - must be in an @Aspect [remove if previous block bypassed in advance]
+                        if (!type.isAnnotationStyleAspect()) {
+                            msgHandler.handleMessage(
+                                    new Message(
+                                            "Found @AspectJ annotations in a non @Aspect type '" + type.getName() + "'",
+                                            IMessage.WARNING,
+                                            null,
+                                            type.getSourceLocation()
+                                    )
+                            );
+                            ;// go ahead
+                        }
+                    }
+                    // there can only be one RuntimeVisible bytecode attribute
+                    break;
+                }
+            }
+            struct.ajAttributes.addAll(fstruct.ajAttributes);
+        }
         return struct.ajAttributes;
     }
 
@@ -315,8 +366,7 @@ public class Aj5Attributes {
      * @return list of AjAttributes, always empty for now
      */
     public static List readAj5FieldAttributes(Field field, ResolvedTypeX type, ISourceContext context,IMessageHandler msgHandler) {
-               if (field.getName().startsWith(NameMangler.PREFIX)) return Collections.EMPTY_LIST;  // already dealt with by ajc...
-        return EMPTY_LIST;
+        return Collections.EMPTY_LIST;
     }
 
     /**
@@ -530,7 +580,14 @@ public class Aj5Attributes {
             String pointcut = null;
             String returned = null;
             if ((annValue!=null && annPointcut!=null) || (annValue==null && annPointcut==null)) {
-                throw new RuntimeException("AfterReturning at most value or pointcut must be filled");
+                struct.handler.handleMessage(
+                        new Message(
+                                "@AfterReturning: either 'value' or 'poincut' must be provided, not both: " + methodToString(struct.method),
+                                struct.enclosingType.getSourceLocation(),
+                                true
+                        )
+                );
+                return false;
             }
             if (annValue != null) {
                 pointcut = annValue.getValue().stringifyValue();
@@ -538,7 +595,14 @@ public class Aj5Attributes {
                 pointcut = annPointcut.getValue().stringifyValue();
             }
             if (isNullOrEmpty(pointcut)) {
-                throw new RuntimeException("AfterReturning pointcut unspecified");
+                struct.handler.handleMessage(
+                        new Message(
+                                "@AfterReturning: either 'value' or 'poincut' must be provided, not both: " + methodToString(struct.method),
+                                struct.enclosingType.getSourceLocation(),
+                                true
+                        )
+                );
+                return false;
             }
             if (annReturned!=null) {
                 returned = annReturned.getValue().stringifyValue();
@@ -608,7 +672,14 @@ public class Aj5Attributes {
             String pointcut = null;
             String throwned = null;
             if ((annValue!=null && annPointcut!=null) || (annValue==null && annPointcut==null)) {
-                throw new RuntimeException("AfterReturning at most value or pointcut must be filled");
+                struct.handler.handleMessage(
+                        new Message(
+                                "@AfterThrowing: either 'value' or 'poincut' must be provided, not both: " + methodToString(struct.method),
+                                struct.enclosingType.getSourceLocation(),
+                                true
+                        )
+                );
+                return false;
             }
             if (annValue != null) {
                 pointcut = annValue.getValue().stringifyValue();
@@ -616,7 +687,14 @@ public class Aj5Attributes {
                 pointcut = annPointcut.getValue().stringifyValue();
             }
             if (isNullOrEmpty(pointcut)) {
-                throw new RuntimeException("AfterReturning pointcut unspecified");
+                struct.handler.handleMessage(
+                        new Message(
+                                "@AfterThrowing: either 'value' or 'poincut' must be provided, not both: " + methodToString(struct.method),
+                                struct.enclosingType.getSourceLocation(),
+                                true
+                        )
+                );
+                return false;
             }
             if (annThrowned!=null) {
                 throwned = annThrowned.getValue().stringifyValue();
@@ -800,6 +878,69 @@ public class Aj5Attributes {
         }
     }
 
+    /**
+     * Read @DeclareError, @DeclareWarning
+     *
+     * @param runtimeAnnotations
+     * @param struct
+     * @return true if found
+     */
+    private static boolean handleDeclareErrorOrWarningAnnotation(RuntimeAnnotations runtimeAnnotations, AjAttributeFieldStruct struct) {
+        Annotation error = getAnnotation(runtimeAnnotations, "org.aspectj.lang.annotation.DeclareError");
+        boolean hasError = false;
+        if (error != null) {
+            ElementNameValuePair declareError = getAnnotationElement(error, "value");
+            if (declareError != null) {
+                if (!"Ljava/lang/String;".equals(struct.field.getSignature()) || struct.field.getConstantValue()==null) {
+                    struct.handler.handleMessage(
+                            new Message(
+                                    "@DeclareError used on a non String constant field " + fieldToString(struct.field),
+                                    struct.enclosingType.getSourceLocation(),
+                                    true
+                    ));
+                    return false;
+                }
+                FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0];
+                IScope binding = new BindingScope(
+                        struct.enclosingType,
+                        bindings
+                );
+                               Pointcut pc = Pointcut.fromString(declareError.getValue().stringifyValue()).resolve(binding);
+                struct.ajAttributes.add(new AjAttribute.DeclareAttribute(
+                        new DeclareErrorOrWarning(true, pc, struct.field.getConstantValue().toString())
+                ));
+                return hasError = true;
+            }
+        }
+        Annotation warning = getAnnotation(runtimeAnnotations, "org.aspectj.lang.annotation.DeclareWarning");
+        boolean hasWarning = false;
+        if (warning != null) {
+            ElementNameValuePair declareWarning = getAnnotationElement(warning, "value");
+            if (declareWarning != null) {
+                if (!"Ljava/lang/String;".equals(struct.field.getSignature()) || struct.field.getConstantValue()==null) {
+                    struct.handler.handleMessage(
+                            new Message(
+                                    "@DeclareWarning used on a non String constant field " + fieldToString(struct.field),
+                                    struct.enclosingType.getSourceLocation(),
+                                    true
+                    ));
+                    return false;
+                }
+                FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0];
+                IScope binding = new BindingScope(
+                        struct.enclosingType,
+                        bindings
+                );
+                               Pointcut pc = Pointcut.fromString(declareWarning.getValue().stringifyValue()).resolve(binding);
+                struct.ajAttributes.add(new AjAttribute.DeclareAttribute(
+                        new DeclareErrorOrWarning(false, pc, struct.field.getConstantValue().toString())
+                ));
+                return hasWarning = true;
+            }
+        }
+        return hasError || hasWarning;
+    }
+
     /**
      * Returns a readable representation of a method.
      * Method.toString() is not suitable.
@@ -814,6 +955,20 @@ public class Aj5Attributes {
         return sb.toString();
     }
 
+    /**
+     * Returns a readable representation of a field.
+     * Field.toString() is not suitable.
+     *
+     * @param field
+     * @return
+     */
+    private static String fieldToString(Field field) {
+        StringBuffer sb = new StringBuffer();
+        sb.append(field.getName()).append(' ');
+        sb.append(field.getSignature());
+        return sb.toString();
+    }
+
     /**
      * Build the bindings for a given method (pointcut / advice)
      *