use of org.checkerframework.javacutil.BugInCF in project checker-framework by typetools.
the class WholeProgramInferenceJavaParserStorage method createWrappersForClass.
/**
* The first two arugments are a javac tree and a JavaParser node representing the same class.
* This method creates wrappers around all the classes, fields, and methods in that class, and
* stores those wrappers in {@code sourceAnnos}.
*
* @param javacClass javac tree for class
* @param javaParserClass JavaParser node corresponding to the same class as {@code javacClass}
* @param sourceAnnos compilation unit wrapper to add new wrappers to
*/
private void createWrappersForClass(ClassTree javacClass, TypeDeclaration<?> javaParserClass, CompilationUnitAnnos sourceAnnos) {
JointJavacJavaParserVisitor visitor = new DefaultJointVisitor() {
@Override
public void processClass(ClassTree javacTree, ClassOrInterfaceDeclaration javaParserNode) {
addClass(javacTree);
}
@Override
public void processClass(ClassTree javacTree, EnumDeclaration javaParserNode) {
addClass(javacTree);
}
@Override
public void processClass(ClassTree javacTree, RecordDeclaration javaParserNode) {
addClass(javacTree);
}
@Override
public void processNewClass(NewClassTree javacTree, ObjectCreationExpr javaParserNode) {
if (javacTree.getClassBody() != null) {
addClass(javacTree.getClassBody());
}
}
/**
* Creates a wrapper around the class for {@code tree} and stores it in {@code
* sourceAnnos}.
*
* @param tree tree to add
*/
private void addClass(ClassTree tree) {
TypeElement classElt = TreeUtils.elementFromDeclaration(tree);
String className = ElementUtils.getBinaryName(classElt);
ClassOrInterfaceAnnos typeWrapper = new ClassOrInterfaceAnnos();
if (!classToAnnos.containsKey(className)) {
classToAnnos.put(className, typeWrapper);
}
sourceAnnos.types.add(typeWrapper);
}
@Override
public void processMethod(MethodTree javacTree, MethodDeclaration javaParserNode) {
addCallableDeclaration(javacTree, javaParserNode);
}
@Override
public void processMethod(MethodTree javacTree, ConstructorDeclaration javaParserNode) {
addCallableDeclaration(javacTree, javaParserNode);
}
/**
* Creates a wrapper around {@code javacTree} with the corresponding declaration {@code
* javaParserNode} and stores it in {@code sourceAnnos}.
*
* @param javacTree javac tree for declaration to add
* @param javaParserNode JavaParser node for the same class as {@code javacTree}
*/
private void addCallableDeclaration(MethodTree javacTree, CallableDeclaration<?> javaParserNode) {
ExecutableElement elt = TreeUtils.elementFromDeclaration(javacTree);
String className = ElementUtils.getEnclosingClassName(elt);
ClassOrInterfaceAnnos enclosingClass = classToAnnos.get(className);
String executableSignature = JVMNames.getJVMMethodSignature(javacTree);
if (!enclosingClass.callableDeclarations.containsKey(executableSignature)) {
enclosingClass.callableDeclarations.put(executableSignature, new CallableDeclarationAnnos(javaParserNode));
}
}
@Override
public void processVariable(VariableTree javacTree, EnumConstantDeclaration javaParserNode) {
VariableElement elt = TreeUtils.elementFromDeclaration(javacTree);
if (!elt.getKind().isField()) {
throw new BugInCF(elt + " is not a field but a " + elt.getKind());
}
String enclosingClassName = ElementUtils.getEnclosingClassName(elt);
ClassOrInterfaceAnnos enclosingClass = classToAnnos.get(enclosingClassName);
String fieldName = javacTree.getName().toString();
enclosingClass.enumConstants.add(fieldName);
// Ensure that if an enum constant defines a class, that class gets registered properly.
// See e.g. https://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.9.1 for
// the specification of an enum constant, which does permit it to define an anonymous
// class.
NewClassTree constructor = (NewClassTree) javacTree.getInitializer();
if (constructor.getClassBody() != null) {
addClass(constructor.getClassBody());
}
}
@Override
public void processVariable(VariableTree javacTree, VariableDeclarator javaParserNode) {
// below call to TreeUtils.elementFromDeclaration causes a crash.
if (TreeUtils.elementFromTree(javacTree) == null) {
return;
}
VariableElement elt = TreeUtils.elementFromDeclaration(javacTree);
if (!elt.getKind().isField()) {
return;
}
String enclosingClassName = ElementUtils.getEnclosingClassName(elt);
ClassOrInterfaceAnnos enclosingClass = classToAnnos.get(enclosingClassName);
String fieldName = javacTree.getName().toString();
if (!enclosingClass.fields.containsKey(fieldName)) {
enclosingClass.fields.put(fieldName, new FieldAnnos(javaParserNode));
}
}
};
visitor.visitClass(javacClass, javaParserClass);
}
use of org.checkerframework.javacutil.BugInCF in project checker-framework by typetools.
the class JointJavacJavaParserVisitor method visitWildcard.
@Override
public Void visitWildcard(WildcardTree javacTree, Node javaParserNode) {
WildcardType node = castNode(WildcardType.class, javaParserNode, javacTree);
processWildcard(javacTree, node);
// In javac, whether the bound is an extends or super clause depends on the kind of the tree.
assert (javacTree.getKind() == Tree.Kind.EXTENDS_WILDCARD) == node.getExtendedType().isPresent();
assert (javacTree.getKind() == Tree.Kind.SUPER_WILDCARD) == node.getSuperType().isPresent();
switch(javacTree.getKind()) {
case UNBOUNDED_WILDCARD:
break;
case EXTENDS_WILDCARD:
javacTree.getBound().accept(this, node.getExtendedType().get());
break;
case SUPER_WILDCARD:
javacTree.getBound().accept(this, node.getSuperType().get());
break;
default:
throw new BugInCF("Unexpected wildcard kind: %s", javacTree);
}
return null;
}
use of org.checkerframework.javacutil.BugInCF in project checker-framework by typetools.
the class AnnotationFileParser method putMerge.
/**
* Just like Map.put, but modifies an existing annotated type for the given key in {@code m}. If
* {@code m} already has an annotated type for {@code key}, each annotation in {@code newType}
* will replace annotations from the same hierarchy at the same location in the existing annotated
* type. Annotations in other hierarchies will be preserved.
*
* @param m the map to put the new type into
* @param key the key for the map
* @param newType the new type for the key
*/
private void putMerge(Map<Element, AnnotatedTypeMirror> m, Element key, AnnotatedTypeMirror newType) {
if (key == null) {
throw new BugInCF("AnnotationFileParser: key is null");
}
if (m.containsKey(key)) {
AnnotatedTypeMirror existingType = m.get(key);
// This works because the JDK is always parsed last, on demand, after all other stub files.
if (fileType != AnnotationFileType.JDK_STUB) {
atypeFactory.replaceAnnotations(newType, existingType);
}
m.put(key, existingType);
} else {
m.put(key, newType);
}
}
use of org.checkerframework.javacutil.BugInCF in project checker-framework by typetools.
the class AnnotationFileParser method getAnnotation.
/**
* Convert {@code annotation} into an AnnotationMirror. Returns null if the annotation isn't
* supported by the checker or if some error occurred while converting it.
*
* @param annotation syntax tree for an annotation
* @param allAnnotations map from simple name to annotation definition; side-effected by this
* method
* @return the AnnotationMirror for the annotation, or null if it cannot be built
*/
@Nullable
private AnnotationMirror getAnnotation(AnnotationExpr annotation, Map<String, TypeElement> allAnnotations) {
// https://tinyurl.com/cfissue/3094
@SuppressWarnings("signature") @FullyQualifiedName String annoNameFq = annotation.getNameAsString();
TypeElement annoTypeElt = allAnnotations.get(annoNameFq);
if (annoTypeElt == null) {
// If the annotation was not imported, then #getImportedAnnotations did not add it to the
// allAnnotations field. This code adds the annotation when it is encountered (i.e. here).
// Note that this does not call AnnotationFileParser#getTypeElement to avoid a spurious
// diagnostic if the annotation is actually unknown.
annoTypeElt = elements.getTypeElement(annoNameFq);
if (annoTypeElt == null) {
// Not a supported annotation -> ignore
return null;
}
putAllNew(allAnnotations, createNameToAnnotationMap(Collections.singletonList(annoTypeElt)));
}
// not anonymous, so name is not empty
@SuppressWarnings("signature") @CanonicalName String annoName = annoTypeElt.getQualifiedName().toString();
if (annotation instanceof MarkerAnnotationExpr) {
return AnnotationBuilder.fromName(elements, annoName);
} else if (annotation instanceof NormalAnnotationExpr) {
NormalAnnotationExpr nrmanno = (NormalAnnotationExpr) annotation;
AnnotationBuilder builder = new AnnotationBuilder(processingEnv, annoName);
List<MemberValuePair> pairs = nrmanno.getPairs();
if (pairs != null) {
for (MemberValuePair mvp : pairs) {
String member = mvp.getNameAsString();
Expression exp = mvp.getValue();
try {
builderAddElement(builder, member, exp);
} catch (AnnotationFileParserException e) {
warn(exp, "For annotation %s, could not add %s=%s because %s", annotation, member, exp, e.getMessage());
return null;
}
}
}
return builder.build();
} else if (annotation instanceof SingleMemberAnnotationExpr) {
SingleMemberAnnotationExpr sglanno = (SingleMemberAnnotationExpr) annotation;
AnnotationBuilder builder = new AnnotationBuilder(processingEnv, annoName);
Expression valExpr = sglanno.getMemberValue();
try {
builderAddElement(builder, "value", valExpr);
} catch (AnnotationFileParserException e) {
warn(valExpr, "For annotation %s, could not add value=%s because %s", annotation, valExpr, e.getMessage());
return null;
}
return builder.build();
} else {
throw new BugInCF("AnnotationFileParser: unknown annotation type: " + annotation);
}
}
use of org.checkerframework.javacutil.BugInCF in project checker-framework by typetools.
the class AnnotationFileParser method processTypeDecl.
/**
* Process a type declaration: copy its annotations to {@code #annotationFileAnnos}.
*
* <p>This method stores the declaration's type parameters in {@link #typeParameters}. When
* processing an ajava file, where traversal is handled externaly by a {@link
* org.checkerframework.framework.ajava.JointJavacJavaParserVisitor}, these type variables must be
* removed after processing the type's members. Otherwise, this method removes them.
*
* @param typeDecl the type declaration to process
* @param outertypeName the name of the containing class, when processing a nested class;
* otherwise null
* @param classTree the tree corresponding to typeDecl if processing an ajava file, null otherwise
* @return a list of types variables for {@code typeDecl}. Only non-null if processing an ajava
* file, in which case the contents should be removed from {@link #typeParameters} after
* processing the type declaration's members
*/
private List<AnnotatedTypeVariable> processTypeDecl(TypeDeclaration<?> typeDecl, String outertypeName, @Nullable ClassTree classTree) {
assert typeBeingParsed != null;
if (skipNode(typeDecl)) {
return null;
}
String innerName;
@FullyQualifiedName String fqTypeName;
TypeElement typeElt;
if (classTree != null) {
typeElt = TreeUtils.elementFromDeclaration(classTree);
innerName = typeElt.getQualifiedName().toString();
typeBeingParsed = new FqName(typeBeingParsed.packageName, innerName);
fqTypeName = typeBeingParsed.toString();
} else {
String packagePrefix = outertypeName == null ? "" : outertypeName + ".";
innerName = packagePrefix + typeDecl.getNameAsString();
typeBeingParsed = new FqName(typeBeingParsed.packageName, innerName);
fqTypeName = typeBeingParsed.toString();
typeElt = elements.getTypeElement(fqTypeName);
}
if (!isAnnotatedForThisChecker(typeDecl.getAnnotations())) {
return null;
}
if (typeElt == null) {
if (debugAnnotationFileParser || (!warnIfNotFoundIgnoresClasses && !hasNoAnnotationFileParserWarning(typeDecl.getAnnotations()) && !hasNoAnnotationFileParserWarning(packageAnnos))) {
if (elements.getAllTypeElements(fqTypeName).isEmpty()) {
stubWarnNotFound(typeDecl, "Type not found: " + fqTypeName);
} else {
stubWarnNotFound(typeDecl, "Type not found uniquely: " + fqTypeName + " : " + elements.getAllTypeElements(fqTypeName));
}
}
return null;
}
List<AnnotatedTypeVariable> typeDeclTypeParameters = null;
if (typeElt.getKind() == ElementKind.ENUM) {
if (!(typeDecl instanceof EnumDeclaration)) {
warn(typeDecl, innerName + " is an enum, but stub file declared it as " + typeDecl.toString().split("\\R", 2)[0] + "...");
return null;
}
typeDeclTypeParameters = processEnum((EnumDeclaration) typeDecl, typeElt);
typeParameters.addAll(typeDeclTypeParameters);
} else if (typeElt.getKind() == ElementKind.ANNOTATION_TYPE) {
if (!(typeDecl instanceof AnnotationDeclaration)) {
warn(typeDecl, innerName + " is an annotation, but stub file declared it as " + typeDecl.toString().split("\\R", 2)[0] + "...");
return null;
}
stubWarnNotFound(typeDecl, "Skipping annotation type: " + fqTypeName);
} else if (typeDecl instanceof ClassOrInterfaceDeclaration) {
if (!(typeDecl instanceof ClassOrInterfaceDeclaration)) {
warn(typeDecl, innerName + " is a class or interface, but stub file declared it as " + typeDecl.toString().split("\\R", 2)[0] + "...");
return null;
}
typeDeclTypeParameters = processType(typeDecl, typeElt);
typeParameters.addAll(typeDeclTypeParameters);
} else if (typeDecl instanceof RecordDeclaration) {
typeDeclTypeParameters = processType(typeDecl, typeElt);
typeParameters.addAll(typeDeclTypeParameters);
}
// of this method.
if (fileType == AnnotationFileType.AJAVA) {
return typeDeclTypeParameters;
}
if (typeDecl instanceof RecordDeclaration) {
NodeList<Parameter> recordMembers = ((RecordDeclaration) typeDecl).getParameters();
LinkedHashMap<String, RecordComponentStub> byName = new LinkedHashMap<>();
for (Parameter recordMember : recordMembers) {
RecordComponentStub stub = processRecordField(recordMember, findFieldElement(typeElt, recordMember.getNameAsString(), recordMember));
byName.put(recordMember.getNameAsString(), stub);
}
annotationFileAnnos.records.put(typeDecl.getFullyQualifiedName().get(), new RecordStub(byName));
}
Pair<Map<Element, BodyDeclaration<?>>, Map<Element, List<BodyDeclaration<?>>>> members = getMembers(typeDecl, typeElt, typeDecl);
for (Map.Entry<Element, BodyDeclaration<?>> entry : members.first.entrySet()) {
final Element elt = entry.getKey();
final BodyDeclaration<?> decl = entry.getValue();
switch(elt.getKind()) {
case FIELD:
processField((FieldDeclaration) decl, (VariableElement) elt);
break;
case ENUM_CONSTANT:
// the TRACKER enum constant annotated with DefaultType:
if (decl instanceof FieldDeclaration) {
processField((FieldDeclaration) decl, (VariableElement) elt);
} else if (decl instanceof EnumConstantDeclaration) {
processEnumConstant((EnumConstantDeclaration) decl, (VariableElement) elt);
} else {
throw new BugInCF("Unexpected decl type " + decl.getClass() + " for ENUM_CONSTANT kind, original: " + decl);
}
break;
case CONSTRUCTOR:
case METHOD:
processCallableDeclaration((CallableDeclaration<?>) decl, (ExecutableElement) elt);
break;
case CLASS:
case INTERFACE:
// Not processing an ajava file, so ignore the return value.
processTypeDecl((ClassOrInterfaceDeclaration) decl, innerName, null);
break;
case ENUM:
// Not processing an ajava file, so ignore the return value.
processTypeDecl((EnumDeclaration) decl, innerName, null);
break;
default:
/* do nothing */
stubWarnNotFound(decl, "AnnotationFileParser ignoring: " + elt);
break;
}
}
for (Map.Entry<Element, List<BodyDeclaration<?>>> entry : members.second.entrySet()) {
ExecutableElement fakeOverridden = (ExecutableElement) entry.getKey();
List<BodyDeclaration<?>> fakeOverrideDecls = entry.getValue();
for (BodyDeclaration<?> bodyDecl : fakeOverrideDecls) {
processFakeOverride(fakeOverridden, (CallableDeclaration<?>) bodyDecl, typeElt);
}
}
if (typeDeclTypeParameters != null) {
typeParameters.removeAll(typeDeclTypeParameters);
}
return null;
}
Aggregations