Skip to content

Commit 77ee83d

Browse files
committed
Fix global using detection
The previous implementation could incorrectly report a non-global using directive as a global using directive because it didn't check for the global keyword.
1 parent 84f2f14 commit 77ee83d

File tree

3 files changed

+76
-6
lines changed

3 files changed

+76
-6
lines changed

StyleCop.Analyzers/StyleCop.Analyzers/Helpers/UsingAliasCache.cs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,18 +64,28 @@ private static bool ContainsLocalUsingAliasNoCache(SyntaxTree tree)
6464
{
6565
// Check for "local" using aliases
6666
var nodes = tree.GetRoot().DescendantNodes(node => node.IsKind(SyntaxKind.CompilationUnit) || node.IsKind(SyntaxKind.NamespaceDeclaration) || node.IsKind(SyntaxKindEx.FileScopedNamespaceDeclaration));
67-
return nodes.OfType<UsingDirectiveSyntax>().Any(x => x.Alias != null);
67+
return nodes.OfType<UsingDirectiveSyntax>().Any(x => x.GlobalKeyword().IsKind(SyntaxKind.None) && x.Alias != null);
68+
}
69+
70+
private static bool ContainsGlobalUsingAliasInCurrentFileNoCache(SyntaxTree tree)
71+
{
72+
// Check for "global" using aliases in one specific syntax tree
73+
var nodes = ((CompilationUnitSyntax)tree.GetRoot()).Usings;
74+
return nodes.Any(x => x.GlobalKeyword().IsKind(SyntaxKindEx.GlobalKeyword) && x.Alias != null);
6875
}
6976

