Fixed annotation parsing in AstBuilder

pulled some hair out on this one
This commit is contained in:
2025-07-23 07:29:16 -04:00
parent 1e15e90d32
commit 85fdcff103
8 changed files with 492 additions and 58 deletions

View File

@@ -297,11 +297,24 @@ namespace MarketAlly.IronJava.Core.AST.Builders
var annotations = new List<Annotation>();
foreach (var context in contexts)
{
// Check if this is directly an annotation context
if (context is Java9Parser.AnnotationContext annotationContext)
{
var annotation = BuildAnnotation(annotationContext);
if (annotation != null) annotations.Add(annotation);
}
// Check if this is a modifier that contains an annotation
else if (context is ParserRuleContext ruleContext && ruleContext.ChildCount > 0)
{
// The first child of a modifier rule that represents an annotation
// will be an AnnotationContext
var firstChild = ruleContext.GetChild(0);
if (firstChild is Java9Parser.AnnotationContext childAnnotation)
{
var annotation = BuildAnnotation(childAnnotation);
if (annotation != null) annotations.Add(annotation);
}
}
}
return annotations;
}
@@ -312,25 +325,37 @@ namespace MarketAlly.IronJava.Core.AST.Builders
TypeReference? type = null;
var arguments = new List<AnnotationArgument>();
// Annotations can be NormalAnnotation, MarkerAnnotation, or SingleElementAnnotation
if (context.normalAnnotation() != null)
{
var normalAnn = context.normalAnnotation();
type = BuildTypeReference(normalAnn.typeName());
if (type == null) return null;
// Don't return early - try to build the type from the name
if (type == null)
{
var typeName = normalAnn.typeName().GetText();
type = new ClassOrInterfaceType(GetSourceRange(normalAnn.typeName()), typeName, null, new List<TypeArgument>(), new List<Annotation>());
}
if (normalAnn.elementValuePairList() != null)
{
foreach (var pair in normalAnn.elementValuePairList().elementValuePair())
{
var name = pair.identifier().GetText();
var value = BuildExpression(pair.elementValue());
if (value != null)
var value = BuildElementValue(pair.elementValue());
// Debug - always add the argument even if value is null
// If value is null, create a string literal as fallback
if (value == null)
{
arguments.Add(new AnnotationValueArgument(
GetSourceRange(pair), name, value
));
var elementText = pair.elementValue().GetText();
value = new LiteralExpression(GetSourceRange(pair.elementValue()), elementText, LiteralKind.String);
}
arguments.Add(new AnnotationValueArgument(
GetSourceRange(pair), name, value
));
}
}
}
@@ -338,30 +363,89 @@ namespace MarketAlly.IronJava.Core.AST.Builders
{
var markerAnn = context.markerAnnotation();
type = BuildTypeReference(markerAnn.typeName());
if (type == null) return null;
if (type == null)
{
var typeName = markerAnn.typeName().GetText();
type = new ClassOrInterfaceType(GetSourceRange(markerAnn.typeName()), typeName, null, new List<TypeArgument>(), new List<Annotation>());
}
// Marker annotations have no arguments
}
else if (context.singleElementAnnotation() != null)
{
var singleAnn = context.singleElementAnnotation();
type = BuildTypeReference(singleAnn.typeName());
if (type == null) return null;
if (type == null)
{
var typeName = singleAnn.typeName().GetText();
type = new ClassOrInterfaceType(GetSourceRange(singleAnn.typeName()), typeName, null, new List<TypeArgument>(), new List<Annotation>());
}
if (singleAnn.elementValue() != null)
{
var value = BuildExpression(singleAnn.elementValue());
if (value != null)
var value = BuildElementValue(singleAnn.elementValue());
// Fallback if BuildElementValue returns null
if (value == null)
{
arguments.Add(new AnnotationValueArgument(
location, "value", value
));
var elementText = singleAnn.elementValue().GetText();
value = new LiteralExpression(GetSourceRange(singleAnn.elementValue()), elementText, LiteralKind.String);
}
arguments.Add(new AnnotationValueArgument(
location, "value", value
));
}
}
return type != null ? new Annotation(location, type, arguments) : null;
}
private Expression? BuildElementValue(Java9Parser.ElementValueContext context)
{
if (context.conditionalExpression() != null)
{
var expr = BuildExpression(context.conditionalExpression());
// If BuildExpression returns null, try to parse it as a string literal
if (expr == null)
{
var text = context.conditionalExpression().GetText();
if (text.StartsWith('"') && text.EndsWith('"'))
{
// Remove quotes and create a string literal
var value = text.Substring(1, text.Length - 2);
expr = new LiteralExpression(GetSourceRange(context), value, LiteralKind.String);
}
}
return expr;
}
else if (context.annotation() != null)
{
var annotation = BuildAnnotation(context.annotation());
if (annotation != null)
{
return new AnnotationExpression(GetSourceRange(context), annotation);
}
}
else if (context.elementValueArrayInitializer() != null)
{
var arrayInit = context.elementValueArrayInitializer();
var elements = new List<Expression>();
if (arrayInit.elementValueList() != null)
{
foreach (var elementValue in arrayInit.elementValueList().elementValue())
{
var element = BuildElementValue(elementValue);
if (element != null) elements.Add(element);
}
}
return new ArrayInitializer(GetSourceRange(arrayInit), elements);
}
return null;
}
private List<TypeParameter> BuildTypeParameters(Java9Parser.TypeParametersContext? context)
{
if (context == null) return new List<TypeParameter>();
@@ -414,8 +498,8 @@ namespace MarketAlly.IronJava.Core.AST.Builders
Java9Parser.ArrayTypeContext array => BuildArrayType(array),
Java9Parser.TypeVariableContext typeVar => BuildTypeVariable(typeVar),
Java9Parser.ClassTypeContext classType => BuildClassType(classType),
Java9Parser.InterfaceTypeContext interfaceType => BuildInterfaceType(interfaceType),
Java9Parser.TypeNameContext typeName => BuildTypeName(typeName),
Java9Parser.InterfaceTypeContext interfaceType => BuildInterfaceType(interfaceType),
_ => null
};
}
@@ -980,6 +1064,13 @@ namespace MarketAlly.IronJava.Core.AST.Builders
private Parameter? BuildLastParameter(Java9Parser.LastFormalParameterContext context)
{
// Check if this is just a regular parameter (not varargs)
if (context.formalParameter() != null)
{
return BuildParameter(context.formalParameter());
}
// Otherwise, it's a varargs parameter
var location = GetSourceRange(context);
var modifiers = BuildModifiers(context.variableModifier());
var annotations = BuildAnnotations(context.variableModifier());
@@ -989,7 +1080,7 @@ namespace MarketAlly.IronJava.Core.AST.Builders
var declaratorId = context.variableDeclaratorId();
var name = declaratorId.identifier().GetText();
var isFinal = modifiers.HasFlag(Modifiers.Final);
var isVarArgs = context.GetChild(context.ChildCount - 2)?.GetText() == "...";
var isVarArgs = true; // This branch is only for varargs
return new Parameter(location, type, name, isVarArgs, isFinal, annotations);
}
@@ -1325,7 +1416,7 @@ namespace MarketAlly.IronJava.Core.AST.Builders
if (context.defaultValue() != null)
{
defaultValue = BuildExpression(context.defaultValue().elementValue());
defaultValue = BuildElementValue(context.defaultValue().elementValue());
}
return new AnnotationMember(location, name, type, defaultValue);
@@ -2590,46 +2681,6 @@ namespace MarketAlly.IronJava.Core.AST.Builders
return parameters;
}
private Expression? BuildElementValue(Java9Parser.ElementValueContext context)
{
if (context.conditionalExpression() != null)
{
return BuildConditionalExpression(context.conditionalExpression());
}
else if (context.elementValueArrayInitializer() != null)
{
return BuildElementValueArrayInitializer(context.elementValueArrayInitializer());
}
else if (context.annotation() != null)
{
// Annotation as expression - wrap it
var annotation = BuildAnnotation(context.annotation());
if (annotation != null)
{
// Create a special expression to hold the annotation
return new IdentifierExpression(annotation.Location, "@" + annotation.Type.ToString());
}
}
return null;
}
private ArrayInitializer? BuildElementValueArrayInitializer(Java9Parser.ElementValueArrayInitializerContext context)
{
var location = GetSourceRange(context);
var elements = new List<Expression>();
if (context.elementValueList() != null)
{
foreach (var value in context.elementValueList().elementValue())
{
var expr = BuildElementValue(value);
if (expr != null) elements.Add(expr);
}
}
return new ArrayInitializer(location, elements);
}
// Statement building methods

