mirror of
https://github.com/Tyrrrz/CliFx.git
synced 2025-10-25 15:19:17 +00:00
Refactor & improve argument conversion feature
This commit is contained in:
@@ -17,16 +17,19 @@ namespace CliFx.Analyzers
|
||||
DiagnosticDescriptors.CliFx0022,
|
||||
DiagnosticDescriptors.CliFx0023,
|
||||
DiagnosticDescriptors.CliFx0024,
|
||||
DiagnosticDescriptors.CliFx0025,
|
||||
DiagnosticDescriptors.CliFx0041,
|
||||
DiagnosticDescriptors.CliFx0042,
|
||||
DiagnosticDescriptors.CliFx0043,
|
||||
DiagnosticDescriptors.CliFx0044,
|
||||
DiagnosticDescriptors.CliFx0045
|
||||
DiagnosticDescriptors.CliFx0045,
|
||||
DiagnosticDescriptors.CliFx0046
|
||||
);
|
||||
|
||||
private static bool IsScalarType(ITypeSymbol typeSymbol) =>
|
||||
KnownSymbols.IsSystemString(typeSymbol) ||
|
||||
!typeSymbol.AllInterfaces.Select(i => i.ConstructedFrom).Any(KnownSymbols.IsSystemCollectionsGenericIEnumerable);
|
||||
!typeSymbol.AllInterfaces.Select(i => i.ConstructedFrom)
|
||||
.Any(KnownSymbols.IsSystemCollectionsGenericIEnumerable);
|
||||
|
||||
private static void CheckCommandParameterProperties(
|
||||
SymbolAnalysisContext context,
|
||||
@@ -50,11 +53,18 @@ namespace CliFx.Analyzers
|
||||
.Select(a => a.Value.Value)
|
||||
.FirstOrDefault() as string;
|
||||
|
||||
var converter = attribute
|
||||
.NamedArguments
|
||||
.Where(a => a.Key == "Converter")
|
||||
.Select(a => a.Value.Value)
|
||||
.FirstOrDefault() as ITypeSymbol;
|
||||
|
||||
return new
|
||||
{
|
||||
Property = p,
|
||||
Order = order,
|
||||
Name = name
|
||||
Name = name,
|
||||
Converter = converter
|
||||
};
|
||||
})
|
||||
.ToArray();
|
||||
@@ -69,8 +79,9 @@ namespace CliFx.Analyzers
|
||||
|
||||
foreach (var parameter in duplicateOrderParameters)
|
||||
{
|
||||
context.ReportDiagnostic(
|
||||
Diagnostic.Create(DiagnosticDescriptors.CliFx0021, parameter.Property.Locations.First()));
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
DiagnosticDescriptors.CliFx0021, parameter.Property.Locations.First()
|
||||
));
|
||||
}
|
||||
|
||||
// Duplicate name
|
||||
@@ -83,8 +94,9 @@ namespace CliFx.Analyzers
|
||||
|
||||
foreach (var parameter in duplicateNameParameters)
|
||||
{
|
||||
context.ReportDiagnostic(
|
||||
Diagnostic.Create(DiagnosticDescriptors.CliFx0022, parameter.Property.Locations.First()));
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
DiagnosticDescriptors.CliFx0022, parameter.Property.Locations.First()
|
||||
));
|
||||
}
|
||||
|
||||
// Multiple non-scalar
|
||||
@@ -96,8 +108,9 @@ namespace CliFx.Analyzers
|
||||
{
|
||||
foreach (var parameter in nonScalarParameters)
|
||||
{
|
||||
context.ReportDiagnostic(
|
||||
Diagnostic.Create(DiagnosticDescriptors.CliFx0023, parameter.Property.Locations.First()));
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
DiagnosticDescriptors.CliFx0023, parameter.Property.Locations.First()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,8 +122,23 @@ namespace CliFx.Analyzers
|
||||
|
||||
if (nonLastNonScalarParameter != null)
|
||||
{
|
||||
context.ReportDiagnostic(
|
||||
Diagnostic.Create(DiagnosticDescriptors.CliFx0024, nonLastNonScalarParameter.Property.Locations.First()));
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
DiagnosticDescriptors.CliFx0024, nonLastNonScalarParameter.Property.Locations.First()
|
||||
));
|
||||
}
|
||||
|
||||
// Invalid converter
|
||||
var invalidConverterParameters = parameters
|
||||
.Where(p =>
|
||||
p.Converter != null &&
|
||||
!p.Converter.AllInterfaces.Any(KnownSymbols.IsArgumentValueConverterInterface))
|
||||
.ToArray();
|
||||
|
||||
foreach (var parameter in invalidConverterParameters)
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
DiagnosticDescriptors.CliFx0025, parameter.Property.Locations.First()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,12 +171,19 @@ namespace CliFx.Analyzers
|
||||
.Select(a => a.Value.Value)
|
||||
.FirstOrDefault() as string;
|
||||
|
||||
var converter = attribute
|
||||
.NamedArguments
|
||||
.Where(a => a.Key == "Converter")
|
||||
.Select(a => a.Value.Value)
|
||||
.FirstOrDefault() as ITypeSymbol;
|
||||
|
||||
return new
|
||||
{
|
||||
Property = p,
|
||||
Name = name,
|
||||
ShortName = shortName,
|
||||
EnvironmentVariableName = envVarName
|
||||
EnvironmentVariableName = envVarName,
|
||||
Converter = converter
|
||||
};
|
||||
})
|
||||
.ToArray();
|
||||
@@ -160,8 +195,9 @@ namespace CliFx.Analyzers
|
||||
|
||||
foreach (var option in noNameOptions)
|
||||
{
|
||||
context.ReportDiagnostic(
|
||||
Diagnostic.Create(DiagnosticDescriptors.CliFx0041, option.Property.Locations.First()));
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
DiagnosticDescriptors.CliFx0041, option.Property.Locations.First()
|
||||
));
|
||||
}
|
||||
|
||||
// Too short name
|
||||
@@ -171,8 +207,9 @@ namespace CliFx.Analyzers
|
||||
|
||||
foreach (var option in invalidNameLengthOptions)
|
||||
{
|
||||
context.ReportDiagnostic(
|
||||
Diagnostic.Create(DiagnosticDescriptors.CliFx0042, option.Property.Locations.First()));
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
DiagnosticDescriptors.CliFx0042, option.Property.Locations.First()
|
||||
));
|
||||
}
|
||||
|
||||
// Duplicate name
|
||||
@@ -185,8 +222,9 @@ namespace CliFx.Analyzers
|
||||
|
||||
foreach (var option in duplicateNameOptions)
|
||||
{
|
||||
context.ReportDiagnostic(
|
||||
Diagnostic.Create(DiagnosticDescriptors.CliFx0043, option.Property.Locations.First()));
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
DiagnosticDescriptors.CliFx0043, option.Property.Locations.First()
|
||||
));
|
||||
}
|
||||
|
||||
// Duplicate name
|
||||
@@ -199,8 +237,9 @@ namespace CliFx.Analyzers
|
||||
|
||||
foreach (var option in duplicateShortNameOptions)
|
||||
{
|
||||
context.ReportDiagnostic(
|
||||
Diagnostic.Create(DiagnosticDescriptors.CliFx0044, option.Property.Locations.First()));
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
DiagnosticDescriptors.CliFx0044, option.Property.Locations.First()
|
||||
));
|
||||
}
|
||||
|
||||
// Duplicate environment variable name
|
||||
@@ -213,8 +252,23 @@ namespace CliFx.Analyzers
|
||||
|
||||
foreach (var option in duplicateEnvironmentVariableNameOptions)
|
||||
{
|
||||
context.ReportDiagnostic(
|
||||
Diagnostic.Create(DiagnosticDescriptors.CliFx0045, option.Property.Locations.First()));
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
DiagnosticDescriptors.CliFx0045, option.Property.Locations.First()
|
||||
));
|
||||
}
|
||||
|
||||
// Invalid converter
|
||||
var invalidConverterOptions = options
|
||||
.Where(o =>
|
||||
o.Converter != null &&
|
||||
!o.Converter.AllInterfaces.Any(KnownSymbols.IsArgumentValueConverterInterface))
|
||||
.ToArray();
|
||||
|
||||
foreach (var option in invalidConverterOptions)
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
DiagnosticDescriptors.CliFx0046, option.Property.Locations.First()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,10 +306,12 @@ namespace CliFx.Analyzers
|
||||
var isAlmostValidCommandType = implementsCommandInterface ^ hasCommandAttribute;
|
||||
|
||||
if (isAlmostValidCommandType && !implementsCommandInterface)
|
||||
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.CliFx0001, namedTypeSymbol.Locations.First()));
|
||||
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.CliFx0001,
|
||||
namedTypeSymbol.Locations.First()));
|
||||
|
||||
if (isAlmostValidCommandType && !hasCommandAttribute)
|
||||
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.CliFx0002, namedTypeSymbol.Locations.First()));
|
||||
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.CliFx0002,
|
||||
namedTypeSymbol.Locations.First()));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -8,72 +8,98 @@ namespace CliFx.Analyzers
|
||||
new DiagnosticDescriptor(nameof(CliFx0001),
|
||||
"Type must implement the 'CliFx.ICommand' interface in order to be a valid command",
|
||||
"Type must implement the 'CliFx.ICommand' interface in order to be a valid command",
|
||||
"Usage", DiagnosticSeverity.Error, true);
|
||||
"Usage", DiagnosticSeverity.Error, true
|
||||
);
|
||||
|
||||
public static readonly DiagnosticDescriptor CliFx0002 =
|
||||
new DiagnosticDescriptor(nameof(CliFx0002),
|
||||
"Type must be annotated with the 'CliFx.Attributes.CommandAttribute' in order to be a valid command",
|
||||
"Type must be annotated with the 'CliFx.Attributes.CommandAttribute' in order to be a valid command",
|
||||
"Usage", DiagnosticSeverity.Error, true);
|
||||
"Usage", DiagnosticSeverity.Error, true
|
||||
);
|
||||
|
||||
public static readonly DiagnosticDescriptor CliFx0021 =
|
||||
new DiagnosticDescriptor(nameof(CliFx0021),
|
||||
"Parameter order must be unique within its command",
|
||||
"Parameter order must be unique within its command",
|
||||
"Usage", DiagnosticSeverity.Error, true);
|
||||
"Usage", DiagnosticSeverity.Error, true
|
||||
);
|
||||
|
||||
public static readonly DiagnosticDescriptor CliFx0022 =
|
||||
new DiagnosticDescriptor(nameof(CliFx0022),
|
||||
"Parameter order must have unique name within its command",
|
||||
"Parameter order must have unique name within its command",
|
||||
"Usage", DiagnosticSeverity.Error, true);
|
||||
"Usage", DiagnosticSeverity.Error, true
|
||||
);
|
||||
|
||||
public static readonly DiagnosticDescriptor CliFx0023 =
|
||||
new DiagnosticDescriptor(nameof(CliFx0023),
|
||||
"Only one non-scalar parameter per command is allowed",
|
||||
"Only one non-scalar parameter per command is allowed",
|
||||
"Usage", DiagnosticSeverity.Error, true);
|
||||
"Usage", DiagnosticSeverity.Error, true
|
||||
);
|
||||
|
||||
public static readonly DiagnosticDescriptor CliFx0024 =
|
||||
new DiagnosticDescriptor(nameof(CliFx0024),
|
||||
"Non-scalar parameter must be last in order",
|
||||
"Non-scalar parameter must be last in order",
|
||||
"Usage", DiagnosticSeverity.Error, true);
|
||||
"Usage", DiagnosticSeverity.Error, true
|
||||
);
|
||||
|
||||
public static readonly DiagnosticDescriptor CliFx0025 =
|
||||
new DiagnosticDescriptor(nameof(CliFx0025),
|
||||
"Parameter converter must implement 'CliFx.IArgumentValueConverter'",
|
||||
"Parameter converter must implement 'CliFx.IArgumentValueConverter'",
|
||||
"Usage", DiagnosticSeverity.Error, true
|
||||
);
|
||||
|
||||
public static readonly DiagnosticDescriptor CliFx0041 =
|
||||
new DiagnosticDescriptor(nameof(CliFx0041),
|
||||
"Option must have a name or short name specified",
|
||||
"Option must have a name or short name specified",
|
||||
"Usage", DiagnosticSeverity.Error, true);
|
||||
"Usage", DiagnosticSeverity.Error, true
|
||||
);
|
||||
|
||||
public static readonly DiagnosticDescriptor CliFx0042 =
|
||||
new DiagnosticDescriptor(nameof(CliFx0042),
|
||||
"Option name must be at least 2 characters long",
|
||||
"Option name must be at least 2 characters long",
|
||||
"Usage", DiagnosticSeverity.Error, true);
|
||||
"Usage", DiagnosticSeverity.Error, true
|
||||
);
|
||||
|
||||
public static readonly DiagnosticDescriptor CliFx0043 =
|
||||
new DiagnosticDescriptor(nameof(CliFx0043),
|
||||
"Option name must be unique within its command",
|
||||
"Option name must be unique within its command",
|
||||
"Usage", DiagnosticSeverity.Error, true);
|
||||
"Usage", DiagnosticSeverity.Error, true
|
||||
);
|
||||
|
||||
public static readonly DiagnosticDescriptor CliFx0044 =
|
||||
new DiagnosticDescriptor(nameof(CliFx0044),
|
||||
"Option short name must be unique within its command",
|
||||
"Option short name must be unique within its command",
|
||||
"Usage", DiagnosticSeverity.Error, true);
|
||||
"Usage", DiagnosticSeverity.Error, true
|
||||
);
|
||||
|
||||
public static readonly DiagnosticDescriptor CliFx0045 =
|
||||
new DiagnosticDescriptor(nameof(CliFx0045),
|
||||
"Option environment variable name must be unique within its command",
|
||||
"Option environment variable name must be unique within its command",
|
||||
"Usage", DiagnosticSeverity.Error, true);
|
||||
"Usage", DiagnosticSeverity.Error, true
|
||||
);
|
||||
|
||||
public static readonly DiagnosticDescriptor CliFx0046 =
|
||||
new DiagnosticDescriptor(nameof(CliFx0046),
|
||||
"Option converter must implement 'CliFx.IArgumentValueConverter'",
|
||||
"Option converter must implement 'CliFx.IArgumentValueConverter'",
|
||||
"Usage", DiagnosticSeverity.Error, true
|
||||
);
|
||||
|
||||
public static readonly DiagnosticDescriptor CliFx0100 =
|
||||
new DiagnosticDescriptor(nameof(CliFx0100),
|
||||
"Use the provided IConsole abstraction instead of System.Console to ensure that the command can be tested in isolation",
|
||||
"Use the provided IConsole abstraction instead of System.Console to ensure that the command can be tested in isolation",
|
||||
"Usage", DiagnosticSeverity.Warning, true);
|
||||
"Usage", DiagnosticSeverity.Warning, true
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace CliFx.Analyzers
|
||||
{
|
||||
public static class KnownSymbols
|
||||
internal static class KnownSymbols
|
||||
{
|
||||
public static bool IsSystemString(ISymbol symbol) =>
|
||||
symbol.DisplayNameMatches("string") ||
|
||||
@@ -25,6 +25,9 @@ namespace CliFx.Analyzers
|
||||
public static bool IsCommandInterface(ISymbol symbol) =>
|
||||
symbol.DisplayNameMatches("CliFx.ICommand");
|
||||
|
||||
public static bool IsArgumentValueConverterInterface(ISymbol symbol) =>
|
||||
symbol.DisplayNameMatches("CliFx.IArgumentValueConverter");
|
||||
|
||||
public static bool IsCommandAttribute(ISymbol symbol) =>
|
||||
symbol.DisplayNameMatches("CliFx.Attributes.CommandAttribute");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user