diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs index 843bb60fa..0b71af086 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs @@ -47,6 +47,20 @@ namespace Flax.Build.Bindings } } + private class ParseException : Exception + { + public ParseException(ref ParsingContext context, string msg) + : base(GetParseErrorLocation(ref context, msg)) + { + } + } + + private static string GetParseErrorLocation(ref ParsingContext context, string msg) + { + // Make it a link clickable in Visual Studio build output + return $"{context.File.Name}({context.Tokenizer.CurrentLine}): {msg}"; + } + private static string[] ParseComment(ref ParsingContext context) { if (context.StringCache == null) @@ -180,7 +194,7 @@ namespace Flax.Build.Bindings case TokenType.RightParent: parameters.Add(tag); return parameters; - default: throw new Exception($"Expected right parent or next argument, but got {token.Type}."); + default: throw new ParseException(ref context, $"Expected right parent or next argument, but got {token.Type}."); } } } @@ -302,7 +316,7 @@ namespace Flax.Build.Bindings if (context.PreprocessorDefines.TryGetValue(length, out var define)) length = define; if (!int.TryParse(length, out type.ArraySize)) - throw new Exception($"Failed to parse the field {entry} array size '{length}'"); + throw new ParseException(ref context, $"Failed to parse the field {entry} array size '{length}'"); } private static List ParseFunctionParameters(ref ParsingContext context) @@ -354,7 +368,7 @@ namespace Flax.Build.Bindings if (valid) break; var location = "function parameter"; - Log.Warning($"Unknown or not supported tag parameter {tag} used on {location} at line {context.Tokenizer.CurrentLine}"); + Log.Warning(GetParseErrorLocation(ref context, $"Unknown or not supported tag parameter '{tag}' used on '{location}'")); break; } } @@ -483,8 +497,7 @@ namespace Flax.Build.Bindings desc.Inheritance = new List(); desc.Inheritance.Add(inheritType); token = context.Tokenizer.NextToken(); - while (token.Type == TokenType.CommentSingleLine - || token.Type == TokenType.CommentMultiLine) + while (token.Type == TokenType.CommentSingleLine || token.Type == TokenType.CommentMultiLine) { token = context.Tokenizer.NextToken(); } @@ -563,7 +576,7 @@ namespace Flax.Build.Bindings // Read 'class' keyword var token = context.Tokenizer.NextToken(); if (token.Value != "class") - throw new Exception($"Invalid {ApiTokens.Class} usage (expected 'class' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}')."); + throw new ParseException(ref context, $"Invalid {ApiTokens.Class} usage (expected 'class' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}')."); // Read specifiers while (true) @@ -644,7 +657,7 @@ namespace Flax.Build.Bindings ParseTypeTag?.Invoke(ref valid, tag, desc); if (valid) break; - Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name} at line {context.Tokenizer.CurrentLine}"); + Log.Warning(GetParseErrorLocation(ref context, $"Unknown or not supported tag parameter '{tag}' used on '{desc.Name}'")); break; } } @@ -672,7 +685,7 @@ namespace Flax.Build.Bindings // Read 'class' keyword var token = context.Tokenizer.NextToken(); if (token.Value != "class") - throw new Exception($"Invalid {ApiTokens.Interface} usage (expected 'class' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}')."); + throw new ParseException(ref context, $"Invalid {ApiTokens.Interface} usage (expected 'class' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}')."); // Read specifiers while (true) @@ -735,7 +748,7 @@ namespace Flax.Build.Bindings ParseTypeTag?.Invoke(ref valid, tag, desc); if (valid) break; - Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name} at line {context.Tokenizer.CurrentLine}"); + Log.Warning(GetParseErrorLocation(ref context, $"Unknown or not supported tag parameter '{tag}' used on '{desc.Name}'")); break; } } @@ -815,13 +828,13 @@ namespace Flax.Build.Bindings { case "const": if (desc.IsConst) - throw new Exception($"Invalid double 'const' specifier in function {desc.Name} at line {context.Tokenizer.CurrentLine}."); + throw new ParseException(ref context, $"Invalid double 'const' specifier in function {desc.Name}"); desc.IsConst = true; break; case "override": desc.IsVirtual = true; break; - default: throw new Exception($"Unknown identifier '{token.Value}' in function {desc.Name} at line {context.Tokenizer.CurrentLine}."); + default: throw new ParseException(ref context, $"Unknown identifier '{token.Value}' in function {desc.Name}"); } } else if (token.Type == TokenType.LeftCurlyBrace) @@ -875,7 +888,7 @@ namespace Flax.Build.Bindings ParseMemberTag?.Invoke(ref valid, tag, desc); if (valid) break; - Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name} at line {context.Tokenizer.CurrentLine}"); + Log.Warning(GetParseErrorLocation(ref context, $"Unknown or not supported tag parameter '{tag}' used on '{desc.Name}'")); break; } } @@ -892,11 +905,11 @@ namespace Flax.Build.Bindings var classInfo = context.ScopeInfo as ClassInfo; if (classInfo == null) - throw new Exception($"Found property {propertyName} outside the class at line {context.Tokenizer.CurrentLine}."); + throw new ParseException(ref context, $"Found property {propertyName} outside the class"); var isGetter = !functionInfo.ReturnType.IsVoid; if (!isGetter && functionInfo.Parameters.Count == 0) - throw new Exception($"Property {propertyName} setter method in class {classInfo.Name} has to have value parameter to set (line {context.Tokenizer.CurrentLine})."); + throw new ParseException(ref context, $"Property {propertyName} setter method in class {classInfo.Name} has to have value parameter to set (line {context.Tokenizer.CurrentLine})."); var propertyType = isGetter ? functionInfo.ReturnType : functionInfo.Parameters[0].Type; var propertyInfo = classInfo.Properties.FirstOrDefault(x => x.Name == propertyName); @@ -917,7 +930,7 @@ namespace Flax.Build.Bindings else { if (propertyInfo.IsStatic != functionInfo.IsStatic) - throw new Exception($"Property {propertyName} in class {classInfo.Name} has to have both getter and setter methods static or non-static (line {context.Tokenizer.CurrentLine})."); + throw new ParseException(ref context, $"Property {propertyName} in class {classInfo.Name} has to have both getter and setter methods static or non-static (line {context.Tokenizer.CurrentLine})."); } if (functionInfo.Tags != null) { @@ -934,9 +947,9 @@ namespace Flax.Build.Bindings } if (isGetter && propertyInfo.Getter != null) - throw new Exception($"Property {propertyName} in class {classInfo.Name} cannot have multiple getter method (line {context.Tokenizer.CurrentLine}). Getter methods of properties must return value, while setters take this as a parameter."); + throw new ParseException(ref context, $"Property {propertyName} in class {classInfo.Name} cannot have multiple getter method (line {context.Tokenizer.CurrentLine}). Getter methods of properties must return value, while setters take this as a parameter."); if (!isGetter && propertyInfo.Setter != null) - throw new Exception($"Property {propertyName} in class {classInfo.Name} cannot have multiple setter method (line {context.Tokenizer.CurrentLine}). Getter methods of properties must return value, while setters take this as a parameter."); + throw new ParseException(ref context, $"Property {propertyName} in class {classInfo.Name} cannot have multiple setter method (line {context.Tokenizer.CurrentLine}). Getter methods of properties must return value, while setters take this as a parameter."); if (isGetter) propertyInfo.Getter = functionInfo; @@ -963,7 +976,7 @@ namespace Flax.Build.Bindings return propertyInfo; if (getterType.Type == "Array" && setterType.Type == "Span" && getterType.GenericArgs?.Count == 1 && setterType.GenericArgs?.Count == 1 && getterType.GenericArgs[0].Equals(setterType.GenericArgs[0])) return propertyInfo; - throw new Exception($"Property {propertyName} in class {classInfo.Name} (line {context.Tokenizer.CurrentLine}) has mismatching getter return type ({getterType}) and setter parameter type ({setterType}). Both getter and setter methods must use the same value type used for property."); + throw new ParseException(ref context, $"Property {propertyName} in class {classInfo.Name} (line {context.Tokenizer.CurrentLine}) has mismatching getter return type ({getterType}) and setter parameter type ({setterType}). Both getter and setter methods must use the same value type used for property."); } if (propertyInfo.Comment != null) @@ -996,7 +1009,7 @@ namespace Flax.Build.Bindings // Read 'enum' or `enum class` keywords var token = context.Tokenizer.NextToken(); if (token.Value != "enum") - throw new Exception($"Invalid {ApiTokens.Enum} usage at line {context.Tokenizer.CurrentLine} (expected 'enum' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}')."); + throw new ParseException(ref context, $"Invalid {ApiTokens.Enum} usage (expected 'enum' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}')."); token = context.Tokenizer.NextToken(); if (token.Value != "class") context.Tokenizer.PreviousToken(); @@ -1079,7 +1092,7 @@ namespace Flax.Build.Bindings entry.Attributes = tag.Value; break; default: - Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name + " enum entry"} at line {context.Tokenizer.CurrentLine}"); + Log.Warning(GetParseErrorLocation(ref context, $"Unknown or not supported tag parameter '{tag}' used on '{desc.Name}'")); break; } } @@ -1153,7 +1166,7 @@ namespace Flax.Build.Bindings ParseTypeTag?.Invoke(ref valid, tag, desc); if (valid) break; - Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name} at line {context.Tokenizer.CurrentLine}"); + Log.Warning(GetParseErrorLocation(ref context, $"Unknown or not supported tag parameter '{tag}' used on '{desc.Name}'")); break; } } @@ -1176,7 +1189,7 @@ namespace Flax.Build.Bindings // Read 'struct' keyword var token = context.Tokenizer.NextToken(); if (token.Value != "struct") - throw new Exception($"Invalid {ApiTokens.Struct} usage (expected 'struct' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}')."); + throw new ParseException(ref context, $"Invalid {ApiTokens.Struct} usage (expected 'struct' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}')."); // Read name desc.Name = desc.NativeName = ParseName(ref context); @@ -1233,7 +1246,7 @@ namespace Flax.Build.Bindings ParseTypeTag?.Invoke(ref valid, tag, desc); if (valid) break; - Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name} at line {context.Tokenizer.CurrentLine}"); + Log.Warning(GetParseErrorLocation(ref context, $"Unknown or not supported tag parameter '{tag}' used on '{desc.Name}'")); break; } } @@ -1366,7 +1379,7 @@ namespace Flax.Build.Bindings ParseMemberTag?.Invoke(ref valid, tag, desc); if (valid) break; - Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name} at line {context.Tokenizer.CurrentLine}"); + Log.Warning(GetParseErrorLocation(ref context, $"Unknown or not supported tag parameter '{tag}' used on '{desc.Name}'")); break; } } @@ -1396,7 +1409,7 @@ namespace Flax.Build.Bindings if (desc.Type.Type == "Action") desc.Type = new TypeInfo { Type = "Delegate", GenericArgs = new List() }; else if (desc.Type.Type != "Delegate") - throw new Exception($"Invalid {ApiTokens.Event} type. Only Action and Delegate<> types are supported. '{desc.Type}' used on event at line {context.Tokenizer.CurrentLine}."); + throw new ParseException(ref context, $"Invalid {ApiTokens.Event} type. Only Action and Delegate<> types are supported. '{desc.Type}' used on event."); // Read name desc.Name = ParseName(ref context); @@ -1438,7 +1451,7 @@ namespace Flax.Build.Bindings ParseMemberTag?.Invoke(ref valid, tag, desc); if (valid) break; - Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name} at line {context.Tokenizer.CurrentLine}"); + Log.Warning(GetParseErrorLocation(ref context, $"Unknown or not supported tag parameter '{tag}' used on '{desc.Name}'")); break; } } @@ -1474,7 +1487,7 @@ namespace Flax.Build.Bindings // Read 'typedef' keyword var token = context.Tokenizer.NextToken(); if (token.Value != "typedef") - throw new Exception($"Invalid {ApiTokens.Typedef} usage (expected 'typedef' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}')."); + throw new ParseException(ref context, $"Invalid {ApiTokens.Typedef} usage (expected 'typedef' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}')."); // Read type definition desc.Type = ParseType(ref context); @@ -1513,7 +1526,7 @@ namespace Flax.Build.Bindings ParseTypeTag?.Invoke(ref valid, tag, desc); if (valid) break; - Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name} at line {context.Tokenizer.CurrentLine}"); + Log.Warning(GetParseErrorLocation(ref context, $"Unknown or not supported tag parameter '{tag}' used on '{desc.Name}'")); break; } }