View File

@@ -400,6 +400,23 @@ namespace MarketAlly.IronJava.Core.AST.Nodes
public override void Accept(IJavaVisitor visitor) => visitor.VisitArrayInitializer(this);
}
/// <summary>
/// Represents an annotation used as an expression (in annotation element values).
/// </summary>
public class AnnotationExpression : Expression
{
public Annotation Annotation { get; }
public AnnotationExpression(SourceRange location, Annotation annotation) : base(location)
{
Annotation = annotation;
AddChild(annotation);
}
public override T Accept<T>(IJavaVisitor<T> visitor) => visitor.VisitAnnotationExpression(this);
public override void Accept(IJavaVisitor visitor) => visitor.VisitAnnotationExpression(this);
}
/// <summary>
/// Represents a lambda expression.
/// </summary>

View File

@@ -51,6 +51,7 @@ namespace MarketAlly.IronJava.Core.AST.Visitors
void VisitNewExpression(NewExpression node);
void VisitNewArrayExpression(NewArrayExpression node);
void VisitArrayInitializer(ArrayInitializer node);
void VisitAnnotationExpression(AnnotationExpression node);
void VisitLambdaExpression(LambdaExpression node);
void VisitLambdaParameter(LambdaParameter node);
void VisitMethodReferenceExpression(MethodReferenceExpression node);
@@ -135,6 +136,7 @@ namespace MarketAlly.IronJava.Core.AST.Visitors
T VisitNewExpression(NewExpression node);
T VisitNewArrayExpression(NewArrayExpression node);
T VisitArrayInitializer(ArrayInitializer node);
T VisitAnnotationExpression(AnnotationExpression node);
T VisitLambdaExpression(LambdaExpression node);
T VisitLambdaParameter(LambdaParameter node);
T VisitMethodReferenceExpression(MethodReferenceExpression node);

