private void CheckDeclared(LocalSymbol local) { if (!DeclaredLocals.Contains(local)) { Debug.Assert(false, "undeclared local " + local.GetDebuggerDisplay()); } }
public override void VisitIdentifierName(IdentifierNameSyntax node) { if (_ignoredSpans?.HasIntervalThatOverlapsWith(node.FullSpan.Start, node.FullSpan.Length) ?? false) { return; } // Always try to simplify identifiers with an 'Attribute' suffix. // // In other cases, don't bother looking at the right side of A.B or A::B. We will process those in // one of our other top level Visit methods (like VisitQualifiedName). var canTrySimplify = node.Identifier.ValueText.EndsWith("Attribute", StringComparison.Ordinal); if (!canTrySimplify && !node.IsRightSideOfDotOrArrowOrColonColon()) { // The only possible simplifications to an unqualified identifier are replacement with an alias or // replacement with a predefined type. canTrySimplify = CanReplaceIdentifierWithAlias(node.Identifier.ValueText) || CanReplaceIdentifierWithPredefinedType(node.Identifier.ValueText); } if (canTrySimplify && TrySimplify(node)) { // found a match. report it and stop processing. return; } // descend further. DefaultVisit(node); return; // Local functions bool CanReplaceIdentifierWithAlias(string identifier) => _aliasedNames.Contains(identifier);
private static TypeSymbol GetNextDeclaredBase(NamedTypeSymbol type, ConsList <Symbol> basesBeingResolved, CSharpCompilation compilation, ref PooledHashSet <NamedTypeSymbol> visited) { // We shouldn't have visited this type earlier. Debug.Assert(visited == null || !visited.Contains(type.OriginalDefinition)); if (basesBeingResolved != null && basesBeingResolved.ContainsReference(type.OriginalDefinition)) { return(null); } if (type.SpecialType == SpecialType.System_Object) { type.SetKnownToHaveNoDeclaredBaseCycles(); return(null); } var nextType = type.GetDeclaredBaseType(basesBeingResolved); // types with no declared bases inherit object's members if ((object)nextType == null) { SetKnownToHaveNoDeclaredBaseCycles(ref visited); return(GetDefaultBaseOrNull(type, compilation)); } var origType = type.OriginalDefinition; if (nextType.KnownToHaveNoDeclaredBaseCycles) { origType.SetKnownToHaveNoDeclaredBaseCycles(); SetKnownToHaveNoDeclaredBaseCycles(ref visited); } else { // start cycle tracking visited = visited ?? PooledHashSet <NamedTypeSymbol> .GetInstance(); visited.Add(origType); if (visited.Contains(nextType.OriginalDefinition)) { return(GetDefaultBaseOrNull(type, compilation)); } } return(nextType); }
internal static void AddNs(UsingDirectiveSyntax usingDirective, NamespaceOrTypeSymbol ns, ArrayBuilder <NamespaceOrTypeAndUsingDirective> usings, PooledHashSet <NamespaceOrTypeSymbol> uniqueUsings) { if (!uniqueUsings.Contains(ns)) { uniqueUsings.Add(ns); usings.Add(new NamespaceOrTypeAndUsingDirective(ns, usingDirective)); } }
protected override ImmutableArray <PendingBranch> Scan(ref bool badRegion) { this.Diagnostics.Clear(); // clear reported diagnostics var result = base.Scan(ref badRegion); foreach (var label in _labelsDefined.Keys) { if (!_labelsUsed.Contains(label)) { Diagnostics.Add(ErrorCode.WRN_UnreferencedLabel, label.Locations[0]); } } return(result); }
private void StopTrackingDataForEntity(AnalysisEntity analysisEntity, TAnalysisData analysisData, PooledHashSet <AnalysisEntity> allEntities) { if (!allEntities.Contains(analysisEntity)) { return; } // Stop tracking entity that is now out of scope. StopTrackingEntity(analysisEntity, analysisData); // Additionally, stop tracking all the child entities if the entity type has value copy semantics. if (analysisEntity.Type.HasValueCopySemantics()) { foreach (var childEntity in GetChildAnalysisEntities(analysisEntity, allEntities)) { StopTrackingEntity(childEntity, analysisData); } } }
/// <summary> /// True if the method signature can be rewritten to contain ref/out parameters. /// </summary> public bool CanTakeRefParameters(MethodSymbol closure) => !(closure.IsAsync || closure.IsIterator // We can't rewrite delegate signatures || MethodsConvertedToDelegates.Contains(closure));
internal void ValidateParameterNameConflicts( ImmutableArray <TypeParameterSymbol> typeParameters, ImmutableArray <ParameterSymbol> parameters, bool allowShadowingNames, DiagnosticBag diagnostics) { PooledHashSet <string>?tpNames = null; if (!typeParameters.IsDefaultOrEmpty) { tpNames = PooledHashSet <string> .GetInstance(); foreach (var tp in typeParameters) { var name = tp.Name; if (string.IsNullOrEmpty(name)) { continue; } if (!tpNames.Add(name)) { // Type parameter declaration name conflicts are detected elsewhere } else if (!allowShadowingNames) { ValidateDeclarationNameConflictsInScope(tp, diagnostics); } } } PooledHashSet <string>?pNames = null; if (!parameters.IsDefaultOrEmpty) { pNames = PooledHashSet <string> .GetInstance(); foreach (var p in parameters) { var name = p.Name; if (string.IsNullOrEmpty(name)) { continue; } if (tpNames != null && tpNames.Contains(name)) { // CS0412: 'X': a parameter or local variable cannot have the same name as a method type parameter diagnostics.Add(ErrorCode.ERR_LocalSameNameAsTypeParam, GetLocation(p), name); } if (!pNames.Add(name)) { // The parameter name '{0}' is a duplicate diagnostics.Add(ErrorCode.ERR_DuplicateParamName, GetLocation(p), name); } else if (!allowShadowingNames) { ValidateDeclarationNameConflictsInScope(p, diagnostics); } } } tpNames?.Free(); pNames?.Free(); }
private void OnSymbolEnd(SymbolAnalysisContext symbolEndContext, bool hasInvalidOrDynamicOperation) { // We bail out reporting diagnostics for named types if it contains following kind of operations: // 1. Invalid operations, i.e. erroneous code: // We do so to ensure that we don't report false positives during editing scenarios in the IDE, where the user // is still editing code and fixing unresolved references to symbols, such as overload resolution errors. // 2. Dynamic operations, where we do not know the exact member being referenced at compile time. if (hasInvalidOrDynamicOperation) { return; } if (symbolEndContext.Symbol.GetAttributes().Any(a => a.AttributeClass == _structLayoutAttributeType)) { // Bail out for types with 'StructLayoutAttribute' as the ordering of the members is critical, // and removal of unused members might break semantics. return; } // Report diagnostics for unused candidate members. var first = true; PooledHashSet <ISymbol> symbolsReferencedInDocComments = null; ArrayBuilder <string> debuggerDisplayAttributeArguments = null; try { var namedType = (INamedTypeSymbol)symbolEndContext.Symbol; foreach (var member in namedType.GetMembers()) { // Check if the underlying member is neither read nor a readable reference to the member is taken. // If so, we flag the member as either unused (never written) or unread (written but not read). if (TryRemove(member, out var valueUsageInfo) && !valueUsageInfo.IsReadFrom()) { Debug.Assert(IsCandidateSymbol(member)); Debug.Assert(!member.IsImplicitlyDeclared); if (first) { // Bail out if there are syntax errors in any of the declarations of the containing type. // Note that we check this only for the first time that we report an unused or unread member for the containing type. if (HasSyntaxErrors(namedType, symbolEndContext.CancellationToken)) { return; } // Compute the set of candidate symbols referenced in all the documentation comments within the named type declarations. // This set is computed once and used for all the iterations of the loop. symbolsReferencedInDocComments = GetCandidateSymbolsReferencedInDocComments(namedType, symbolEndContext.Compilation, symbolEndContext.CancellationToken); // Compute the set of string arguments to DebuggerDisplay attributes applied to any symbol within the named type declaration. // These strings may have an embedded reference to the symbol. // This set is computed once and used for all the iterations of the loop. debuggerDisplayAttributeArguments = GetDebuggerDisplayAttributeArguments(namedType); first = false; } // Simple heuristic for members referenced in DebuggerDisplayAttribute's string argument: // bail out if any of the DebuggerDisplay string arguments contains the member name. // In future, we can consider improving this heuristic to parse the embedded expression // and resolve symbol references. if (debuggerDisplayAttributeArguments.Any(arg => arg.Contains(member.Name))) { continue; } // Report IDE0051 or IDE0052 based on whether the underlying member has any Write/WritableRef/NonReadWriteRef references or not. var rule = !valueUsageInfo.IsWrittenTo() && !valueUsageInfo.IsNameOnly() && !symbolsReferencedInDocComments.Contains(member) ? s_removeUnusedMembersRule : s_removeUnreadMembersRule; // Do not flag write-only properties that are not read. // Write-only properties are assumed to have side effects // visible through other means than a property getter. if (rule == s_removeUnreadMembersRule && member is IPropertySymbol property && property.IsWriteOnly) { continue; } // Most of the members should have a single location, except for partial methods. // We report the diagnostic on the first location of the member. var diagnostic = DiagnosticHelper.CreateWithMessage( rule, member.Locations[0], rule.GetEffectiveSeverity(symbolEndContext.Compilation.Options), additionalLocations: null, properties: null, GetMessage(rule, member)); symbolEndContext.ReportDiagnostic(diagnostic); } } } finally { symbolsReferencedInDocComments?.Free(); debuggerDisplayAttributeArguments?.Free(); } return; }
private static TypeSymbol GetNextDeclaredBase(NamedTypeSymbol type, ConsList<Symbol> basesBeingResolved, CSharpCompilation compilation, ref PooledHashSet<NamedTypeSymbol> visited) { // We shouldn't have visited this type earlier. Debug.Assert(visited == null || !visited.Contains(type.OriginalDefinition)); if (basesBeingResolved != null && basesBeingResolved.ContainsReference(type.OriginalDefinition)) { return null; } if (type.SpecialType == SpecialType.System_Object) { type.SetKnownToHaveNoDeclaredBaseCycles(); return null; } var nextType = type.GetDeclaredBaseType(basesBeingResolved); // types with no declared bases inherit object's members if ((object)nextType == null) { SetKnownToHaveNoDeclaredBaseCycles(ref visited); return GetDefaultBaseOrNull(type, compilation); } var origType = type.OriginalDefinition; if (nextType.KnownToHaveNoDeclaredBaseCycles) { origType.SetKnownToHaveNoDeclaredBaseCycles(); SetKnownToHaveNoDeclaredBaseCycles(ref visited); } else { // start cycle tracking visited = visited ?? PooledHashSet<NamedTypeSymbol>.GetInstance(); visited.Add(origType); if (visited.Contains(nextType.OriginalDefinition)) { return GetDefaultBaseOrNull(type, compilation); } } return nextType; }
private void OnSymbolEnd(SymbolAnalysisContext symbolEndContext, bool hasInvalidOperation) { // We bail out reporting diagnostics for named types which have any invalid operations, i.e. erroneous code. // We do so to ensure that we don't report false positives during editing scenarios in the IDE, where the user // is still editing code and fixing unresolved references to symbols, such as overload resolution errors. if (hasInvalidOperation) { return; } // Report diagnostics for unused candidate members. var first = true; PooledHashSet <ISymbol> symbolsReferencedInDocComments = null; try { var namedType = (INamedTypeSymbol)symbolEndContext.Symbol; foreach (var member in namedType.GetMembers()) { // Check if the underlying member is neither read nor a readable reference to the member is taken. // If so, we flag the member as either unused (never written) or unread (written but not read). if (TryRemove(member, out var valueUsageInfo) && !valueUsageInfo.ContainsReadOrReadableRef()) { Debug.Assert(IsCandidateSymbol(member)); Debug.Assert(!member.IsImplicitlyDeclared); if (first) { // Bail out if there are syntax errors in any of the declarations of the containing type. // Note that we check this only for the first time that we report an unused or unread member for the containing type. if (HasSyntaxErrors(namedType, symbolEndContext.CancellationToken)) { return; } // Compute the set of candidate symbols referenced in all the documentation comments within the named type declarations. // This set is computed once and used for all the iterations of the loop. symbolsReferencedInDocComments = GetCandidateSymbolsReferencedInDocComments(namedType, symbolEndContext.Compilation, symbolEndContext.CancellationToken); first = false; } // Report IDE0051 or IDE0052 based on whether the underlying member has any Write/WritableRef/NonReadWriteRef references or not. var rule = !valueUsageInfo.ContainsWriteOrWritableRef() && !valueUsageInfo.ContainsNonReadWriteRef() && !symbolsReferencedInDocComments.Contains(member) ? s_removeUnusedMembersWithFadingRule : s_removeUnreadMembersWithFadingRule; var effectiveSeverity = rule.GetEffectiveSeverity(symbolEndContext.Compilation.Options); // Most of the members should have a single location, except for partial methods. // We report the diagnostic on the first location of the member. var diagnostic = DiagnosticHelper.Create( rule, member.Locations[0], effectiveSeverity, additionalLocations: null, properties: null, member.ContainingType.Name, member.Name); symbolEndContext.ReportDiagnostic(diagnostic); } } } finally { symbolsReferencedInDocComments?.Free(); } return; }
public bool CanTakeRefParameters(MethodSymbol function) => !function.IsAsync && !function.IsIterator // We can't rewrite delegate signatures && !MethodsConvertedToDelegates.Contains(function);