@Override public boolean matches(ExpressionTree expressionTree, VisitorState state) { Symbol sym = ASTHelpers.getSymbol(expressionTree); if (sym == null) { return false; } if (!(sym instanceof MethodSymbol)) { throw new IllegalArgumentException( "DescendantOf matcher expects a method call but found " + sym.getClass() + ". Expression: " + expressionTree); } if (sym.isStatic()) { return false; } if (methodName.equals(sym.toString())) { Type accessedReferenceType = sym.owner.type; Type collectionType = state.getTypeFromString(fullClassName); if (collectionType != null) { return state .getTypes() .isSubtype(accessedReferenceType, state.getTypes().erasure(collectionType)); } } return false; }
public static Set<MethodSymbol> findSuperMethods(MethodSymbol methodSymbol, Types types) { Set<MethodSymbol> supers = new HashSet<>(); if (methodSymbol.isStatic()) { return supers; } TypeSymbol owner = (TypeSymbol) methodSymbol.owner; // Iterates over an ordered list of all super classes and interfaces. for (Type sup : types.closure(owner.type)) { if (sup == owner.type) { continue; // Skip the owner of the method } Scope scope = sup.tsym.members(); for (Symbol sym : scope.getSymbolsByName(methodSymbol.name)) { if (sym != null && !sym.isStatic() && ((sym.flags() & Flags.SYNTHETIC) == 0) && sym.name.contentEquals(methodSymbol.name) && methodSymbol.overrides(sym, owner, types, true)) { supers.add((MethodSymbol) sym); } } } return supers; }
/** * Determines whether two expressions refer to the same variable. Note that returning false * doesn't necessarily mean the expressions do *not* refer to the same field. We don't attempt to * do any complex analysis here, just catch the obvious cases. */ public static boolean sameVariable(ExpressionTree expr1, ExpressionTree expr2) { // Throw up our hands if we're not comparing identifiers and/or field accesses. if ((expr1.getKind() != Kind.IDENTIFIER && expr1.getKind() != Kind.MEMBER_SELECT) || (expr2.getKind() != Kind.IDENTIFIER && expr2.getKind() != Kind.MEMBER_SELECT)) { return false; } Symbol sym1 = getSymbol(expr1); Symbol sym2 = getSymbol(expr2); if (sym1 == null) { throw new IllegalStateException("Couldn't get symbol for " + expr1); } else if (sym2 == null) { throw new IllegalStateException("Couldn't get symbol for " + expr2); } if (expr1.getKind() == Kind.IDENTIFIER && expr2.getKind() == Kind.IDENTIFIER) { // foo == foo? return sym1.equals(sym2); } else if (expr1.getKind() == Kind.MEMBER_SELECT && expr2.getKind() == Kind.MEMBER_SELECT) { // foo.baz.bar == foo.baz.bar? return sym1.equals(sym2) && sameVariable(((JCFieldAccess) expr1).selected, ((JCFieldAccess) expr2).selected); } else { // this.foo == foo? ExpressionTree selected = null; if (expr1.getKind() == Kind.IDENTIFIER) { selected = ((JCFieldAccess) expr2).selected; } else { selected = ((JCFieldAccess) expr1).selected; } // TODO(eaftan): really shouldn't be relying on .toString() return selected.toString().equals("this") && sym1.equals(sym2); } }
/** * Returns the type of an element when that element is viewed as a member of, or otherwise * directly contained by, a given type. For example, when viewed as a member of the parameterized * type {@code Set<String>}, the {@code Set.add} method is an {@code ExecutableType} whose * parameter is of type {@code String}. * * @param containing the containing type * @param element the element * @return the type of the element as viewed from the containing type * @throws IllegalArgumentException if the element is not a valid one for the given type */ public TypeMirror asMemberOf(DeclaredType containing, Element element) { Type site = (Type) containing; Symbol sym = (Symbol) element; if (types.asSuper(site, sym.getEnclosingElement()) == null) throw new IllegalArgumentException(sym + "@" + site); return types.memberType(site, sym); }
/** * Collect dependencies in the enclosing class * * @param from The enclosing class sym * @param to The enclosing classes references this sym. */ @Override public void reportDependence(Symbol from, Symbol to) { // Capture dependencies between the packages. deps.reportPackageDep(from.packge().fullname, to.packge().fullname); // It would be convenient to check if to.outermost comes from source or classpath // and only report it, if its from the classpath. This would reduce the amount // of data sent over the wire to the sjavac client. Can this be done? All interesting // classes are private within javac/file or javac/jvm.... deps.reportClassDep(to.outermostClass()); }
/** {@inheritDoc} */ public Collection<ConstructorDeclaration> getConstructors() { ArrayList<ConstructorDeclaration> res = new ArrayList<ConstructorDeclaration>(); for (Symbol s : getMembers(true)) { if (s.isConstructor()) { MethodSymbol m = (MethodSymbol) s; res.add((ConstructorDeclaration) env.declMaker.getExecutableDeclaration(m)); } } return res; }
private void addAllFireListenerMethods( final JavacType type, final TypeSymbol interfaze, final TypeSymbol superInterfaze) { for (Symbol member : superInterfaze.getEnclosedElements()) { if (member.getKind() != ElementKind.METHOD) continue; handler.addFireListenerMethod(type, interfaze, (MethodSymbol) member); } ClassType superInterfazeType = (ClassType) superInterfaze.type; if (superInterfazeType.interfaces_field != null) for (Type iface : superInterfazeType.interfaces_field) { addAllFireListenerMethods(type, interfaze, iface.asElement()); } }
@Override @Nullable protected Unifier defaultAction(Tree node, @Nullable Unifier unifier) { Symbol symbol = ASTHelpers.getSymbol(node); if (symbol != null && symbol.getEnclosingElement() != null && symbol .getEnclosingElement() .getQualifiedName() .contentEquals(classIdent().getQualifiedName()) && symbol.getSimpleName().contentEquals(member())) { return memberType().unify(symbol.asType(), unifier); } return null; }
/** {@inheritDoc} */ public Collection<AnnotationMirror> getAnnotationMirrors() { Collection<AnnotationMirror> res = new ArrayList<AnnotationMirror>(); for (Attribute.Compound a : sym.getAnnotationMirrors()) { res.add(env.declMaker.getAnnotationMirror(a, this)); } return res; }
/** Add a name usage to the simplifier's internal cache */ protected void addUsage(Symbol sym) { Name n = sym.getSimpleName(); List<Symbol> conflicts = nameClashes.get(n); if (conflicts == null) { conflicts = List.nil(); } if (!conflicts.contains(sym)) nameClashes.put(n, conflicts.append(sym)); }
Item invoke() { MethodType mtype = (MethodType) member.erasure(types); int argsize = Code.width(mtype.argtypes); int rescode = Code.typecode(mtype.restype); int sdiff = Code.width(rescode) - argsize; code.emitInvokestatic(pool.put(member), mtype); return stackItem[rescode]; }
/** * Determines whether a symbol has an annotation of the given type. This includes annotations * inherited from superclasses due to @Inherited. * * @param annotationType The type of the annotation to look for (e.g, "javax.annotation.Nullable") */ public static boolean hasAnnotation(Symbol sym, String annotationType, VisitorState state) { Symbol annotationSym = state.getSymbolFromString(annotationType); Symbol inheritedSym = state.getSymtab().inheritedType.tsym; if ((sym == null) || (annotationSym == null)) { return false; } if ((sym instanceof ClassSymbol) && (annotationSym.attribute(inheritedSym) != null)) { while (sym != null) { if (sym.attribute(annotationSym) != null) { return true; } sym = ((ClassSymbol) sym).getSuperclass().tsym; } return false; } else { return sym.attribute(annotationSym) != null; } }
@Override public boolean matches(ExpressionTree expressionTree, VisitorState state) { Symbol sym = ASTHelpers.getSymbol(expressionTree); if (sym != null && sym.getSimpleName().toString().startsWith("log")) { return true; } if (sym != null && sym.isStatic()) { if (sym.owner.getQualifiedName().toString().contains("Logger")) { return true; } } else if (expressionTree instanceof MemberSelectTree) { if (((MemberSelectTree) expressionTree).getExpression().toString().startsWith("log")) { return true; } } return false; }
Item invoke() { MethodType mtype = (MethodType) member.externalType(types); int rescode = Code.typecode(mtype.restype); if ((member.owner.flags() & Flags.INTERFACE) != 0) { code.emitInvokeinterface(pool.put(member), mtype); } else if (nonvirtual) { code.emitInvokespecial(pool.put(member), mtype); } else { code.emitInvokevirtual(pool.put(member), mtype); } return stackItem[rescode]; }
protected <A extends Annotation> A getAnnotation(Class<A> annoType, Symbol annotated) { if (!annoType.isAnnotation()) { throw new IllegalArgumentException("Not an annotation type: " + annoType); } String name = annoType.getName(); for (Attribute.Compound attr : annotated.getAnnotationMirrors()) { if (name.equals(attr.type.tsym.flatName().toString())) { return AnnotationProxyMaker.generateAnnotation(env, attr, annoType); } } return null; }
/** * Returns the symbols of type or package members (and constructors) that are not synthetic or * otherwise unwanted. Caches the result if "cache" is true. */ protected Collection<Symbol> getMembers(boolean cache) { if (members != null) { return members; } LinkedList<Symbol> res = new LinkedList<Symbol>(); for (Scope.Entry e = sym.members().elems; e != null; e = e.sibling) { if (e.sym != null && !unwanted(e.sym)) { res.addFirst(e.sym); } } return cache ? (members = res) : res; }
protected void printSymbol(String label, Symbol sym, Details details) { if (sym == null) { printNull(label); } else { switch (details) { case SUMMARY: printString(label, toString(sym)); break; case FULL: indent(); out.print(label); out.println( ": " + info( sym.getClass(), String.format("0x%x--%s", sym.kind.ordinal(), Kinds.kindName(sym)), sym.getKind()) + " " + sym.name + " " + hashString(sym)); indent(+1); if (showSrc) { JCTree tree = (JCTree) trees.getTree(sym); if (tree != null) printSource("src", tree); } printString( "flags", String.format("0x%x--%s", sym.flags_field, Flags.toString(sym.flags_field))); printObject("completer", sym.completer, Details.SUMMARY); // what if too long? printSymbol("owner", sym.owner, Details.SUMMARY); printType("type", sym.type, Details.SUMMARY); printType("erasure", sym.erasure_field, Details.SUMMARY); sym.accept(symVisitor, null); printAnnotations("annotations", sym.getMetadata(), Details.SUMMARY); indent(-1); } } }
/** Check if the method declares or inherits an implementation of .equals() */ public static boolean implementsEquals(Type type, VisitorState state) { Name equalsName = state.getName("equals"); Symbol objectEquals = getOnlyMember(state, state.getSymtab().objectType, "equals"); for (Type sup : state.getTypes().closure(type)) { if (sup.tsym.isInterface()) { continue; } if (ASTHelpers.isSameType(sup, state.getSymtab().objectType, state)) { return false; } WriteableScope scope = sup.tsym.members(); if (scope == null) { continue; } for (Symbol sym : scope.getSymbolsByName(equalsName)) { if (sym.overrides(objectEquals, type.tsym, state.getTypes(), false)) { return true; } } } return false; }
public String simplify(Symbol s) { String name = s.getQualifiedName().toString(); if (!s.type.isCompound()) { List<Symbol> conflicts = nameClashes.get(s.getSimpleName()); if (conflicts == null || (conflicts.size() == 1 && conflicts.contains(s))) { List<Name> l = List.nil(); Symbol s2 = s; while (s2.type.getEnclosingType().tag == CLASS && s2.owner.kind == Kinds.TYP) { l = l.prepend(s2.getSimpleName()); s2 = s2.owner; } l = l.prepend(s2.getSimpleName()); StringBuilder buf = new StringBuilder(); String sep = ""; for (Name n2 : l) { buf.append(sep); buf.append(n2); sep = "."; } name = buf.toString(); } } return name; }
/** * Extend suppression sets for both {@code @SuppressWarnings} and custom suppression annotations. * When we explore a new node, we have to extend the suppression sets with any new suppressed * warnings or custom suppression annotations. We also have to retain the previous suppression set * so that we can reinstate it when we move up the tree. * * <p>We do not modify the existing suppression sets, so they can be restored when moving up the * tree. We also avoid copying the suppression sets if the next node to explore does not have any * suppressed warnings or custom suppression annotations. This is the common case. * * @param sym The {@code Symbol} for the AST node currently being scanned * @param suppressWarningsType The {@code Type} for {@code @SuppressWarnings}, as given by javac's * symbol table * @param suppressionsOnCurrentPath The set of strings in all {@code @SuppressWarnings} * annotations on the current path through the AST * @param customSuppressionsOnCurrentPath The set of all custom suppression annotations */ public NewSuppressions extendSuppressionSets( Symbol sym, Type suppressWarningsType, Set<String> suppressionsOnCurrentPath, Set<Class<? extends Annotation>> customSuppressionsOnCurrentPath, boolean inGeneratedCode) { boolean newInGeneratedCode = inGeneratedCode || ASTHelpers.hasAnnotation(sym, Generated.class); /** Handle custom suppression annotations. */ Set<Class<? extends Annotation>> newCustomSuppressions = null; for (Class<? extends Annotation> annotationType : customSuppressionAnnotations) { if (ASTHelpers.hasAnnotation(sym, annotationType)) { if (newCustomSuppressions == null) { newCustomSuppressions = new HashSet<>(customSuppressionsOnCurrentPath); } newCustomSuppressions.add(annotationType); } } /** Handle @SuppressWarnings. */ Set<String> newSuppressions = null; // Iterate over annotations on this symbol, looking for SuppressWarnings for (Attribute.Compound attr : sym.getAnnotationMirrors()) { // TODO(eaftan): use JavacElements.getAnnotation instead if (attr.type.tsym == suppressWarningsType.tsym) { for (List<Pair<MethodSymbol, Attribute>> v = attr.values; v.nonEmpty(); v = v.tail) { Pair<MethodSymbol, Attribute> value = v.head; if (value.fst.name.toString().equals("value")) if (value.snd instanceof Attribute.Array) { // SuppressWarnings takes an array for (Attribute suppress : ((Attribute.Array) value.snd).values) { if (newSuppressions == null) { newSuppressions = new HashSet<>(suppressionsOnCurrentPath); } // TODO(eaftan): check return value to see if this was a new warning? newSuppressions.add((String) suppress.getValue()); } } else { throw new RuntimeException("Expected SuppressWarnings annotation to take array type"); } } } } return new NewSuppressions(newSuppressions, newCustomSuppressions, newInGeneratedCode); }
/** * Collect dependencies in the enclosing class * * @param from The enclosing class sym * @param to The enclosing classes references this sym. */ @Override public void reportDependence(Symbol from, Symbol to) { // Capture dependencies between the packages. deps.collect(from.packge().fullname, to.packge().fullname); }
@Override public boolean matches(ExpressionTree expressionTree, VisitorState state) { Symbol sym = ASTHelpers.getSymbol(expressionTree); return sym != null && sym.getSimpleName().toString().startsWith("assert"); }
/** * {@inheritDoc} * * <p>ParameterDeclarationImpl overrides this implementation. */ public int hashCode() { return sym.hashCode() + env.hashCode(); }
/** Returns this declaration's enter environment, or null if it has none. */ private Env<AttrContext> getEnterEnv() { // Get enclosing class of sym, or sym itself if it is a class // or package. TypeSymbol ts = (sym.kind != PCK) ? sym.enclClass() : (PackageSymbol) sym; return (ts != null) ? env.enter.getEnv(ts) : null; }
Attribute enterAttributeValue(Type expected, JCExpression tree, Env<AttrContext> env) { // first, try completing the attribution value sym - if a completion // error is thrown, we should recover gracefully, and display an // ordinary resolution diagnostic. try { expected.tsym.complete(); } catch (CompletionFailure e) { log.error(tree.pos(), "cant.resolve", Kinds.kindName(e.sym), e.sym); return new Attribute.Error(expected); } if (expected.isPrimitive() || types.isSameType(expected, syms.stringType)) { Type result = attr.attribExpr(tree, env, expected); if (result.isErroneous()) return new Attribute.Error(expected); if (result.constValue() == null) { log.error(tree.pos(), "attribute.value.must.be.constant"); return new Attribute.Error(expected); } result = cfolder.coerce(result, expected); return new Attribute.Constant(expected, result.constValue()); } if (expected.tsym == syms.classType.tsym) { Type result = attr.attribExpr(tree, env, expected); if (result.isErroneous()) return new Attribute.Error(expected); if (TreeInfo.name(tree) != names._class) { log.error(tree.pos(), "annotation.value.must.be.class.literal"); return new Attribute.Error(expected); } return new Attribute.Class(types, (((JCFieldAccess) tree).selected).type); } if ((expected.tsym.flags() & Flags.ANNOTATION) != 0 || types.isSameType(expected, syms.annotationType)) { if (tree.getTag() != JCTree.ANNOTATION) { log.error(tree.pos(), "annotation.value.must.be.annotation"); expected = syms.errorType; } return enterAnnotation((JCAnnotation) tree, expected, env); } if (expected.tag == TypeTags.ARRAY) { // should really be isArray() if (tree.getTag() != JCTree.NEWARRAY) { tree = make.at(tree.pos).NewArray(null, List.<JCExpression>nil(), List.of(tree)); } JCNewArray na = (JCNewArray) tree; if (na.elemtype != null) { log.error(na.elemtype.pos(), "new.not.allowed.in.annotation"); return new Attribute.Error(expected); } ListBuffer<Attribute> buf = new ListBuffer<Attribute>(); for (List<JCExpression> l = na.elems; l.nonEmpty(); l = l.tail) { buf.append(enterAttributeValue(types.elemtype(expected), l.head, env)); } na.type = expected; return new Attribute.Array(expected, buf.toArray(new Attribute[buf.length()])); } if (expected.tag == TypeTags.CLASS && (expected.tsym.flags() & Flags.ENUM) != 0) { attr.attribExpr(tree, env, expected); Symbol sym = TreeInfo.symbol(tree); if (sym == null || TreeInfo.nonstaticSelect(tree) || sym.kind != Kinds.VAR || (sym.flags() & Flags.ENUM) == 0) { log.error(tree.pos(), "enum.annotation.must.be.enum.constant"); return new Attribute.Error(expected); } VarSymbol enumerator = (VarSymbol) sym; return new Attribute.Enum(expected, enumerator); } if (!expected.isErroneous()) log.error(tree.pos(), "annotation.value.not.allowable.type"); return new Attribute.Error(attr.attribExpr(tree, env, expected)); }
// Symbol#getAnnotation is not intended for internal javac use, but because error-prone is run // after attribution it's safe to use here. @SuppressWarnings("deprecation") public static <T extends Annotation> T getAnnotation(Symbol sym, Class<T> annotationType) { return sym == null ? null : sym.getAnnotation(annotationType); }
Item invoke() { MethodType mtype = (MethodType) member.erasure(types); int rescode = Code.typecode(mtype.restype); code.emitInvokestatic(pool.put(member), mtype); return stackItem[rescode]; }
MemberItem(Symbol member, boolean nonvirtual) { super(Code.typecode(member.erasure(types))); this.member = member; this.nonvirtual = nonvirtual; }
public void visitClassDef(JCClassDecl tree) { Symbol owner = env.info.scope.owner; Scope enclScope = enterScope(env); ClassSymbol c; if (owner.kind == PCK) { // We are seeing a toplevel class. PackageSymbol packge = (PackageSymbol) owner; for (Symbol q = packge; q != null && q.kind == PCK; q = q.owner) q.flags_field |= EXISTS; c = reader.enterClass(tree.name, packge); packge.members().enterIfAbsent(c); if ((tree.mods.flags & PUBLIC) != 0 && !classNameMatchesFileName(c, env)) { log.error(tree.pos(), "class.public.should.be.in.file", tree.name); } } else { if (!tree.name.isEmpty() && !chk.checkUniqueClassName(tree.pos(), tree.name, enclScope)) { result = null; return; } if (owner.kind == TYP) { // We are seeing a member class. c = reader.enterClass(tree.name, (TypeSymbol) owner); if ((owner.flags_field & INTERFACE) != 0) { tree.mods.flags |= PUBLIC | STATIC; } } else { // We are seeing a local class. c = reader.defineClass(tree.name, owner); c.flatname = chk.localClassName(c); if (!c.name.isEmpty()) chk.checkTransparentClass(tree.pos(), c, env.info.scope); } } tree.sym = c; // Enter class into `compiled' table and enclosing scope. if (chk.compiled.get(c.flatname) != null) { duplicateClass(tree.pos(), c); result = types.createErrorType(tree.name, (TypeSymbol) owner, Type.noType); tree.sym = (ClassSymbol) result.tsym; return; } chk.compiled.put(c.flatname, c); enclScope.enter(c); // Set up an environment for class block and store in `typeEnvs' // table, to be retrieved later in memberEnter and attribution. Env<AttrContext> localEnv = classEnv(tree, env); typeEnvs.put(c, localEnv); // Fill out class fields. c.completer = memberEnter; c.flags_field = chk.checkFlags(tree.pos(), tree.mods.flags, c, tree); c.sourcefile = env.toplevel.sourcefile; c.members_field = new Scope(c); ClassType ct = (ClassType) c.type; if (owner.kind != PCK && (c.flags_field & STATIC) == 0) { // We are seeing a local or inner class. // Set outer_field of this class to closest enclosing class // which contains this class in a non-static context // (its "enclosing instance class"), provided such a class exists. Symbol owner1 = owner; while ((owner1.kind & (VAR | MTH)) != 0 && (owner1.flags_field & STATIC) == 0) { owner1 = owner1.owner; } if (owner1.kind == TYP) { ct.setEnclosingType(owner1.type); } } // Enter type parameters. ct.typarams_field = classEnter(tree.typarams, localEnv); // Add non-local class to uncompleted, to make sure it will be // completed later. if (!c.isLocal() && uncompleted != null) uncompleted.append(c); // System.err.println("entering " + c.fullname + " in " + c.owner);//DEBUG // Recursively enter all member classes. classEnter(tree.defs, localEnv); result = c.type; }
StaticItem(Symbol member) { super(Code.typecode(member.erasure(types))); this.member = member; }