7077
private bool ContainsGlobalUsingAlias(SemanticModel semanticModel, CancellationToken cancellationToken)
7178
{
7279
if (this.perCompilationCache == -1)
7380
{
74-
// Check for global using aliases. We check at the end of the file since GetImportScopes is observed to
75-
// omit global using directives in the current file at position 0 for the specific case where there is
76-
// no whitespace preceding the global using directives.
77-
var scopes = semanticModel.GetImportScopes(semanticModel.SyntaxTree.Length, cancellationToken);
78-
Interlocked.CompareExchange(ref this.perCompilationCache, scopes.Any(x => x.Aliases.Length > 0) ? 1 : 0, -1);
81+
// Check for global using aliases. We check at the beginning of the file to ensure using directives in
82+
// the current file are not picked up as a scope. Since global using directives in the current file may
83+
// not be reported by this call, we include a second check specifically for global usings defined in the
84+
// current file.
85+
var scopes = semanticModel.GetImportScopes(0, cancellationToken);
86+
bool hasGlobalUsingAlias = scopes.Any(x => x.Aliases.Length > 0)
87+
|| ContainsGlobalUsingAliasInCurrentFileNoCache(semanticModel.SyntaxTree);
88+
Interlocked.CompareExchange(ref this.perCompilationCache, hasGlobalUsingAlias ? 1 : 0, -1);
7989
}
8090

8191
return this.perCompilationCache == 1;

StyleCop.Analyzers/StyleCop.Analyzers/Lightup/SyntaxKindEx.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ internal static class SyntaxKindEx
99
{
1010
public const SyntaxKind DotDotToken = (SyntaxKind)8222;
1111
public const SyntaxKind QuestionQuestionEqualsToken = (SyntaxKind)8284;
12+
public const SyntaxKind GlobalKeyword = (SyntaxKind)8408;
1213
public const SyntaxKind OrKeyword = (SyntaxKind)8438;
1314
public const SyntaxKind AndKeyword = (SyntaxKind)8439;
1415
public const SyntaxKind NotKeyword = (SyntaxKind)8440;
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
namespace StyleCop.Analyzers.Lightup
5+
{
6+
using System;
7+
using Microsoft.CodeAnalysis;
8+
using Microsoft.CodeAnalysis.CSharp.Syntax;
9+
10+
internal static class UsingDirectiveSyntaxExtensions
11+
{
12+
private static readonly Func<UsingDirectiveSyntax, SyntaxToken> GlobalKeywordAccessor;
13+
private static readonly Func<UsingDirectiveSyntax, SyntaxToken> UnsafeKeywordAccessor;
14+
private static readonly Func<UsingDirectiveSyntax, TypeSyntax> NamespaceOrTypeAccessor;
15+
private static readonly Func<UsingDirectiveSyntax, SyntaxToken, UsingDirectiveSyntax> WithGlobalKeywordAccessor;
16+
private static readonly Func<UsingDirectiveSyntax, SyntaxToken, UsingDirectiveSyntax> WithUnsafeKeywordAccessor;
17+
private static readonly Func<UsingDirectiveSyntax, TypeSyntax, UsingDirectiveSyntax> WithNamespaceOrTypeAccessor;
18+
19+
static UsingDirectiveSyntaxExtensions()
20+
{
21+
GlobalKeywordAccessor = LightupHelpers.CreateSyntaxPropertyAccessor<UsingDirectiveSyntax, SyntaxToken>(typeof(UsingDirectiveSyntax), nameof(GlobalKeyword));
22+
UnsafeKeywordAccessor = LightupHelpers.CreateSyntaxPropertyAccessor<UsingDirectiveSyntax, SyntaxToken>(typeof(UsingDirectiveSyntax), nameof(UnsafeKeyword));
23+
NamespaceOrTypeAccessor = LightupHelpers.CreateSyntaxPropertyAccessor<UsingDirectiveSyntax, TypeSyntax>(typeof(UsingDirectiveSyntax), nameof(NamespaceOrType));
24+
WithGlobalKeywordAccessor = LightupHelpers.CreateSyntaxWithPropertyAccessor<UsingDirectiveSyntax, SyntaxToken>(typeof(UsingDirectiveSyntax), nameof(GlobalKeyword));
25+
WithUnsafeKeywordAccessor = LightupHelpers.CreateSyntaxWithPropertyAccessor<UsingDirectiveSyntax, SyntaxToken>(typeof(UsingDirectiveSyntax), nameof(UnsafeKeyword));
26+
WithNamespaceOrTypeAccessor = LightupHelpers.CreateSyntaxWithPropertyAccessor<UsingDirectiveSyntax, TypeSyntax>(typeof(UsingDirectiveSyntax), nameof(NamespaceOrType));
27+
}
28+
29+
public static SyntaxToken GlobalKeyword(this UsingDirectiveSyntax syntax)
30+
{
31+
return GlobalKeywordAccessor(syntax);
32+
}
33+
34+
public static SyntaxToken UnsafeKeyword(this UsingDirectiveSyntax syntax)
35+
{
36+
return UnsafeKeywordAccessor(syntax);
37+
}
38+
39+
public static TypeSyntax NamespaceOrType(this UsingDirectiveSyntax syntax)
40+
{
41+
return NamespaceOrTypeAccessor(syntax);
42+
}
43+
44+
public static UsingDirectiveSyntax WithGlobalKeyword(this UsingDirectiveSyntax syntax, SyntaxToken globalKeyword)
45+
{
46+
return WithGlobalKeywordAccessor(syntax, globalKeyword);
47+
}
48+
49+
public static UsingDirectiveSyntax WithUnsafeKeyword(this UsingDirectiveSyntax syntax, SyntaxToken unsafeKeyword)
50+
{
51+
return WithUnsafeKeywordAccessor(syntax, unsafeKeyword);
52+
}
53+
54+
public static UsingDirectiveSyntax WithNamespaceOrType(this UsingDirectiveSyntax syntax, TypeSyntax namespaceOrType)
55+
{
56+
return WithNamespaceOrTypeAccessor(syntax, namespaceOrType);
57+
}
58+
}
59+
}

0 commit comments

Comments
 (0)