Tools and libraries to glue C/C++ APIs to high-level languages
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

142 lines
4.5 KiB

using System.Collections.Generic;
using System.Linq;
using System.Text;
using CppSharp.AST;
using CppSharp.Generators.CLI;
using CppSharp.Generators.CSharp;
using CppSharp.Generators;
namespace CppSharp.Passes
{
public class CleanInvalidDeclNamesPass : TranslationUnitPass
{
public override bool VisitClassDecl(Class @class)
{
if (!base.VisitClassDecl(@class))
return false;
if (Options.GeneratorKind == GeneratorKind.CLI &&
string.IsNullOrEmpty(@class.OriginalName))
{
@class.ExplicitlyIgnore();
return true;
}
if (@class.Layout != null)
{
int order = 0;
foreach (var field in @class.Layout.Fields)
field.Name = CheckName(field.Name, ref order);
}
return true;
}
public override bool VisitEnumDecl(Enumeration @enum)
{
if (!base.VisitEnumDecl(@enum))
return false;
CheckChildrenNames(@enum.Items,
string.IsNullOrEmpty(@enum.Name) ? 1 : 0);
CheckEnumName(@enum);
return true;
}
public override bool VisitFunctionDecl(Function function)
{
if (!base.VisitFunctionDecl(function))
return false;
CheckChildrenNames(function.Parameters);
return true;
}
public override bool VisitDeclarationContext(DeclarationContext context)
{
if (!base.VisitDeclarationContext(context))
return false;
DeclarationContext currentContext = context;
int parents = 0;
while (currentContext != null)
{
parents++;
currentContext = currentContext.Namespace;
}
int order = parents % 2;
CheckChildrenNames(context.Declarations, ref order);
var @class = context as Class;
if (@class != null)
CheckChildrenNames(@class.Fields, order);
return true;
}
public override bool VisitFunctionType(FunctionType function, TypeQualifiers quals)
{
if (!base.VisitFunctionType(function, quals))
return false;
CheckChildrenNames(function.Parameters);
return true;
}
private void CheckChildrenNames(IEnumerable<Declaration> children, int order = 0) =>
CheckChildrenNames(children, ref order);
private void CheckChildrenNames(IEnumerable<Declaration> children, ref int order)
{
foreach (var child in children)
child.Name = CheckName(child.Name, ref order);
}
private void CheckEnumName(Enumeration @enum)
{
// If we still do not have a valid name, then try to guess one
// based on the enum value names.
if (!string.IsNullOrWhiteSpace(@enum.Name))
return;
var prefix = @enum.Items.Select(item => item.Name)
.ToArray().CommonPrefix();
// Try a simple heuristic to make sure we end up with a valid name.
if (prefix.Length < 3)
{
int order = @enum.Namespace.Enums.Count(e => e != @enum &&
string.IsNullOrEmpty(e.Name));
@enum.Name = CheckName(@enum.Name, ref order);
return;
}
var prefixBuilder = new StringBuilder(prefix);
prefixBuilder.TrimUnderscores();
while (@enum.Namespace.Enums.Any(e => e != @enum &&
e.Name == prefixBuilder.ToString()))
prefixBuilder.Append('_');
@enum.Name = prefixBuilder.ToString();
}
private string CheckName(string name, ref int order)
{
// Generate a new name if the decl still does not have a name
if (string.IsNullOrWhiteSpace(name))
return $"_{order++}";
// Clean up the item name if the first digit is not a valid name.
if (char.IsNumber(name[0]))
return '_' + name;
// TODO: Fix this to not need per-generator code.
var units = new List<TranslationUnit> { new TranslationUnit() };
if (Options.IsCLIGenerator)
return new CLIHeaders(Context, units).SafeIdentifier(name);
return new CSharpSources(Context, units).SafeIdentifier(name);
}
}
}