View File

@@ -54,6 +54,7 @@ namespace MarketAlly.IronJava.Core.AST.Visitors
public virtual void VisitNewExpression(NewExpression node) => DefaultVisit(node);
public virtual void VisitNewArrayExpression(NewArrayExpression node) => DefaultVisit(node);
public virtual void VisitArrayInitializer(ArrayInitializer node) => DefaultVisit(node);
public virtual void VisitAnnotationExpression(AnnotationExpression node) => DefaultVisit(node);
public virtual void VisitLambdaExpression(LambdaExpression node) => DefaultVisit(node);
public virtual void VisitLambdaParameter(LambdaParameter node) => DefaultVisit(node);
public virtual void VisitMethodReferenceExpression(MethodReferenceExpression node) => DefaultVisit(node);
@@ -133,6 +134,7 @@ namespace MarketAlly.IronJava.Core.AST.Visitors
public virtual T VisitNewExpression(NewExpression node) => DefaultVisit(node);
public virtual T VisitNewArrayExpression(NewArrayExpression node) => DefaultVisit(node);
public virtual T VisitArrayInitializer(ArrayInitializer node) => DefaultVisit(node);
public virtual T VisitAnnotationExpression(AnnotationExpression node) => DefaultVisit(node);
public virtual T VisitLambdaExpression(LambdaExpression node) => DefaultVisit(node);
public virtual T VisitLambdaParameter(LambdaParameter node) => DefaultVisit(node);
public virtual T VisitMethodReferenceExpression(MethodReferenceExpression node) => DefaultVisit(node);