unknownAnotationAndUnwritten.removeAll(data.writtenFields);
declaredFields.removeAll(unknownAnotationAndUnwritten);
declaredFields.removeAll(data.containerFields);
declaredFields.removeAll(data.reflectiveFields);
for (Iterator<XField> i = declaredFields.iterator(); i.hasNext();) {
XField f = i.next();
if (f.isSynthetic() && !f.getName().startsWith("this$") || f.getName().startsWith("_")) {
i.remove();
}
}
TreeSet<XField> notInitializedInConstructors = new TreeSet<XField>(declaredFields);
notInitializedInConstructors.retainAll(data.readFields);
notInitializedInConstructors.retainAll(data.writtenNonNullFields);
notInitializedInConstructors.retainAll(data.assumedNonNull.keySet());
notInitializedInConstructors.removeAll(data.writtenInConstructorFields);
notInitializedInConstructors.removeAll(data.writtenInInitializationFields);
for (Iterator<XField> i = notInitializedInConstructors.iterator(); i.hasNext();) {
if (i.next().isStatic()) {
i.remove();
}
}
TreeSet<XField> readOnlyFields = new TreeSet<XField>(declaredFields);
readOnlyFields.removeAll(data.writtenFields);
readOnlyFields.retainAll(data.readFields);
TreeSet<XField> nullOnlyFields = new TreeSet<XField>(declaredFields);
nullOnlyFields.removeAll(data.writtenNonNullFields);
nullOnlyFields.retainAll(data.readFields);
Set<XField> writeOnlyFields = declaredFields;
writeOnlyFields.removeAll(data.readFields);
Map<String, Integer> count = new HashMap<String, Integer>();
Bag<String> nullOnlyFieldNames = new Bag<String>();
Bag<ClassDescriptor> classContainingNullOnlyFields = new Bag<ClassDescriptor>();
for (XField f : nullOnlyFields) {
nullOnlyFieldNames.add(f.getName());
classContainingNullOnlyFields.add(f.getClassDescriptor());
int increment = 3;
Collection<ProgramPoint> assumedNonNullAt = data.assumedNonNull.get(f);
if (assumedNonNullAt != null) {
increment += assumedNonNullAt.size();
}
for (String s : data.unknownAnnotation.get(f)) {
Integer value = count.get(s);
if (value == null) {
count.put(s, increment);
} else {
count.put(s, value + increment);
}
}
}
Map<XField, Integer> maxCount = new HashMap<XField, Integer>();
LinkedList<XField> assumeReflective = new LinkedList<XField>();
for (XField f : nullOnlyFields) {
int myMaxCount = 0;
for (String s : data.unknownAnnotation.get(f)) {
Integer value = count.get(s);
if (value != null && myMaxCount < value) {
myMaxCount = value;
}
}
if (myMaxCount > 0) {
maxCount.put(f, myMaxCount);
}
if (myMaxCount > 15) {
assumeReflective.add(f);
} else if (nullOnlyFieldNames.getCount(f.getName()) > 8) {
assumeReflective.add(f);
} else if (classContainingNullOnlyFields.getCount(f.getClassDescriptor()) > 4) {
assumeReflective.add(f);
} else if (classContainingNullOnlyFields.getCount(f.getClassDescriptor()) > 2 && f.getName().length() == 1) {
assumeReflective.add(f);
}
}
readOnlyFields.removeAll(assumeReflective);
nullOnlyFields.removeAll(assumeReflective);
notInitializedInConstructors.removeAll(assumeReflective);
Bag<String> notInitializedUses = new Bag<String>();
for (XField f : notInitializedInConstructors) {
String className = f.getClassName();
Set<ProgramPoint> assumedNonnullAt = data.assumedNonNull.get(f);
notInitializedUses.add(className, assumedNonnullAt.size());
}
for (XField f : notInitializedInConstructors) {
String className = f.getClassName();
if (notInitializedUses.getCount(className) >= 8) {
continue;
}
String fieldSignature = f.getSignature();
if (f.isResolved() && !data.fieldsOfNativeClasses.contains(f)
&& (fieldSignature.charAt(0) == 'L' || fieldSignature.charAt(0) == '[')) {
int priority = LOW_PRIORITY;
Set<ProgramPoint> assumedNonnullAt = data.assumedNonNull.get(f);
if (assumedNonnullAt.size() < 4) {
for (ProgramPoint p : assumedNonnullAt) {
BugInstance bug = new BugInstance(this, "UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR", priority)
.addClass(className).addField(f).addMethod(p.getMethodAnnotation());
bugAccumulator.accumulateBug(bug, p.getSourceLineAnnotation());
}
}
}
}
for (XField f : readOnlyFields) {
// String fieldName = f.getName();
// String className = f.getClassName();
String fieldSignature = f.getSignature();
if (f.isResolved() && !data.fieldsOfNativeClasses.contains(f)) {
int priority = NORMAL_PRIORITY;
if (!(fieldSignature.charAt(0) == 'L' || fieldSignature.charAt(0) == '[')) {
priority++;
}
if (maxCount.containsKey(f)) {
priority++;
}
String pattern = "UWF_UNWRITTEN_FIELD";
if (f.isProtected() || f.isPublic()) {
pattern = "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD";
}
bugReporter.reportBug(addClassFieldAndAccess(new BugInstance(this, pattern, priority), f));
}
}
for (XField f : nullOnlyFields) {
// String fieldName = f.getName();
// String className = f.getClassName();
// String fieldSignature = f.getSignature();
if (DEBUG) {
System.out.println("Null only: " + f);
System.out.println(" : " + data.assumedNonNull.containsKey(f));
System.out.println(" : " + data.fieldsOfSerializableOrNativeClassed.contains(f));
System.out.println(" : " + fieldNamesSet.contains(f.getName()));
System.out.println(" : " + data.abstractClasses.contains(f.getClassName()));
System.out.println(" : " + data.hasNonAbstractSubClass.contains(f.getClassName()));
System.out.println(" : " + f.isResolved());
}
if (!f.isResolved()) {
continue;
}
if (data.fieldsOfNativeClasses.contains(f)) {
continue;
}
if (DEBUG) {
System.out.println("Ready to report");
}
int priority = NORMAL_PRIORITY;
if (maxCount.containsKey(f)) {
priority++;
}
if (data.abstractClasses.contains(f.getClassName())) {
priority++;
if (!data.hasNonAbstractSubClass.contains(f.getClassName())) {
priority++;
}
}
// if (fieldNamesSet.contains(f.getName())) priority++;
if (data.assumedNonNull.containsKey(f)) {
int npPriority = priority;
Set<ProgramPoint> assumedNonNullAt = data.assumedNonNull.get(f);
if (assumedNonNullAt.size() > 14) {
npPriority += 2;
} else if (assumedNonNullAt.size() > 6) {
npPriority++;
} else {
priority--;
}
String pattern = (f.isPublic() || f.isProtected()) ? "NP_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD"
: "NP_UNWRITTEN_FIELD";
for (ProgramPoint p : assumedNonNullAt) {
bugAccumulator.accumulateBug(
new BugInstance(this, pattern, npPriority).addClassAndMethod(p.method).addField(f),
p.getSourceLineAnnotation());
}
} else {
if (f.isStatic()) {
priority++;
}
if (f.isFinal()) {
priority++;
}
if (data.fieldsOfSerializableOrNativeClassed.contains(f)) {
priority++;
}
}
if (!readOnlyFields.contains(f)) {
bugReporter.reportBug(addClassFieldAndAccess(new BugInstance(this, "UWF_NULL_FIELD", priority), f)
.lowerPriorityIfDeprecated());
}
}
writeOnlyFields: for (XField f : writeOnlyFields) {
String fieldName = f.getName();
String className = f.getClassName();
int lastDollar = Math.max(className.lastIndexOf('$'), className.lastIndexOf('+'));
boolean isAnonymousInnerClass = (lastDollar > 0) && (lastDollar < className.length() - 1)
&& Character.isDigit(className.charAt(lastDollar + 1));
if (DEBUG) {
System.out.println("Checking write only field " + className + "." + fieldName + "\t" + data.constantFields.contains(f)
+ "\t" + f.isStatic());
}
if (!f.isResolved()) {
continue;
}
if (dontComplainAbout.matcher(fieldName).find()) {
continue;
}
if (lastDollar >= 0 && (fieldName.startsWith("this$") || fieldName.startsWith("this+"))) {
String outerClassName = className.substring(0, lastDollar);
try {
XClass thisClass = Global.getAnalysisCache().getClassAnalysis(XClass.class, f.getClassDescriptor());
if (isAnonymousInnerClass) {
for (XField f2 : thisClass.getXFields()) {
if (f2 != f && f2.isPrivate() && f2.isSynthetic() && !f2.getName().startsWith("this$")
&& f2.getName().contains("$")) {
continue writeOnlyFields;
}
}
}
JavaClass outerClass = Repository.lookupClass(outerClassName);
if (classHasParameter(outerClass)) {
continue;
}
ClassDescriptor cDesc = DescriptorFactory.createClassDescriptorFromDottedClassName(outerClassName);
XClass outerXClass = Global.getAnalysisCache().getClassAnalysis(XClass.class, cDesc);
AnalysisContext analysisContext = AnalysisContext.currentAnalysisContext();
Subtypes2 subtypes2 = analysisContext.getSubtypes2();
for (XField of : outerXClass.getXFields()) {
if (!of.isStatic()) {
String sourceSignature = of.getSourceSignature();
if (sourceSignature != null && of.getSignature().equals("Ljava/lang/ThreadLocal;")) {
Type ofType = GenericUtilities.getType(sourceSignature);
if (ofType instanceof GenericObjectType) {
GenericObjectType gType = (GenericObjectType) ofType;
for (ReferenceType r : gType.getParameters()) {
if (r instanceof ObjectType) {
ClassDescriptor c = DescriptorFactory.getClassDescriptor((ObjectType) r);
if (subtypes2.isSubtype(f.getClassDescriptor(), c)) {
ProgramPoint p = data.threadLocalAssignedInConstructor.get(of);
int priority = p == null ? NORMAL_PRIORITY : HIGH_PRIORITY;
BugInstance bug = new BugInstance(this, "SIC_THREADLOCAL_DEADLY_EMBRACE",
priority).addClass(className).addField(of);
if (p != null) {
bug.addMethod(p.method).add(p.getSourceLineAnnotation());
}
bugReporter.reportBug(bug);
}
}
}
}
}
}
}
boolean outerClassIsInnerClass = false;
for (Field field : outerClass.getFields()) {
if (field.getName().equals("this$0")) {
outerClassIsInnerClass = true;
}
}
if (outerClassIsInnerClass) {
continue;
}
} catch (ClassNotFoundException e) {
bugReporter.reportMissingClass(e);
} catch (CheckedAnalysisException e) {
bugReporter.logError("Error getting outer XClass for " + outerClassName, e);
}
if (!data.innerClassCannotBeStatic.contains(className)) {
boolean easyChange = !data.needsOuterObjectInConstructor.contains(className);
if (easyChange || !isAnonymousInnerClass) {
// easyChange isAnonymousInnerClass
// true false medium, SIC
// true true low, SIC_ANON
// false true not reported
// false false low, SIC_THIS
int priority = LOW_PRIORITY;
if (easyChange && !isAnonymousInnerClass) {
priority = NORMAL_PRIORITY;
}
String bug = "SIC_INNER_SHOULD_BE_STATIC";
if (isAnonymousInnerClass) {
bug = "SIC_INNER_SHOULD_BE_STATIC_ANON";
} else if (!easyChange) {
bug = "SIC_INNER_SHOULD_BE_STATIC_NEEDS_THIS";
}
bugReporter.reportBug(new BugInstance(this, bug, priority).addClass(className));
}
}
} else if (f.isResolved()) {
if (data.constantFields.contains(f)) {
if (!f.isStatic()) {
bugReporter.reportBug(addClassFieldAndAccess(
new BugInstance(this, "SS_SHOULD_BE_STATIC", NORMAL_PRIORITY), f));
}
} else if (data.fieldsOfSerializableOrNativeClassed.contains(f)) {
// ignore it
} else if (!data.writtenFields.contains(f)) {
bugReporter.reportBug(new BugInstance(this,
(f.isPublic() || f.isProtected()) ? "UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD" : "UUF_UNUSED_FIELD",
NORMAL_PRIORITY).addClass(className).addField(f).lowerPriorityIfDeprecated());
} else if (f.getName().toLowerCase().indexOf("guardian") < 0) {
int priority = NORMAL_PRIORITY;
if (f.isStatic()) {
priority++;
}
if (f.isFinal()) {
priority++;
}
bugReporter.reportBug(addClassFieldAndAccess(new BugInstance(this,
(f.isPublic() || f.isProtected()) ? "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD" : "URF_UNREAD_FIELD",
priority), f));
}
}
}
bugAccumulator.reportAccumulatedBugs();