Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<Title>D2L.CodeStyle.Analyzers</Title>
<Product>D2L.CodeStyle</Product>
<Description>D2L.CodeStyle analyzers</Description>
<Version>0.229.0</Version>
<Version>0.230.0</Version>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/Brightspace/D2L.CodeStyle</PackageProjectUrl>
<Authors>D2L</Authors>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<Title>D2L.CodeStyle.TestAnalyzers</Title>
<Product>D2L.CodeStyle</Product>
<Description>D2L.CodeStyle test analyzers</Description>
<Version>0.32.0</Version>
<Version>0.33.0</Version>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/Brightspace/D2L.CodeStyle</PackageProjectUrl>
<Authors>D2L</Authors>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,39 +162,84 @@ private static void AnalyzeConversion(
IConversionOperation conversion,
INamedTypeSymbol constantAttribute
) {
// Operand is a constant value or string.Empty, so trust it
IOperation operand = conversion.Operand;
if( operand.ConstantValue.HasValue || IsStringEmpty( operand ) ) {
return;
}

IMethodSymbol @operator = conversion.OperatorMethod;
if( @operator is null ) {
// Operand was defined as [Constant] already, so trust it
ISymbol operandSymbol = conversion.SemanticModel.GetSymbolInfo( operand.Syntax ).Symbol;
if( operandSymbol is not null && HasAttribute( operandSymbol, constantAttribute ) ) {
return;
}
Comment on lines +165 to 175

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically moved to check constant operands first, and anything after that is deciding if the conversion was to something with a [Constant] parameter that we should raise a diagnostic for

if( @operator.Parameters.Length != 1 ) {

AnalyzeConversion(
context,
constantAttribute,
sourceType: conversion.Operand.Type,
targetType: conversion.Type,
conversion.Operand.Syntax
);
}

private static void AnalyzeConversion(
OperationAnalysisContext context,
INamedTypeSymbol constantAttribute,
ITypeSymbol sourceType,
ITypeSymbol targetType,
SyntaxNode diagnosticSyntax
) {
Conversion conversion = context.Compilation.ClassifyConversion(
source: sourceType,
destination: targetType
);

if( !conversion.Exists ) {
return;
}

// Operator parameter is not [Constant], so do nothing
IParameterSymbol parameter = @operator.Parameters[0];
if( !HasAttribute( parameter, constantAttribute ) ) {
if( conversion.MethodSymbol is { } @operator ) {
// Operator parameter is not [Constant], so do nothing
IParameterSymbol parameter = @operator.Parameters[ 0 ];
if( !HasAttribute( parameter, constantAttribute ) ) {
return;
}

// Operand is not constant, so report it
context.ReportDiagnostic(
descriptor: Diagnostics.NonConstantPassedToConstantParameter,
location: diagnosticSyntax.GetLocation(),
messageArgs: [ parameter.Name ]
);
return;
}

// Operand is a constant value or string.Empty, so trust it
IOperation operand = conversion.Operand;
if( operand.ConstantValue.HasValue || IsStringEmpty( operand ) ) {
if( !conversion.IsTupleConversion ) {
return;
}

// Operand was defined as [Constant] already, so trust it
ISymbol operandSymbol = conversion.SemanticModel.GetSymbolInfo( operand.Syntax ).Symbol;
if( operandSymbol is not null && HasAttribute( operandSymbol, constantAttribute ) ) {
if( sourceType is not INamedTypeSymbol namedSourceType ) {
return;
}

// Operand is not constant, so report it
context.ReportDiagnostic(
descriptor: Diagnostics.NonConstantPassedToConstantParameter,
location: operand.Syntax.GetLocation(),
messageArgs: new[] { parameter.Name }
);
if( targetType is not INamedTypeSymbol namedTargetType ) {
return;
}

if( namedSourceType.TypeArguments.Length != namedTargetType.TypeArguments.Length ) {
return;
}

for( int i = 0; i < namedSourceType.TypeArguments.Length; i++ ) {
AnalyzeConversion(
context,
constantAttribute,
sourceType: namedSourceType.TypeArguments[ i ],
targetType: namedTargetType.TypeArguments[ i ],
diagnosticSyntax
);
}
}

private static void AnalyzeCompoundAssignment(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,12 @@ string untrusted
{ Types.ConstantStruct v = trusted; }
{ Types.ConstantStruct v = /* NonConstantPassedToConstantParameter(value) */ variable /**/; }
{ Types.ConstantStruct v = /* NonConstantPassedToConstantParameter(value) */ untrusted /**/; }
{ (Types.ConstantStruct X, Types.ConstantStruct Y) v = (Constants.String, Constants.String); }
{ (Types.ConstantStruct X, Types.ConstantStruct Y) v = (Constants.String, trusted); }
{ (Types.ConstantStruct X, Types.ConstantStruct Y) v = (Constants.String, /* NonConstantPassedToConstantParameter(value) */ variable /**/); }
{ (Types.ConstantStruct X, Types.ConstantStruct Y) v = (Constants.String, /* NonConstantPassedToConstantParameter(value) */ untrusted /**/); }
Comment on lines +278 to +281

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These already worked, just adding the tests for it

{ (Types.ConstantStruct X, Types.ConstantStruct Y) v = /* NonConstantPassedToConstantParameter(value) */ true ? (Constants.String, Constants.String) : (Constants.String, Constants.String) /**/; }
{ (Types.ConstantStruct X, Types.ConstantStruct Y) v = /* NonConstantPassedToConstantParameter(value) */ true ? (Constants.String, variable) : (Constants.String, Constants.String) /**/; }
Comment on lines +282 to +283

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally the former of these two would work, but would require special-casing the ternarys etc. etc. (we similarly don't special case switch expressions or anything)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either of these cases previously fell into conversion.OperatorMethod is null and so we bailed despite the implicit tuple conversion existing.

{
Types.ConstantStruct v = Constants.String;
v += /* NonConstantPassedToConstantParameter(value) */ "abc" /**/;
Expand Down
Loading