From 8650dd4d75c86441715ebebc98d1100ad2d0f0a5 Mon Sep 17 00:00:00 2001 From: ksg Date: Sun, 10 Nov 2024 17:45:34 +0900 Subject: [PATCH 1/4] fix: handle empty lines and spaces between annotations and class declarations Signed-off-by: ksg --- src/miniparsers/java.cr | 68 ++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/miniparsers/java.cr b/src/miniparsers/java.cr index 5e176bd2..24ab2e86 100644 --- a/src/miniparsers/java.cr +++ b/src/miniparsers/java.cr @@ -22,7 +22,7 @@ class JavaParser @classes_tokens.each do |class_tokens| name = get_class_name(class_tokens) methods = parse_methods(class_tokens) - annotations = parse_annotations(@tokens, class_tokens[0].index) + annotations = parse_annotations_backwards(@tokens, class_tokens[0].index) fields = parse_fields(class_tokens, methods, annotations) @classes << ClassModel.new(annotations, name, fields, methods, class_tokens) end @@ -158,50 +158,50 @@ class JavaParser parameters end - def parse_annotations(tokens : Array(Token), declare_token_index : Int32) - skip_line = 0 - annotation_tokens = Hash(String, AnnotationModel).new + def parse_annotations_backwards(tokens : Array(Token), declare_token_index : Int32) + annotations = Hash(String, AnnotationModel).new - cursor = declare_token_index - 1 + # Find the closest newline before the declaration last_newline_index = -1 - while cursor > 0 + cursor = declare_token_index - 1 + + # Locate the newline token marking the end of the declaration line + while cursor >= 0 && last_newline_index == -1 if tokens[cursor].type == :NEWLINE - skip_line += 1 - if skip_line == 1 - last_newline_index = cursor - end + last_newline_index = cursor end + cursor -= 1 + end - if skip_line == 2 - # :NEWLINE(cursor) @RequestMapping - # :NEWLINE public class Controller(type param) - annotation_token_index = cursor + 1 - starts_with_at = while annotation_token_index < last_newline_index - if tokens[annotation_token_index].type == :AT - break true - elsif tokens[annotation_token_index].type == :TAB - annotation_token_index += 1 - next - else - break false - end - end + # Return empty annotations if no newline was found + return annotations if last_newline_index == -1 - if starts_with_at - annotation_name = tokens[annotation_token_index + 1].value - annotation_params = parse_formal_parameters(tokens, annotation_token_index + 2) - annotation_tokens[annotation_name] = AnnotationModel.new(annotation_name, annotation_params, tokens[annotation_token_index - 1..last_newline_index - 1]) - skip_line = 1 + # Continue parsing annotations above the declaration line + while cursor >= 0 + if tokens[cursor].type == :NEWLINE + unless tokens[cursor + 1].type == :NEWLINE || tokens[cursor + 1].type == :TAB + # Break if the next token is not an annotation start + break if tokens[cursor + 1].type != :AT + + # Extract annotation name and parameters + annotation_name = tokens[cursor + 2].value + annotation_params = parse_formal_parameters(tokens, cursor + 3) + + # Store the annotation in the hash + annotations[annotation_name] = AnnotationModel.new( + annotation_name, + annotation_params, + tokens[cursor..last_newline_index - 1] + ) + + # Update the newline index to the current cursor last_newline_index = cursor - else - break end end - cursor -= 1 end - annotation_tokens + annotations end def parse_classes(tokens : Array(Token)) @@ -455,7 +455,7 @@ class JavaParser if token.type == :RBRACE rbrace_count += 1 if lbrace_count == rbrace_count - annotations = parse_annotations(class_tokens, method_name_index) + annotations = parse_annotations_backwards(class_tokens, method_name_index) if !method_name.nil? method_params = parse_formal_parameters(class_tokens, method_name_index + 1) method_body = class_tokens[method_body_index + 1..index - 1] From e04cc6362c8d6370d7eb2e94d582d39f0e1501a9 Mon Sep 17 00:00:00 2001 From: ksg Date: Sun, 10 Nov 2024 17:54:09 +0900 Subject: [PATCH 2/4] fix: handle empty URLs in endpoint parsing to prevent crashes Signed-off-by: ksg --- src/models/noir.cr | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/models/noir.cr b/src/models/noir.cr index c400af6d..76562858 100644 --- a/src/models/noir.cr +++ b/src/models/noir.cr @@ -151,16 +151,16 @@ class NoirRunner end end - # Check start with slash - if tiny_tmp.url[0] != "/" - tiny_tmp.url = "/#{tiny_tmp.url}" - end - - # Check double slash - tiny_tmp.url = tiny_tmp.url.gsub_repeatedly("//", "/") - # Duplicate check if tiny_tmp.url != "" + # Check start with slash + if tiny_tmp.url[0] != "/" + tiny_tmp.url = "/#{tiny_tmp.url}" + end + + # Check double slash + tiny_tmp.url = tiny_tmp.url.gsub_repeatedly("//", "/") + is_new = true final.each do |dup| if dup.method == tiny_tmp.method && dup.url == tiny_tmp.url From 9e70456fc132e0170474e5e0f4f46c70531c40ff Mon Sep 17 00:00:00 2001 From: ksg Date: Sun, 10 Nov 2024 18:21:37 +0900 Subject: [PATCH 3/4] fix: adjust Java lexer to handle newline sanitization correctly Signed-off-by: ksg --- src/minilexers/java.cr | 1 - 1 file changed, 1 deletion(-) diff --git a/src/minilexers/java.cr b/src/minilexers/java.cr index 915e8eae..e7332d42 100644 --- a/src/minilexers/java.cr +++ b/src/minilexers/java.cr @@ -217,7 +217,6 @@ class JavaLexer < MiniLexer @position += 2 while @position < @input.size if @input[@position] == '\n' - @position += 1 break end @position += 1 From 61e90f1bb8586f3d0db4e1b3b2720b8df0395fa9 Mon Sep 17 00:00:00 2001 From: ksg Date: Sun, 10 Nov 2024 19:04:08 +0900 Subject: [PATCH 4/4] test: add functional tests for handling empty URLs in Spring annotations Signed-off-by: ksg --- .../java/spring/src/RequestMethodClass.java | 13 +++++++++++++ spec/functional_test/testers/java/spring_spec.cr | 2 ++ 2 files changed, 15 insertions(+) create mode 100644 spec/functional_test/fixtures/java/spring/src/RequestMethodClass.java diff --git a/spec/functional_test/fixtures/java/spring/src/RequestMethodClass.java b/spec/functional_test/fixtures/java/spring/src/RequestMethodClass.java new file mode 100644 index 00000000..a410a14e --- /dev/null +++ b/spec/functional_test/fixtures/java/spring/src/RequestMethodClass.java @@ -0,0 +1,13 @@ +package com.test; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RequestMapping(value = "/empty") // Comment + +public class RequestMethodClass { + + @GetMapping("") + public void getData() { + } +} \ No newline at end of file diff --git a/spec/functional_test/testers/java/spring_spec.cr b/spec/functional_test/testers/java/spring_spec.cr index 83306a03..e2f65a8b 100644 --- a/spec/functional_test/testers/java/spring_spec.cr +++ b/spec/functional_test/testers/java/spring_spec.cr @@ -34,6 +34,8 @@ extected_endpoints = [ Param.new("b", "", "query"), Param.new("name", "", "query"), ]), + # EmptyController.java + Endpoint.new("/empty", "GET"), ] FunctionalTester.new("fixtures/java/spring/", {