Skip to content

Commit

Permalink
Move to the use site tracking for the purpose of recording used assem…
Browse files Browse the repository at this point in the history
…blies (#40560)

The basic idea is that dependencies should be tracked along with use-site diagnostics and should be collected together with regular diagnostic messages and bubble up to the top. The final consumer of the combined diagnostics and dependencies is responsible for “registering” dependencies with the compilation or ignore them (due to the context, etc.). I believe this is a very robust approach and would require little maintenance going forward, because every time a symbol is used, a use-site diagnostics should be reported. We just need to keep doing that for every new feature and dependencies will be tracked automatically. Also, the code performing binding no longer needs to be context aware (should dependencies be tracked at this moment or not), they should always be tracked and collected along with the regular diagnostics and it is up to the consumer to decide what to do with them, if anything. For example, when SemanticModel performs binding, dependencies are not registered, etc.

Record used assembly references for more scenarios.

- Merged extern aliases
- Embeddable attributes
- Usage of canonical definitions for NoPia embedded types
- nameof of a method
- Deconstruction
- Collection initializers

Related to #37768.
  • Loading branch information
AlekseyTs authored Dec 31, 2019
1 parent 0f03fa5 commit bb8f2df
Show file tree
Hide file tree
Showing 296 changed files with 8,298 additions and 5,384 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public RangeVariableMap RangeVariableMap()
return result;
}

