Skip to content

Commit 2514855

Browse files
authored
Ps7 syntax (#1426)
* Add PS7 syntaxes to UseCompatibleSyntax rule * Use correct ifdef constant
1 parent d3cd1f7 commit 2514855

File tree

2 files changed

+179
-55
lines changed

2 files changed

+179
-55
lines changed

Rules/CompatibilityRules/UseCompatibleSyntax.cs

Lines changed: 162 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -183,27 +183,34 @@ public override AstVisitAction VisitInvokeMemberExpression(InvokeMemberExpressio
183183
{
184184
// Look for [typename]::new(...) and [typename]::$dynamicMethodName syntax
185185

186+
#if PSV7
187+
if (!TargetsNonPS7())
188+
{
189+
return AstVisitAction.Continue;
190+
}
191+
192+
if (methodCallAst.NullConditional)
193+
{
194+
AddDiagnostic(
195+
methodCallAst,
196+
"null-conditional method invocation",
197+
"${x}?.Method()",
198+
"3,4,5,6");
199+
}
200+
#endif
201+
186202
if (!_targetVersions.Contains(s_v3) && !_targetVersions.Contains(s_v4))
187203
{
188204
return AstVisitAction.Continue;
189205
}
190206

191207
if (_targetVersions.Contains(s_v3) && methodCallAst.Member is VariableExpressionAst)
192208
{
193-
string message = string.Format(
194-
CultureInfo.CurrentCulture,
195-
Strings.UseCompatibleSyntaxError,
209+
AddDiagnostic(
210+
methodCallAst,
196211
"dynamic method invocation",
197-
methodCallAst.Extent.Text,
212+
"$x.$method()",
198213
"3");
199-
200-
_diagnosticAccumulator.Add(new DiagnosticRecord(
201-
message,
202-
methodCallAst.Extent,
203-
_rule.GetName(),
204-
_rule.Severity,
205-
_analyzedFilePath
206-
));
207214
}
208215

209216
if (!(methodCallAst.Expression is TypeExpressionAst typeExpressionAst))
@@ -226,22 +233,7 @@ public override AstVisitAction VisitInvokeMemberExpression(InvokeMemberExpressio
226233
typeName,
227234
methodCallAst.Arguments);
228235

229-
string message = string.Format(
230-
CultureInfo.CurrentCulture,
231-
Strings.UseCompatibleSyntaxError,
232-
"constructor",
233-
methodCallAst.Extent.Text,
234-
"3,4");
235-
236-
_diagnosticAccumulator.Add(new DiagnosticRecord(
237-
message,
238-
methodCallAst.Extent,
239-
_rule.GetName(),
240-
_rule.Severity,
241-
_analyzedFilePath,
242-
ruleId: null,
243-
suggestedCorrections: new [] { suggestedCorrection }
244-
));
236+
AddDiagnostic(methodCallAst, "constructor", "[type]::new()", "3,4", suggestedCorrection);
245237

246238
return AstVisitAction.Continue;
247239
}
@@ -263,21 +255,7 @@ public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst fun
263255
return AstVisitAction.Continue;
264256
}
265257

266-
string message = string.Format(
267-
CultureInfo.CurrentCulture,
268-
Strings.UseCompatibleSyntaxError,
269-
"workflow",
270-
"workflow { ... }",
271-
"6");
272-
273-
_diagnosticAccumulator.Add(
274-
new DiagnosticRecord(
275-
message,
276-
functionDefinitionAst.Extent,
277-
_rule.GetName(),
278-
_rule.Severity,
279-
_analyzedFilePath
280-
));
258+
AddDiagnostic(functionDefinitionAst, "workflow", "workflow { ... }", "6,7");
281259

282260
return AstVisitAction.Continue;
283261
}
@@ -292,22 +270,12 @@ public override AstVisitAction VisitUsingStatement(UsingStatementAst usingStatem
292270
return AstVisitAction.Continue;
293271
}
294272

295-
string message = string.Format(
296-
CultureInfo.CurrentCulture,
297-
Strings.UseCompatibleSyntaxError,
273+
AddDiagnostic(
274+
usingStatementAst,
298275
"using statement",
299276
"using ...;",
300277
"3,4");
301278

302-
_diagnosticAccumulator.Add(
303-
new DiagnosticRecord(
304-
message,
305-
usingStatementAst.Extent,
306-
_rule.GetName(),
307-
_rule.Severity,
308-
_analyzedFilePath
309-
));
310-
311279
return AstVisitAction.Continue;
312280
}
313281

@@ -340,6 +308,145 @@ public override AstVisitAction VisitTypeDefinition(TypeDefinitionAst typeDefinit
340308
}
341309
#endif
342310

311+
#if PSV7
312+
public override AstVisitAction VisitMemberExpression(MemberExpressionAst memberExpressionAst)
313+
{
314+
if (!TargetsNonPS7())
315+
{
316+
return AstVisitAction.Continue;
317+
}
318+
319+
if (memberExpressionAst.NullConditional)
320+
{
321+
AddDiagnostic(
322+
memberExpressionAst,
323+
"null-conditional member access",
324+
"${x}?.Member",
325+
"3,4,5,6");
326+
}
327+
328+
return AstVisitAction.Continue;
329+
}
330+
331+
public override AstVisitAction VisitAssignmentStatement(AssignmentStatementAst assignmentStatementAst)
332+
{
333+
if (!TargetsNonPS7())
334+
{
335+
return AstVisitAction.Continue;
336+
}
337+
338+
if (assignmentStatementAst.Operator == TokenKind.QuestionQuestionEquals)
339+
{
340+
AddDiagnostic(assignmentStatementAst, "null-conditional assignment", "$x ??= $y", "3,4,5,6");
341+
}
342+
343+
return AstVisitAction.Continue;
344+
}
345+
346+
public override AstVisitAction VisitBinaryExpression(BinaryExpressionAst binaryExpressionAst)
347+
{
348+
if (!TargetsNonPS7())
349+
{
350+
return AstVisitAction.Continue;
351+
}
352+
353+
if (binaryExpressionAst.Operator == TokenKind.QuestionQuestion)
354+
{
355+
AddDiagnostic(
356+
binaryExpressionAst,
357+
"null-coalescing operator",
358+
"$x ?? $y",
359+
"3,4,5,6");
360+
}
361+
362+
return AstVisitAction.Continue;
363+
}
364+
365+
public override AstVisitAction VisitTernaryExpression(TernaryExpressionAst ternaryExpressionAst)
366+
{
367+
if (!TargetsNonPS7())
368+
{
369+
return AstVisitAction.Continue;
370+
}
371+
372+
var correction = new CorrectionExtent(
373+
ternaryExpressionAst.Extent,
374+
$"if ({ternaryExpressionAst.Condition.Extent.Text}) {{ {ternaryExpressionAst.IfTrue.Extent.Text} }} else {{ {ternaryExpressionAst.IfFalse.Extent.Text} }}",
375+
_analyzedFilePath);
376+
377+
AddDiagnostic(
378+
ternaryExpressionAst,
379+
"ternary expression",
380+
"<test> ? <exp1> : <exp2>",
381+
"3,4,5,6",
382+
correction);
383+
384+
return AstVisitAction.Continue;
385+
}
386+
387+
public override AstVisitAction VisitPipelineChain(PipelineChainAst statementChain)
388+
{
389+
if (!TargetsNonPS7())
390+
{
391+
return AstVisitAction.Continue;
392+
}
393+
394+
AddDiagnostic(
395+
statementChain,
396+
"pipeline chain",
397+
"<pipeline1> && <pipeline2> OR <pipeline1> || <pipeline2>",
398+
"3,4,5,6");
399+
400+
return AstVisitAction.Continue;
401+
}
402+
403+
private bool TargetsNonPS7()
404+
{
405+
return _targetVersions.Contains(s_v3)
406+
|| _targetVersions.Contains(s_v4)
407+
|| _targetVersions.Contains(s_v5)
408+
|| _targetVersions.Contains(s_v6);
409+
}
410+
#endif
411+
412+
private void AddDiagnostic(
413+
Ast offendingAst,
414+
string syntaxName,
415+
string syntaxExample,
416+
string unsupportedVersions,
417+
CorrectionExtent correction = null)
418+
{
419+
string message = string.Format(
420+
CultureInfo.CurrentCulture,
421+
Strings.UseCompatibleSyntaxError,
422+
syntaxName,
423+
syntaxExample,
424+
unsupportedVersions);
425+
426+
if (correction == null)
427+
{
428+
_diagnosticAccumulator.Add(
429+
new DiagnosticRecord(
430+
message,
431+
offendingAst.Extent,
432+
_rule.GetName(),
433+
_rule.Severity,
434+
_analyzedFilePath));
435+
436+
return;
437+
}
438+
439+
_diagnosticAccumulator.Add(
440+
new DiagnosticRecord(
441+
message,
442+
offendingAst.Extent,
443+
_rule.GetName(),
444+
_rule.Severity,
445+
_analyzedFilePath,
446+
ruleId: null,
447+
new[] { correction }));
448+
}
449+
343450
private static CorrectionExtent CreateNewObjectCorrection(
344451
string filePath,
345452
IScriptExtent offendingExtent,

Tests/Rules/UseCompatibleSyntax.Tests.ps1

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,23 @@ Describe "PSUseCompatibleSyntax" {
3131
@{ Script = 'workflow Banana { Do-ExpensiveCommandOnAnotherMachine -Argument "Banana" }'; Versions = @(6) }
3232
)
3333
}
34+
35+
if ($PSVersionTable.PSVersion.Major -ge 7)
36+
{
37+
$testCases += @(
38+
@{ Script = '$x = $path ? (Get-Content -Raw $path) : "default"'; Versions = @(3,4,5,6) }
39+
@{ Script = '$x ??= 7'; Versions = @(3,4,5,6) }
40+
@{ Script = 'git pull origin master && git pull upstream master'; Versions = @(3,4,5,6) }
41+
)
42+
43+
if ((Get-ExperimentalFeature -Name 'PSNullConditionalOperators').Enabled)
44+
{
45+
$testCases += @(
46+
@{ Script = '${item}?.Invoke()'; Versions = @(3,4,5,6) }
47+
@{ Script = '${object}?.Member'; Versions = @(3,4,5,6) }
48+
)
49+
}
50+
}
3451
}
3552

3653
foreach ($v in 3,4,5,6)

0 commit comments

Comments
 (0)