use of org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration in project lombok by rzwitserloot.
the class HandleHelper method handle.
@Override
public void handle(AnnotationValues<Helper> annotation, Annotation ast, EclipseNode annotationNode) {
handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.HELPER_FLAG_USAGE, "@Helper");
EclipseNode annotatedType = annotationNode.up();
EclipseNode containingBlock = annotatedType == null ? null : annotatedType.directUp();
Statement[] origStatements = getStatementsFromAstNode(containingBlock == null ? null : containingBlock.get());
if (annotatedType == null || annotatedType.getKind() != Kind.TYPE || origStatements == null) {
annotationNode.addError("@Helper is legal only on method-local classes.");
return;
}
TypeDeclaration annotatedType_ = (TypeDeclaration) annotatedType.get();
int indexOfType = -1;
for (int i = 0; i < origStatements.length; i++) {
if (origStatements[i] == annotatedType_) {
indexOfType = i;
break;
}
}
final List<String> knownMethodNames = new ArrayList<String>();
for (AbstractMethodDeclaration methodOfHelper : annotatedType_.methods) {
if (!(methodOfHelper instanceof MethodDeclaration))
continue;
char[] name = methodOfHelper.selector;
if (name != null && name.length > 0 && name[0] != '<')
knownMethodNames.add(new String(name));
}
Collections.sort(knownMethodNames);
final String[] knownMethodNames_ = knownMethodNames.toArray(new String[knownMethodNames.size()]);
final char[] helperName = new char[annotatedType_.name.length + 1];
final boolean[] helperUsed = new boolean[1];
helperName[0] = '$';
System.arraycopy(annotatedType_.name, 0, helperName, 1, helperName.length - 1);
ASTVisitor visitor = new ASTVisitor() {
@Override
public boolean visit(MessageSend messageSend, BlockScope scope) {
if (messageSend.receiver instanceof ThisReference) {
if ((((ThisReference) messageSend.receiver).bits & ASTNode.IsImplicitThis) == 0)
return true;
} else if (messageSend.receiver != null)
return true;
char[] name = messageSend.selector;
if (name == null || name.length == 0 || name[0] == '<')
return true;
String n = new String(name);
if (Arrays.binarySearch(knownMethodNames_, n) < 0)
return true;
messageSend.receiver = new SingleNameReference(helperName, messageSend.nameSourcePosition);
helperUsed[0] = true;
return true;
}
};
for (int i = indexOfType + 1; i < origStatements.length; i++) {
origStatements[i].traverse(visitor, null);
}
if (!helperUsed[0]) {
annotationNode.addWarning("No methods of this helper class are ever used.");
return;
}
Statement[] newStatements = new Statement[origStatements.length + 1];
System.arraycopy(origStatements, 0, newStatements, 0, indexOfType + 1);
System.arraycopy(origStatements, indexOfType + 1, newStatements, indexOfType + 2, origStatements.length - indexOfType - 1);
LocalDeclaration decl = new LocalDeclaration(helperName, 0, 0);
decl.modifiers |= ClassFileConstants.AccFinal;
AllocationExpression alloc = new AllocationExpression();
alloc.type = new SingleTypeReference(annotatedType_.name, 0L);
decl.initialization = alloc;
decl.type = new SingleTypeReference(annotatedType_.name, 0L);
SetGeneratedByVisitor sgbvVisitor = new SetGeneratedByVisitor(annotationNode.get());
decl.traverse(sgbvVisitor, null);
newStatements[indexOfType + 1] = decl;
setStatementsOfAstNode(containingBlock.get(), newStatements);
}
use of org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration in project lombok by rzwitserloot.
the class HandleNonNull method handle.
@Override
public void handle(AnnotationValues<NonNull> annotation, Annotation ast, EclipseNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.NON_NULL_FLAG_USAGE, "@NonNull");
if (annotationNode.up().getKind() == Kind.FIELD) {
try {
if (isPrimitive(((AbstractVariableDeclaration) annotationNode.up().get()).type)) {
annotationNode.addWarning("@NonNull is meaningless on a primitive.");
}
} catch (Exception ignore) {
}
return;
}
if (annotationNode.up().getKind() != Kind.ARGUMENT)
return;
Argument arg;
AbstractMethodDeclaration declaration;
try {
arg = (Argument) annotationNode.up().get();
declaration = (AbstractMethodDeclaration) annotationNode.up().up().get();
} catch (Exception e) {
return;
}
if (isGenerated(declaration))
return;
if (declaration.isAbstract()) {
// This used to be a warning, but as @NonNull also has a documentary purpose, better to not warn about this. Since 1.16.7
return;
}
// Possibly, if 'declaration instanceof ConstructorDeclaration', fetch declaration.constructorCall, search it for any references to our parameter,
// and if they exist, create a new method in the class: 'private static <T> T lombok$nullCheck(T expr, String msg) {if (expr == null) throw NPE; return expr;}' and
// wrap all references to it in the super/this to a call to this method.
Statement nullCheck = generateNullCheck(arg, annotationNode);
if (nullCheck == null) {
// @NonNull applied to a primitive. Kinda pointless. Let's generate a warning.
annotationNode.addWarning("@NonNull is meaningless on a primitive.");
return;
}
if (declaration.statements == null) {
declaration.statements = new Statement[] { nullCheck };
} else {
char[] expectedName = arg.name;
/* Abort if the null check is already there, delving into try and synchronized statements */
{
Statement[] stats = declaration.statements;
int idx = 0;
while (stats != null && stats.length > idx) {
Statement stat = stats[idx++];
if (stat instanceof TryStatement) {
stats = ((TryStatement) stat).tryBlock.statements;
idx = 0;
continue;
}
if (stat instanceof SynchronizedStatement) {
stats = ((SynchronizedStatement) stat).block.statements;
idx = 0;
continue;
}
char[] varNameOfNullCheck = returnVarNameIfNullCheck(stat);
if (varNameOfNullCheck == null)
break;
if (Arrays.equals(varNameOfNullCheck, expectedName))
return;
}
}
Statement[] newStatements = new Statement[declaration.statements.length + 1];
int skipOver = 0;
for (Statement stat : declaration.statements) {
if (isGenerated(stat) && isNullCheck(stat))
skipOver++;
else
break;
}
System.arraycopy(declaration.statements, 0, newStatements, 0, skipOver);
System.arraycopy(declaration.statements, skipOver, newStatements, skipOver + 1, declaration.statements.length - skipOver);
newStatements[skipOver] = nullCheck;
declaration.statements = newStatements;
}
annotationNode.up().up().rebuild();
}
use of org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration in project lombok by rzwitserloot.
the class PatchDelegate method createDelegateMethod.
private static MethodDeclaration createDelegateMethod(char[] name, EclipseNode typeNode, BindingTuple pair, CompilationResult compilationResult, EclipseNode annNode, DelegateReceiver delegateReceiver) {
/* public <T, U, ...> ReturnType methodName(ParamType1 name1, ParamType2 name2, ...) throws T1, T2, ... {
* (return) delegate.<T, U>methodName(name1, name2);
* }
*/
boolean isVarargs = (pair.base.modifiers & ClassFileConstants.AccVarargs) != 0;
try {
checkConflictOfTypeVarNames(pair, typeNode);
} catch (CantMakeDelegates e) {
annNode.addError("There's a conflict in the names of type parameters. Fix it by renaming the following type parameters of your class: " + e.conflicted);
return null;
}
ASTNode source = annNode.get();
int pS = source.sourceStart, pE = source.sourceEnd;
MethodBinding binding = pair.parameterized;
MethodDeclaration method = new MethodDeclaration(compilationResult);
setGeneratedBy(method, source);
method.sourceStart = pS;
method.sourceEnd = pE;
method.modifiers = ClassFileConstants.AccPublic;
method.returnType = makeType(binding.returnType, source, false);
boolean isDeprecated = binding.isDeprecated();
method.selector = binding.selector;
if (binding.thrownExceptions != null && binding.thrownExceptions.length > 0) {
method.thrownExceptions = new TypeReference[binding.thrownExceptions.length];
for (int i = 0; i < method.thrownExceptions.length; i++) {
method.thrownExceptions[i] = makeType(binding.thrownExceptions[i], source, false);
}
}
MessageSend call = new MessageSend();
call.sourceStart = pS;
call.sourceEnd = pE;
call.nameSourcePosition = pos(source);
setGeneratedBy(call, source);
call.receiver = delegateReceiver.get(source, name);
call.selector = binding.selector;
if (binding.typeVariables != null && binding.typeVariables.length > 0) {
method.typeParameters = new TypeParameter[binding.typeVariables.length];
call.typeArguments = new TypeReference[binding.typeVariables.length];
for (int i = 0; i < method.typeParameters.length; i++) {
method.typeParameters[i] = new TypeParameter();
method.typeParameters[i].sourceStart = pS;
method.typeParameters[i].sourceEnd = pE;
setGeneratedBy(method.typeParameters[i], source);
method.typeParameters[i].name = binding.typeVariables[i].sourceName;
call.typeArguments[i] = new SingleTypeReference(binding.typeVariables[i].sourceName, pos(source));
setGeneratedBy(call.typeArguments[i], source);
ReferenceBinding super1 = binding.typeVariables[i].superclass;
ReferenceBinding[] super2 = binding.typeVariables[i].superInterfaces;
if (super2 == null)
super2 = new ReferenceBinding[0];
if (super1 != null || super2.length > 0) {
int offset = super1 == null ? 0 : 1;
method.typeParameters[i].bounds = new TypeReference[super2.length + offset - 1];
if (super1 != null)
method.typeParameters[i].type = makeType(super1, source, false);
else
method.typeParameters[i].type = makeType(super2[0], source, false);
int ctr = 0;
for (int j = (super1 == null) ? 1 : 0; j < super2.length; j++) {
method.typeParameters[i].bounds[ctr] = makeType(super2[j], source, false);
method.typeParameters[i].bounds[ctr++].bits |= ASTNode.IsSuperType;
}
}
}
}
if (isDeprecated) {
method.annotations = new Annotation[] { generateDeprecatedAnnotation(source) };
}
method.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
if (binding.parameters != null && binding.parameters.length > 0) {
method.arguments = new Argument[binding.parameters.length];
call.arguments = new Expression[method.arguments.length];
for (int i = 0; i < method.arguments.length; i++) {
AbstractMethodDeclaration sourceElem;
try {
sourceElem = pair.base.sourceMethod();
} catch (Exception e) {
sourceElem = null;
}
char[] argName;
if (sourceElem == null)
argName = ("arg" + i).toCharArray();
else {
argName = sourceElem.arguments[i].name;
}
method.arguments[i] = new Argument(argName, pos(source), makeType(binding.parameters[i], source, false), ClassFileConstants.AccFinal);
setGeneratedBy(method.arguments[i], source);
call.arguments[i] = new SingleNameReference(argName, pos(source));
setGeneratedBy(call.arguments[i], source);
}
if (isVarargs) {
method.arguments[method.arguments.length - 1].type.bits |= ASTNode.IsVarArgs;
}
}
Statement body;
if (method.returnType instanceof SingleTypeReference && ((SingleTypeReference) method.returnType).token == TypeConstants.VOID) {
body = call;
} else {
body = new ReturnStatement(call, source.sourceStart, source.sourceEnd);
setGeneratedBy(body, source);
}
method.statements = new Statement[] { body };
return method;
}
use of org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration in project lombok by rzwitserloot.
the class PatchDelegate method fillMethodBindingsForMethods.
private static void fillMethodBindingsForMethods(CompilationUnitDeclaration cud, ClassScope scope, List<BindingTuple> methodsToDelegate) {
TypeDeclaration decl = scope.referenceContext;
if (decl == null)
return;
if (decl.methods != null)
for (AbstractMethodDeclaration methodDecl : decl.methods) {
if (methodDecl.annotations == null)
continue;
for (Annotation ann : methodDecl.annotations) {
if (!isDelegate(ann, decl))
continue;
if (Annotation_applied.getAndSet(ann, true))
continue;
if (!(methodDecl instanceof MethodDeclaration)) {
EclipseAST eclipseAst = TransformEclipseAST.getAST(cud, true);
eclipseAst.get(ann).addError(LEGALITY_OF_DELEGATE);
break;
}
if (methodDecl.arguments != null) {
EclipseAST eclipseAst = TransformEclipseAST.getAST(cud, true);
eclipseAst.get(ann).addError(LEGALITY_OF_DELEGATE);
break;
}
if ((methodDecl.modifiers & ClassFileConstants.AccStatic) != 0) {
EclipseAST eclipseAst = TransformEclipseAST.getAST(cud, true);
eclipseAst.get(ann).addError(LEGALITY_OF_DELEGATE);
break;
}
MethodDeclaration method = (MethodDeclaration) methodDecl;
List<ClassLiteralAccess> rawTypes = rawTypes(ann, "types");
List<ClassLiteralAccess> excludedRawTypes = rawTypes(ann, "excludes");
List<BindingTuple> methodsToExclude = new ArrayList<BindingTuple>();
List<BindingTuple> methodsToDelegateForThisAnn = new ArrayList<BindingTuple>();
try {
for (ClassLiteralAccess cla : excludedRawTypes) {
addAllMethodBindings(methodsToExclude, cla.type.resolveType(decl.initializerScope), new HashSet<String>(), method.selector, ann);
}
Set<String> banList = new HashSet<String>();
for (BindingTuple excluded : methodsToExclude) banList.add(printSig(excluded.parameterized));
if (rawTypes.isEmpty()) {
if (method.returnType == null)
continue;
addAllMethodBindings(methodsToDelegateForThisAnn, method.returnType.resolveType(decl.initializerScope), banList, method.selector, ann);
} else {
for (ClassLiteralAccess cla : rawTypes) {
addAllMethodBindings(methodsToDelegateForThisAnn, cla.type.resolveType(decl.initializerScope), banList, method.selector, ann);
}
}
} catch (DelegateRecursion e) {
EclipseAST eclipseAst = TransformEclipseAST.getAST(cud, true);
eclipseAst.get(ann).addError(String.format(RECURSION_NOT_ALLOWED, new String(e.member), new String(e.type)));
break;
}
// Not doing this right now because of problems - see commented-out-method for info.
// removeExistingMethods(methodsToDelegate, decl, scope);
String dupe = containsDuplicates(methodsToDelegateForThisAnn);
if (dupe != null) {
EclipseAST eclipseAst = TransformEclipseAST.getAST(cud, true);
eclipseAst.get(ann).addError("The method '" + dupe + "' is being delegated by more than one specified type.");
} else {
methodsToDelegate.addAll(methodsToDelegateForThisAnn);
}
}
}
}
use of org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration in project lombok by rzwitserloot.
the class EclipseHandlerUtil method methodExists.
/**
* Checks if there is a method with the provided name. In case of multiple methods (overloading), only
* the first method decides if EXISTS_BY_USER or EXISTS_BY_LOMBOK is returned.
*
* @param methodName the method name to check for.
* @param node Any node that represents the Type (TypeDeclaration) to look in, or any child node thereof.
* @param caseSensitive If the search should be case sensitive.
* @param params The number of parameters the method should have; varargs count as 0-*. Set to -1 to find any method with the appropriate name regardless of parameter count.
*/
public static MemberExistsResult methodExists(String methodName, EclipseNode node, boolean caseSensitive, int params) {
while (node != null && !(node.get() instanceof TypeDeclaration)) {
node = node.up();
}
if (node != null && node.get() instanceof TypeDeclaration) {
TypeDeclaration typeDecl = (TypeDeclaration) node.get();
if (typeDecl.methods != null)
top: for (AbstractMethodDeclaration def : typeDecl.methods) {
if (def instanceof MethodDeclaration) {
char[] mName = def.selector;
if (mName == null)
continue;
boolean nameEquals = caseSensitive ? methodName.equals(new String(mName)) : methodName.equalsIgnoreCase(new String(mName));
if (nameEquals) {
if (params > -1) {
int minArgs = 0;
int maxArgs = 0;
if (def.arguments != null && def.arguments.length > 0) {
minArgs = def.arguments.length;
if ((def.arguments[def.arguments.length - 1].type.bits & ASTNode.IsVarArgs) != 0) {
minArgs--;
maxArgs = Integer.MAX_VALUE;
} else {
maxArgs = minArgs;
}
}
if (params < minArgs || params > maxArgs)
continue;
}
if (isTolerate(node, def))
continue top;
return getGeneratedBy(def) == null ? MemberExistsResult.EXISTS_BY_USER : MemberExistsResult.EXISTS_BY_LOMBOK;
}
}
}
}
return MemberExistsResult.NOT_EXISTS;
}
Aggregations