From 9d9c64dc4fb45d15847ed1081256f8f0f975e15c Mon Sep 17 00:00:00 2001 From: jborgers Date: Thu, 16 Apr 2020 17:10:38 +0200 Subject: [PATCH] Extend AvoidUnguardedAssignmentToNonFinalFieldsInSharedObjects rule for inherited fields #18 --- rulesets/java/jpinpoint-rules.xml | 26 ++++++--- .../resources/category/java/concurrent.xml | 10 +++- ...ignmentToNonFinalFieldsInSharedObjects.xml | 54 +++++++++++++++---- 3 files changed, 70 insertions(+), 20 deletions(-) diff --git a/rulesets/java/jpinpoint-rules.xml b/rulesets/java/jpinpoint-rules.xml index a8fd2a05..e878cf44 100644 --- a/rulesets/java/jpinpoint-rules.xml +++ b/rulesets/java/jpinpoint-rules.xml @@ -701,23 +701,25 @@ ClassOrInterfaceBodyDeclaration//MethodDeclaration/Block//PrimaryExpression/Prim @@ -1314,6 +1316,7 @@ or (ancestor::ClassOrInterfaceBodyDeclaration/Annotation//Name[@Image='VisibleFo 1. Autowiring/injection is thread safe, yet make sure no other thread-unsafe assignment is made to that field. 2. In case you are sure the Component is used in single threaded context only (e.g. a Tasklet), annotate the class with @NotThreadSafe to make this explicit. 3. Use package-private and @VisibleForTesting for methods (e.g. setters) used for JUnit only. + 4. Use synchronized for accessors to inherited fields, or better: make field private and use proper accessors on base class level using @GuardedBy. (jpinpoint-rules) 2 @@ -1331,9 +1334,16 @@ and not (ancestor::TypeDeclaration/Annotation/MarkerAnnotation/Name[@Image='NotT and not ((ancestor::TypeDeclaration/Annotation//Name[@Image='ConfigurationProperties']) and not (ancestor::TypeDeclaration/Annotation/MarkerAnnotation/Name[@Image='Setter']))] (: assignment to a field :) -/../../..//ClassOrInterfaceDeclaration[@Static=false()]/ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/MethodDeclaration/Block/BlockStatement/Statement//StatementExpression/AssignmentOperator/../PrimaryExpression//*[@Image= +/../../..//ClassOrInterfaceDeclaration[@Static=false()]/ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/MethodDeclaration/Block/BlockStatement/Statement//StatementExpression/AssignmentOperator/../PrimaryExpression//*[(@Image= (: non-final, non-volatile, non-GuardedBy fields :) ancestor::ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/FieldDeclaration[@Final=false() and @Volatile=false() and not (../Annotation//Name[@Image='GuardedBy'])]/VariableDeclarator/VariableDeclaratorId/@Image +or +(: not 'this' and extends a base class :) +((@Image != '') and ancestor::ClassOrInterfaceDeclaration/ExtendsList and +(: not a field of this class, param or local :) +not (exists(index-of((ancestor::ClassOrInterfaceBody//VariableDeclaratorId/@Image), @Image)))) +and not (ancestor::SynchronizedStatement or ancestor::MethodDeclaration[@Synchronized = true()]) +) (: field not on accessor method with assignment, annotated with framework annotation :) and not (ancestor::ClassOrInterfaceBodyDeclaration/Annotation//Name[@Image='Autowired' or @Image='PostConstruct' or @Image='BeforeStep' or @Image='Value' or @Image='Inject'] (: field not assigned in non-public accessor method annotated with VisibleForTesting :) diff --git a/src/main/resources/category/java/concurrent.xml b/src/main/resources/category/java/concurrent.xml index ddbf23bf..063fda1c 100644 --- a/src/main/resources/category/java/concurrent.xml +++ b/src/main/resources/category/java/concurrent.xml @@ -182,6 +182,7 @@ ancestor::StatementExpression/PrimaryExpression/PrimaryPrefix/Name/@Image = ance 1. Autowiring/injection is thread safe, yet make sure no other thread-unsafe assignment is made to that field. 2. In case you are sure the Component is used in single threaded context only (e.g. a Tasklet), annotate the class with @NotThreadSafe to make this explicit. 3. Use package-private and @VisibleForTesting for methods (e.g. setters) used for JUnit only. + 4. Use synchronized for accessors to inherited fields, or better: make field private and use proper accessors on base class level using @GuardedBy. (jpinpoint-rules) 2 @@ -199,9 +200,16 @@ and not (ancestor::TypeDeclaration/Annotation/MarkerAnnotation/Name[@Image='NotT and not ((ancestor::TypeDeclaration/Annotation//Name[@Image='ConfigurationProperties']) and not (ancestor::TypeDeclaration/Annotation/MarkerAnnotation/Name[@Image='Setter']))] (: assignment to a field :) -/../../..//ClassOrInterfaceDeclaration[@Static=false()]/ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/MethodDeclaration/Block/BlockStatement/Statement//StatementExpression/AssignmentOperator/../PrimaryExpression//*[@Image= +/../../..//ClassOrInterfaceDeclaration[@Static=false()]/ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/MethodDeclaration/Block/BlockStatement/Statement//StatementExpression/AssignmentOperator/../PrimaryExpression//*[(@Image= (: non-final, non-volatile, non-GuardedBy fields :) ancestor::ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/FieldDeclaration[@Final=false() and @Volatile=false() and not (../Annotation//Name[@Image='GuardedBy'])]/VariableDeclarator/VariableDeclaratorId/@Image +or +(: not 'this' and extends a base class :) +((@Image != '') and ancestor::ClassOrInterfaceDeclaration/ExtendsList and +(: not a field of this class, param or local :) +not (exists(index-of((ancestor::ClassOrInterfaceBody//VariableDeclaratorId/@Image), @Image)))) +and not (ancestor::SynchronizedStatement or ancestor::MethodDeclaration[@Synchronized = true()]) +) (: field not on accessor method with assignment, annotated with framework annotation :) and not (ancestor::ClassOrInterfaceBodyDeclaration/Annotation//Name[@Image='Autowired' or @Image='PostConstruct' or @Image='BeforeStep' or @Image='Value' or @Image='Inject'] (: field not assigned in non-public accessor method annotated with VisibleForTesting :) diff --git a/src/test/resources/com/jpinpoint/perf/lang/java/ruleset/concurrent/xml/AvoidUnguardedAssignmentToNonFinalFieldsInSharedObjects.xml b/src/test/resources/com/jpinpoint/perf/lang/java/ruleset/concurrent/xml/AvoidUnguardedAssignmentToNonFinalFieldsInSharedObjects.xml index a4e1e005..f1f615e1 100644 --- a/src/test/resources/com/jpinpoint/perf/lang/java/ruleset/concurrent/xml/AvoidUnguardedAssignmentToNonFinalFieldsInSharedObjects.xml +++ b/src/test/resources/com/jpinpoint/perf/lang/java/ruleset/concurrent/xml/AvoidUnguardedAssignmentToNonFinalFieldsInSharedObjects.xml @@ -5,7 +5,8 @@ xsi:schemaLocation="http://pmd.sourceforge.net/rule-tests http://pmd.sourceforge.net/rule-tests_1_0_0.xsd"> Avoid unguarded assignment to non-final fields in shared objects - 10 + 11 + 55, 87, 96, 137, 138, 157, 198, 223, 328, 338, 395