internal RangeVariableSymbol AddRangeVariable(Binder binder, SyntaxToken identifier, DiagnosticBag diagnostics)
internal RangeVariableSymbol AddRangeVariable(Binder binder, SyntaxToken identifier, BindingDiagnosticBag diagnostics)
{
string name = identifier.ValueText;
var result = new RangeVariableSymbol(name, binder.ContainingMemberOrLambda, identifier.GetLocation());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.CSharp
{
internal partial class Binder
{
private delegate BoundBlock LambdaBodyFactory(LambdaSymbol lambdaSymbol, Binder lambdaBodyBinder, DiagnosticBag diagnostics);
private delegate BoundBlock LambdaBodyFactory(LambdaSymbol lambdaSymbol, Binder lambdaBodyBinder, BindingDiagnosticBag diagnostics);

private class QueryUnboundLambdaState : UnboundLambdaState
{
Expand Down Expand Up @@ -38,7 +38,7 @@ public QueryUnboundLambdaState(Binder binder, RangeVariableMap rangeVariableMap,
public override Location ParameterLocation(int index) { return _parameters[index].Locations[0]; }
public override TypeWithAnnotations ParameterTypeWithAnnotations(int index) { throw new ArgumentException(); } // implicitly typed

public override void GenerateAnonymousFunctionConversionError(DiagnosticBag diagnostics, TypeSymbol targetType)
public override void GenerateAnonymousFunctionConversionError(BindingDiagnosticBag diagnostics, TypeSymbol targetType)
{
// TODO: improved diagnostics for query expressions
base.GenerateAnonymousFunctionConversionError(diagnostics, targetType);
Expand All @@ -49,7 +49,7 @@ public override Binder ParameterBinder(LambdaSymbol lambdaSymbol, Binder binder)
return new WithQueryLambdaParametersBinder(lambdaSymbol, _rangeVariableMap, binder);
}

protected override BoundBlock BindLambdaBody(LambdaSymbol lambdaSymbol, Binder lambdaBodyBinder, DiagnosticBag diagnostics)
protected override BoundBlock BindLambdaBody(LambdaSymbol lambdaSymbol, Binder lambdaBodyBinder, BindingDiagnosticBag diagnostics)
{
return _bodyFactory(lambdaSymbol, lambdaBodyBinder, diagnostics);
}
Expand Down
104 changes: 61 additions & 43 deletions src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public WithQueryLambdaParametersBinder(LambdaSymbol lambdaSymbol, RangeVariableM
}
}

protected override BoundExpression BindRangeVariable(SimpleNameSyntax node, RangeVariableSymbol qv, DiagnosticBag diagnostics)
protected override BoundExpression BindRangeVariable(SimpleNameSyntax node, RangeVariableSymbol qv, BindingDiagnosticBag diagnostics)
{
Debug.Assert(!qv.IsTransparent);

Expand Down Expand Up @@ -65,7 +65,7 @@ protected override BoundExpression BindRangeVariable(SimpleNameSyntax node, Rang
return base.BindRangeVariable(node, qv, diagnostics);
}

private BoundExpression SelectField(SimpleNameSyntax node, BoundExpression receiver, string name, DiagnosticBag diagnostics)
private BoundExpression SelectField(SimpleNameSyntax node, BoundExpression receiver, string name, BindingDiagnosticBag diagnostics)
{
var receiverType = receiver.Type as NamedTypeSymbol;
if ((object)receiverType == null || !receiverType.IsAnonymousType)
Expand All @@ -88,17 +88,17 @@ private BoundExpression SelectField(SimpleNameSyntax node, BoundExpression recei

LookupResult lookupResult = LookupResult.GetInstance();
LookupOptions options = LookupOptions.MustBeInstance;
HashSet<DiagnosticInfo> useSiteDiagnostics = null;
LookupMembersWithFallback(lookupResult, receiver.Type, name, 0, ref useSiteDiagnostics, basesBeingResolved: null, options: options);
diagnostics.Add(node, useSiteDiagnostics);
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = default;
LookupMembersWithFallback(lookupResult, receiver.Type, name, 0, ref useSiteInfo, basesBeingResolved: null, options: options);
diagnostics.Add(node, useSiteInfo);

var result = BindMemberOfType(node, node, name, 0, indexed: false, receiver, default(SeparatedSyntaxList<TypeSyntax>), default(ImmutableArray<TypeWithAnnotations>), lookupResult, BoundMethodGroupFlags.None, diagnostics);
lookupResult.Free();
return result;
}

internal override void LookupSymbolsInSingleBinder(
LookupResult result, string name, int arity, ConsList<TypeSymbol> basesBeingResolved, LookupOptions options, Binder originalBinder, bool diagnose, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
LookupResult result, string name, int arity, ConsList<TypeSymbol> basesBeingResolved, LookupOptions options, Binder originalBinder, bool diagnose, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
Debug.Assert(result.IsClear);

Expand All @@ -109,7 +109,7 @@ internal override void LookupSymbolsInSingleBinder(

foreach (var rangeVariable in _parameterMap[name])
{
result.MergeEqual(originalBinder.CheckViability(rangeVariable, arity, options, null, diagnose, ref useSiteDiagnostics));
result.MergeEqual(originalBinder.CheckViability(rangeVariable, arity, options, null, diagnose, ref useSiteInfo));
}
}

Expand Down
70 changes: 50 additions & 20 deletions src/Compilers/CSharp/Portable/Binder/Binder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -457,52 +457,52 @@ internal OverloadResolution OverloadResolution
}
}

internal static void Error(DiagnosticBag diagnostics, DiagnosticInfo info, SyntaxNode syntax)
internal static void Error(BindingDiagnosticBag diagnostics, DiagnosticInfo info, SyntaxNode syntax)
{
diagnostics.Add(new CSDiagnostic(info, syntax.Location));
}

internal static void Error(DiagnosticBag diagnostics, DiagnosticInfo info, Location location)
internal static void Error(BindingDiagnosticBag diagnostics, DiagnosticInfo info, Location location)
{
diagnostics.Add(new CSDiagnostic(info, location));
}

internal static void Error(DiagnosticBag diagnostics, ErrorCode code, CSharpSyntaxNode syntax)
internal static void Error(BindingDiagnosticBag diagnostics, ErrorCode code, CSharpSyntaxNode syntax)
{
diagnostics.Add(new CSDiagnostic(new CSDiagnosticInfo(code), syntax.Location));
}

internal static void Error(DiagnosticBag diagnostics, ErrorCode code, CSharpSyntaxNode syntax, params object[] args)
internal static void Error(BindingDiagnosticBag diagnostics, ErrorCode code, CSharpSyntaxNode syntax, params object[] args)
{
diagnostics.Add(new CSDiagnostic(new CSDiagnosticInfo(code, args), syntax.Location));
}

internal static void Error(DiagnosticBag diagnostics, ErrorCode code, SyntaxToken token)
internal static void Error(BindingDiagnosticBag diagnostics, ErrorCode code, SyntaxToken token)
{
diagnostics.Add(new CSDiagnostic(new CSDiagnosticInfo(code), token.GetLocation()));
}

internal static void Error(DiagnosticBag diagnostics, ErrorCode code, SyntaxToken token, params object[] args)
internal static void Error(BindingDiagnosticBag diagnostics, ErrorCode code, SyntaxToken token, params object[] args)
{
diagnostics.Add(new CSDiagnostic(new CSDiagnosticInfo(code, args), token.GetLocation()));
}

internal static void Error(DiagnosticBag diagnostics, ErrorCode code, SyntaxNodeOrToken syntax)
internal static void Error(BindingDiagnosticBag diagnostics, ErrorCode code, SyntaxNodeOrToken syntax)
{
Error(diagnostics, code, syntax.GetLocation());
}

internal static void Error(DiagnosticBag diagnostics, ErrorCode code, SyntaxNodeOrToken syntax, params object[] args)
internal static void Error(BindingDiagnosticBag diagnostics, ErrorCode code, SyntaxNodeOrToken syntax, params object[] args)
{
Error(diagnostics, code, syntax.GetLocation(), args);
}

internal static void Error(DiagnosticBag diagnostics, ErrorCode code, Location location)
internal static void Error(BindingDiagnosticBag diagnostics, ErrorCode code, Location location)
{
diagnostics.Add(new CSDiagnostic(new CSDiagnosticInfo(code), location));
}

internal static void Error(DiagnosticBag diagnostics, ErrorCode code, Location location, params object[] args)
internal static void Error(BindingDiagnosticBag diagnostics, ErrorCode code, Location location, params object[] args)
{
diagnostics.Add(new CSDiagnostic(new CSDiagnosticInfo(code, args), location));
}
Expand All @@ -526,7 +526,15 @@ internal void ReportDiagnosticsIfObsolete(DiagnosticBag diagnostics, Symbol symb
}
}

internal void ReportDiagnosticsIfObsolete(DiagnosticBag diagnostics, Conversion conversion, SyntaxNodeOrToken node, bool hasBaseReceiver)
internal void ReportDiagnosticsIfObsolete(BindingDiagnosticBag diagnostics, Symbol symbol, SyntaxNodeOrToken node, bool hasBaseReceiver)
{
if (diagnostics.DiagnosticBag is object)
{
ReportDiagnosticsIfObsolete(diagnostics.DiagnosticBag, symbol, node, hasBaseReceiver);
}
}

internal void ReportDiagnosticsIfObsolete(BindingDiagnosticBag diagnostics, Conversion conversion, SyntaxNodeOrToken node, bool hasBaseReceiver)
{
if (conversion.IsValid && (object)conversion.Method != null)
{
Expand Down Expand Up @@ -600,6 +608,21 @@ internal static void ReportDiagnosticsIfObsolete(
}
}

internal static void ReportDiagnosticsIfObsolete(
BindingDiagnosticBag diagnostics,
Symbol symbol,
SyntaxNodeOrToken node,
bool hasBaseReceiver,
Symbol containingMember,
NamedTypeSymbol containingType,
BinderFlags location)
{
if (diagnostics.DiagnosticBag is object)
{
ReportDiagnosticsIfObsolete(diagnostics.DiagnosticBag, symbol, node, hasBaseReceiver, containingMember, containingType, location);
}
}

internal static ObsoleteDiagnosticKind ReportDiagnosticsIfObsoleteInternal(DiagnosticBag diagnostics, Symbol symbol, SyntaxNodeOrToken node, Symbol containingMember, BinderFlags location)
{
Debug.Assert(diagnostics != null);
Expand All @@ -626,29 +649,37 @@ internal static ObsoleteDiagnosticKind ReportDiagnosticsIfObsoleteInternal(Diagn
return kind;
}

internal static void ReportDiagnosticsIfObsoleteInternal(BindingDiagnosticBag diagnostics, Symbol symbol, SyntaxNodeOrToken node, Symbol containingMember, BinderFlags location)
{
if (diagnostics.DiagnosticBag is object)
{
ReportDiagnosticsIfObsoleteInternal(diagnostics.DiagnosticBag, symbol, node, containingMember, location);
}
}

internal static bool IsSymbolAccessibleConditional(
Symbol symbol,
AssemblySymbol within,
ref HashSet<DiagnosticInfo> useSiteDiagnostics)
ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
return AccessCheck.IsSymbolAccessible(symbol, within, ref useSiteDiagnostics);
return AccessCheck.IsSymbolAccessible(symbol, within, ref useSiteInfo);
}

internal bool IsSymbolAccessibleConditional(
Symbol symbol,
NamedTypeSymbol within,
ref HashSet<DiagnosticInfo> useSiteDiagnostics,
ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo,
TypeSymbol throughTypeOpt = null)
{
return this.Flags.Includes(BinderFlags.IgnoreAccessibility) || AccessCheck.IsSymbolAccessible(symbol, within, ref useSiteDiagnostics, throughTypeOpt);
return this.Flags.Includes(BinderFlags.IgnoreAccessibility) || AccessCheck.IsSymbolAccessible(symbol, within, ref useSiteInfo, throughTypeOpt);
}

internal bool IsSymbolAccessibleConditional(
Symbol symbol,
NamedTypeSymbol within,
TypeSymbol throughTypeOpt,
out bool failedThroughTypeCheck,
ref HashSet<DiagnosticInfo> useSiteDiagnostics,
ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo,
ConsList<TypeSymbol> basesBeingResolved = null)
{
if (this.Flags.Includes(BinderFlags.IgnoreAccessibility))
Expand All @@ -657,7 +688,7 @@ internal bool IsSymbolAccessibleConditional(
return true;
}

return AccessCheck.IsSymbolAccessible(symbol, within, throughTypeOpt, out failedThroughTypeCheck, ref useSiteDiagnostics, basesBeingResolved);
return AccessCheck.IsSymbolAccessible(symbol, within, throughTypeOpt, out failedThroughTypeCheck, ref useSiteInfo, basesBeingResolved);
}

/// <summary>
Expand All @@ -666,8 +697,7 @@ internal bool IsSymbolAccessibleConditional(
internal static void ReportUseSiteDiagnosticForSynthesizedAttribute(
CSharpCompilation compilation,
WellKnownMember attributeMember,
bool recordUsage,
DiagnosticBag diagnostics,
BindingDiagnosticBag diagnostics,
Location location = null,
CSharpSyntaxNode syntax = null)
{
Expand All @@ -677,7 +707,7 @@ internal static void ReportUseSiteDiagnosticForSynthesizedAttribute(
// (comes from an unified assembly). When the symbol is not found no error is reported. See test VersionUnification_UseSiteDiagnostics_OptionalAttributes.
bool isOptional = WellKnownMembers.IsSynthesizedAttributeOptional(attributeMember);

GetWellKnownTypeMember(compilation, attributeMember, recordUsage, diagnostics, location, syntax, isOptional);
GetWellKnownTypeMember(compilation, attributeMember, diagnostics, location, syntax, isOptional);
}

#if DEBUG
Expand Down
13 changes: 4 additions & 9 deletions src/Compilers/CSharp/Portable/Binder/BinderFlags.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,31 +87,26 @@ internal enum BinderFlags : uint
/// </summary>
IgnoreCorLibraryDuplicatedTypes = 1 << 26,

/// <summary>
/// Set for a binder used to bind a using directive target
/// </summary>
InUsing = 1 << 27,

/// <summary>
/// When binding imports in scripts/submissions, using aliases (other than from the current submission)
/// are considered but other imports are not.
/// </summary>
InScriptUsing = 1 << 28,
InScriptUsing = 1 << 27,

/// <summary>
/// In a file that has been included in the compilation via #load.
/// </summary>
InLoadedSyntaxTree = 1 << 29,
InLoadedSyntaxTree = 1 << 28,

/// <summary>
/// This is a <see cref="ContextualAttributeBinder"/>, or has <see cref="ContextualAttributeBinder"/> as its parent.
/// </summary>
InContextualAttributeBinder = 1 << 30,
InContextualAttributeBinder = 1 << 29,

/// <summary>
/// Are we binding for the purpose of an Expression Evaluator
/// </summary>
InEEMethodBinder = 1U << 31,
InEEMethodBinder = 1U << 30,

// Groups

Expand Down
4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/Binder/Binder_AnonymousTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.CSharp
/// </summary>
internal partial class Binder
{
private BoundExpression BindAnonymousObjectCreation(AnonymousObjectCreationExpressionSyntax node, DiagnosticBag diagnostics)
private BoundExpression BindAnonymousObjectCreation(AnonymousObjectCreationExpressionSyntax node, BindingDiagnosticBag diagnostics)
{
// prepare
var initializers = node.Initializers;
Expand Down Expand Up @@ -201,7 +201,7 @@ private bool IsAnonymousTypesAllowed()
/// Returns the type to be used as a field type; generates errors in case the type is not
/// supported for anonymous type fields.
/// </summary>
private TypeSymbol GetAnonymousTypeFieldType(BoundExpression expression, CSharpSyntaxNode errorSyntax, DiagnosticBag diagnostics, ref bool hasError)
private TypeSymbol GetAnonymousTypeFieldType(BoundExpression expression, CSharpSyntaxNode errorSyntax, BindingDiagnosticBag diagnostics, ref bool hasError)
{
object errorArg = null;
TypeSymbol expressionType = expression.Type;
Expand Down
Loading

0 comments on commit bb8f2df

Please sign in to comment.