From 84ee2fdb53a4193fc8d1fbabd60244b804d9599c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isak=20N=C3=A4slund?= Date: Thu, 20 Mar 2025 12:58:01 +0000 Subject: [PATCH 1/3] #506 Fixes checks by adding [] as split chars --- .../Query/Checks/HasUniqueMultiOutputAttributes.cs | 6 +++--- .../Query/Checks/HasValidMultiOutputAttributes.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CodeComplianceTest_Engine/Query/Checks/HasUniqueMultiOutputAttributes.cs b/CodeComplianceTest_Engine/Query/Checks/HasUniqueMultiOutputAttributes.cs index 2bdcee7b..85731b63 100644 --- a/CodeComplianceTest_Engine/Query/Checks/HasUniqueMultiOutputAttributes.cs +++ b/CodeComplianceTest_Engine/Query/Checks/HasUniqueMultiOutputAttributes.cs @@ -68,10 +68,10 @@ public static Span HasUniqueMultiOutputAttributes(this MethodDeclarationSyntax n string builtString = ""; foreach (char x in returnType) { - if (x == '<') + if (x == '<' || x == '[') split++; - if (x == '>') + if (x == '>' || x == ']') split--; if (x == ',' && split == 0) @@ -91,7 +91,7 @@ public static Span HasUniqueMultiOutputAttributes(this MethodDeclarationSyntax n string t = x.ToString(); string newString = ""; int bracketCount = 0; - foreach(char i in t) + foreach (char i in t) { if (i == '<') bracketCount++; diff --git a/CodeComplianceTest_Engine/Query/Checks/HasValidMultiOutputAttributes.cs b/CodeComplianceTest_Engine/Query/Checks/HasValidMultiOutputAttributes.cs index 3e4606a8..03c64dd0 100644 --- a/CodeComplianceTest_Engine/Query/Checks/HasValidMultiOutputAttributes.cs +++ b/CodeComplianceTest_Engine/Query/Checks/HasValidMultiOutputAttributes.cs @@ -68,10 +68,10 @@ public static Span HasValidMultiOutputAttributes(this MethodDeclarationSyntax no string builtString = ""; foreach (char x in returnType) { - if (x == '<') + if (x == '<' || x == '[') split++; - if(x == '>') + if (x == '>' || x == ']') split--; if (x == ',' && split == 0) From 0fde94884d13f86972c876641f9fa80eda21dd36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isak=20N=C3=A4slund?= Date: Thu, 20 Mar 2025 12:58:30 +0000 Subject: [PATCH 2/3] Check for more acceptable returnType names for case of generic return on IsValidCreateMethod --- .../Query/Checks/IsValidCreateMethod.cs | 76 ++++++++++++++----- 1 file changed, 55 insertions(+), 21 deletions(-) diff --git a/CodeComplianceTest_Engine/Query/Checks/IsValidCreateMethod.cs b/CodeComplianceTest_Engine/Query/Checks/IsValidCreateMethod.cs index 053645fe..03783a43 100644 --- a/CodeComplianceTest_Engine/Query/Checks/IsValidCreateMethod.cs +++ b/CodeComplianceTest_Engine/Query/Checks/IsValidCreateMethod.cs @@ -47,38 +47,72 @@ public static Span IsValidCreateMethod(this MethodDeclarationSyntax node) return null; string filePath = node.SyntaxTree.FilePath; + + foreach (string returnType in ReturnTypeCandidates(node)) + { + string fileName = ""; + + if (!string.IsNullOrEmpty(filePath)) + { + fileName = System.IO.Path.GetFileNameWithoutExtension(filePath); + if (Regex.Match(returnType, $"((List|IEnumerable)<)?I?{fileName}(<.*>)?>?$").Success) + return null; //File name matches return type exactly so this is valid. IsValidCreateMethodName will check if the method name matches the file name + } + + //If file name does not exactly match the return type then we need to check if the return type is in a sub-folder in create + + List pathSplit = filePath.Split(System.IO.Path.DirectorySeparatorChar).ToList(); + int createIndex = pathSplit.IndexOf("Create"); + if (createIndex == -1) + return node.Identifier.Span.ToSpan(); //Evidently this create method isn't working for some reason - even though it should but this is as a protection/precaution + + try + { + if (Regex.Match(returnType, $"((List|IEnumerable)<)?I?{pathSplit[createIndex + 1]}(<.*>)?>?$").Success || Regex.Match(returnType, $"((List|IEnumerable)<)?I?{pathSplit[createIndex + 2]}(<.*>)?>?$").Success) + return null; //The folder path after the 'Create' folder matches the return type so this is valid. IsValidCreateMethodName will check if the method name matches the file name + } + catch + { + //In case createIndex + 1 || createIndex + 2 result in an out of bounds error - it means the check has failed and something isn't compliant so can pass through to returning the span + } + } + return node.Identifier.Span.ToSpan(); //Create method file (name/path) and return type do not match as required + } + + private static List ReturnTypeCandidates(this MethodDeclarationSyntax node) + { + List returnTypeNames = new List(); + var type = node.ReturnType; if (type is QualifiedNameSyntax) type = ((QualifiedNameSyntax)type).Right; - + string returnType = type.ToString(); - string fileName = ""; - if(!string.IsNullOrEmpty(filePath)) + returnTypeNames.Add(returnType); + + //Handle the case where the method returns a generic type. + //Then check the constraints of the generic type, if any of them matches the file name, then that is also acceptable + if (node.ConstraintClauses.Count != 0) { - fileName = System.IO.Path.GetFileNameWithoutExtension(filePath); - if(Regex.Match(returnType, $"((List|IEnumerable)<)?I?{fileName}(<.*>)?>?$").Success) - return null; //File name matches return type exactly so this is valid. IsValidCreateMethodName will check if the method name matches the file name - } + foreach (var constraintClause in node.ConstraintClauses) + { + string constraintString = constraintClause.ToString(); //where T : XXX, YYY + string[] split = constraintString.Split(':'); + if (split.Length != 2) + continue; - //If file name does not exactly match the return type then we need to check if the return type is in a sub-folder in create + string target = split[0].Trim().Split(' ').Last().Trim(); //Split with space to get rid of where - List pathSplit = filePath.Split(System.IO.Path.DirectorySeparatorChar).ToList(); - int createIndex = pathSplit.IndexOf("Create"); - if (createIndex == -1) - return node.Identifier.Span.ToSpan(); //Evidently this create method isn't working for some reason - even though it should but this is as a protection/precaution + if (target != returnType) + continue; - try - { - if (Regex.Match(returnType, $"((List|IEnumerable)<)?I?{pathSplit[createIndex + 1]}(<.*>)?>?$").Success || Regex.Match(returnType, $"((List|IEnumerable)<)?I?{pathSplit[createIndex + 2]}(<.*>)?>?$").Success) - return null; //The folder path after the 'Create' folder matches the return type so this is valid. IsValidCreateMethodName will check if the method name matches the file name - } - catch - { - //In case createIndex + 1 || createIndex + 2 result in an out of bounds error - it means the check has failed and something isn't compliant so can pass through to returning the span + returnTypeNames.AddRange(split[1].Split(',').Select(x => x.Trim())); + } } - return node.Identifier.Span.ToSpan(); //Create method file (name/path) and return type do not match as required + + return returnTypeNames; } } } From ace1be2cd7a87651c5c838a190e2704febd0c522 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isak=20N=C3=A4slund?= Date: Thu, 20 Mar 2025 13:24:00 +0000 Subject: [PATCH 3/3] Simplify the code Instead of explicitly checking some specific folder and specific indices simply loop backwards from the filename to the create folder and check if one of the paths is a match. Avoid the copy pasting of regex, and constant index out of bounds exceptions being thrown. --- .../Query/Checks/IsValidCreateMethod.cs | 35 +++++++------------ 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/CodeComplianceTest_Engine/Query/Checks/IsValidCreateMethod.cs b/CodeComplianceTest_Engine/Query/Checks/IsValidCreateMethod.cs index 03783a43..cfe4dd9c 100644 --- a/CodeComplianceTest_Engine/Query/Checks/IsValidCreateMethod.cs +++ b/CodeComplianceTest_Engine/Query/Checks/IsValidCreateMethod.cs @@ -48,32 +48,23 @@ public static Span IsValidCreateMethod(this MethodDeclarationSyntax node) string filePath = node.SyntaxTree.FilePath; + if (string.IsNullOrEmpty(filePath)) + return node.Identifier.Span.ToSpan(); + + //Split the path, including the filename. + //Split by dot to get rid of extension in .cs file. + //Reverse to start with file and walk backwards + List pathSplit = filePath.Split(System.IO.Path.DirectorySeparatorChar).Select(x => x.Split('.').First()).Reverse().ToList(); + foreach (string returnType in ReturnTypeCandidates(node)) { - string fileName = ""; - - if (!string.IsNullOrEmpty(filePath)) + foreach (string path in pathSplit) { - fileName = System.IO.Path.GetFileNameWithoutExtension(filePath); - if (Regex.Match(returnType, $"((List|IEnumerable)<)?I?{fileName}(<.*>)?>?$").Success) - return null; //File name matches return type exactly so this is valid. IsValidCreateMethodName will check if the method name matches the file name - } - - //If file name does not exactly match the return type then we need to check if the return type is in a sub-folder in create + if (path == "Create") //Loop until we reach the create folder + break; - List pathSplit = filePath.Split(System.IO.Path.DirectorySeparatorChar).ToList(); - int createIndex = pathSplit.IndexOf("Create"); - if (createIndex == -1) - return node.Identifier.Span.ToSpan(); //Evidently this create method isn't working for some reason - even though it should but this is as a protection/precaution - - try - { - if (Regex.Match(returnType, $"((List|IEnumerable)<)?I?{pathSplit[createIndex + 1]}(<.*>)?>?$").Success || Regex.Match(returnType, $"((List|IEnumerable)<)?I?{pathSplit[createIndex + 2]}(<.*>)?>?$").Success) - return null; //The folder path after the 'Create' folder matches the return type so this is valid. IsValidCreateMethodName will check if the method name matches the file name - } - catch - { - //In case createIndex + 1 || createIndex + 2 result in an out of bounds error - it means the check has failed and something isn't compliant so can pass through to returning the span + if (Regex.Match(returnType, $"((List|IEnumerable)<)?I?{path}(<.*>)?>?$").Success) + return null; //Name of file or folder matches return type exactly so this is valid. IsValidCreateMethodName will check if the method name matches the file name } } return node.Identifier.Span.ToSpan(); //Create method file (name/path) and return type do not match as required