public static void AddRange <T>(this PooledHashSet <T> builder, IEnumerable <T> set2) { foreach (var item in set2) { builder.Add(item); } }
public override BoundNode VisitGotoStatement(BoundGotoStatement node) { _labelsUsed.Add(node.Label); // check for illegal jumps across using declarations var sourceLocation = node.Syntax.Location; var sourceStart = sourceLocation.SourceSpan.Start; var targetStart = node.Label.Locations[0].SourceSpan.Start; foreach (var usingDecl in _usingDeclarations) { var usingStart = usingDecl.symbol.Locations[0].SourceSpan.Start; if (sourceStart < usingStart && targetStart > usingStart) { // No forward jumps Diagnostics.Add(ErrorCode.ERR_GoToForwardJumpOverUsingVar, sourceLocation); break; } else if (sourceStart > usingStart && targetStart < usingStart) { // Backwards jump, so we must have already seen the label Debug.Assert(_labelsDefined.ContainsKey(node.Label)); // Error if label and using are part of the same block if (_labelsDefined[node.Label] == usingDecl.block) { Diagnostics.Add(ErrorCode.ERR_GoToBackwardJumpOverUsingVar, sourceLocation); break; } } } return(base.VisitGotoStatement(node)); }
static void getBaseInterfaces(TypeSymbol derived, ArrayBuilder <NamedTypeSymbol> baseInterfaces, PooledHashSet <NamedTypeSymbol> interfacesLookedAt, ConsList <TypeSymbol> basesBeingResolved) { if (basesBeingResolved != null && basesBeingResolved.ContainsReference(derived)) { return; } ImmutableArray <NamedTypeSymbol> declaredInterfaces; switch (derived) { case TypeParameterSymbol typeParameter: declaredInterfaces = typeParameter.AllEffectiveInterfacesNoUseSiteDiagnostics; break; case NamedTypeSymbol namedType: declaredInterfaces = namedType.GetDeclaredInterfaces(basesBeingResolved); break; default: declaredInterfaces = derived.InterfacesNoUseSiteDiagnostics(basesBeingResolved); break; } foreach (var @interface in declaredInterfaces) { NamedTypeSymbol definition = @interface.OriginalDefinition; if (interfacesLookedAt.Add(definition)) { baseInterfaces.Add(definition); } } }
public SyntaxToken GenerateUniqueNameAtSpanStart(SyntaxNode node) { var nameToken = _semanticFacts.GenerateUniqueName(_semanticModel, node, _memberDeclaration, "unused", _usedNames, _cancellationToken); _usedNames.Add(nameToken.ValueText); return(nameToken); }
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)); } }
static void AddIfHasKnownInstanceLocation(AnalysisEntity entity, PooledHashSet <AnalysisEntity> builder) { // Only add entity to address shared entities if they have known instance location. if (!entity.HasUnknownInstanceLocation) { builder.Add(entity); } }
private void AddAll(ImmutableArray <LocalSymbol> locals) { foreach (var local in locals) { if (!DeclaredLocals.Add(local)) { Debug.Assert(false, "duplicate local " + local.GetDebuggerDisplay()); } } }
static void AddNamesForTypeWorker(ITypeSymbol receiverTypeSymbol, PooledHashSet <string> builder) { if (receiverTypeSymbol is ITypeParameterSymbol typeParameter) { foreach (var constraintType in typeParameter.ConstraintTypes) { AddNamesForTypeWorker(constraintType, builder); } } else { builder.Add(GetReceiverTypeName(receiverTypeSymbol)); builder.AddRange(receiverTypeSymbol.GetBaseTypes().Select(t => t.MetadataName)); builder.AddRange(receiverTypeSymbol.GetAllInterfacesIncludingThis().Select(t => t.MetadataName)); // interface doesn't inherit from object, but is implicitly convertible to object type. if (receiverTypeSymbol.IsInterfaceType()) { builder.Add(nameof(Object)); } } }
public void AddPredecessor(BasicBlockBuilder predecessor) { Debug.Assert(predecessor != null); if (_predecessors != null) { Debug.Assert(_predecessor1 == null); Debug.Assert(_predecessor2 == null); _predecessors.Add(predecessor); } else if (_predecessor1 == predecessor) { return; } else if (_predecessor2 == predecessor) { return; } else if (_predecessor1 == null) { _predecessor1 = predecessor; } else if (_predecessor2 == null) { _predecessor2 = predecessor; } else { _predecessors = PooledHashSet <BasicBlockBuilder> .GetInstance(); _predecessors.Add(_predecessor1); _predecessors.Add(_predecessor2); _predecessors.Add(predecessor); Debug.Assert(_predecessors.Count == 3); _predecessor1 = null; _predecessor2 = null; } }
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); }
public void RemoveDuplicates() { PooledHashSet <T> set = PooledHashSet <T> .GetInstance(); int j = 0; for (int i = 0; i < Count; i++) { if (set.Add(this[i])) { this[j] = this[i]; j++; } } Clip(j); set.Free(); }
public ImmutableArray <S> SelectDistinct <S>(Func <T, S> selector) { ArrayBuilder <S> result = ArrayBuilder <S> .GetInstance(Count); PooledHashSet <S> set = PooledHashSet <S> .GetInstance(); foreach (T item in this) { S selected = selector(item); if (set.Add(selected)) { result.Add(selected); } } set.Free(); return(result.ToImmutableAndFree()); }
/// <summary> /// Determines if the given method is a potential tainted data source and get the related argument check methods. /// </summary> /// <param name="sourceSymbolMap"></param> /// <param name="method"></param> /// <param name="evaluateWithPointsToAnalysis"></param> /// <param name="evaluateWithValueContentAnalysis"></param> /// <returns></returns> public static bool IsSourceMethod( this TaintedDataSymbolMap <SourceInfo> sourceSymbolMap, IMethodSymbol method, out PooledHashSet <IsInvocationTaintedWithPointsToAnalysis> evaluateWithPointsToAnalysis, out PooledHashSet <IsInvocationTaintedWithValueContentAnalysis> evaluateWithValueContentAnalysis) { evaluateWithPointsToAnalysis = null; evaluateWithValueContentAnalysis = null; foreach (SourceInfo sourceInfo in sourceSymbolMap.GetInfosForType(method.ContainingType)) { if (sourceInfo.TaintedMethodsNeedPointsToAnalysis.TryGetValue(method.MetadataName, out IsInvocationTaintedWithPointsToAnalysis pointsToAnalysisMethod)) { if (evaluateWithPointsToAnalysis == null) { evaluateWithPointsToAnalysis = PooledHashSet <IsInvocationTaintedWithPointsToAnalysis> .GetInstance(); } evaluateWithPointsToAnalysis.Add(pointsToAnalysisMethod); } else if (sourceInfo.TaintedMethodsNeedsValueContentAnalysis.TryGetValue(method.MetadataName, out IsInvocationTaintedWithValueContentAnalysis valueContentAnalysisMethod)) { if (evaluateWithValueContentAnalysis == null) { evaluateWithValueContentAnalysis = PooledHashSet <IsInvocationTaintedWithValueContentAnalysis> .GetInstance(); } evaluateWithValueContentAnalysis.Add(valueContentAnalysisMethod); } } if (evaluateWithPointsToAnalysis == null && evaluateWithValueContentAnalysis == null) { return(false); } else { return(true); } }
public override BoundNode VisitGotoStatement(BoundGotoStatement node) { _labelsUsed.Add(node.Label); return(base.VisitGotoStatement(node)); }
protected override void VisitLabel(BoundLabeledStatement node) { _labelsDefined.Add(node.Label); base.VisitLabel(node); }
public sealed override void Initialize(AnalysisContext context) { ImmutableHashSet <string> cachedDeserializationMethodNames = this.DeserializationMethodNames; Debug.Assert(!String.IsNullOrWhiteSpace(this.DeserializerTypeMetadataName)); Debug.Assert(!String.IsNullOrWhiteSpace(this.SerializationBinderPropertyMetadataName)); Debug.Assert(cachedDeserializationMethodNames != null); Debug.Assert(!cachedDeserializationMethodNames.IsEmpty); Debug.Assert(this.BinderDefinitelyNotSetDescriptor != null); Debug.Assert(this.BinderMaybeNotSetDescriptor != null); context.EnableConcurrentExecution(); // Security analyzer - analyze and report diagnostics on generated code. context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); // For PropertySetAnalysis dataflow analysis. PropertyMapperCollection propertyMappers = new PropertyMapperCollection( new PropertyMapper( this.SerializationBinderPropertyMetadataName, (NullAbstractValue nullAbstractValue) => { // A null SerializationBinder is what we want to flag as hazardous. switch (nullAbstractValue) { case NullAbstractValue.Null: return(PropertySetAbstractValueKind.Flagged); case NullAbstractValue.NotNull: return(PropertySetAbstractValueKind.Unflagged); default: return(PropertySetAbstractValueKind.MaybeFlagged); } })); HazardousUsageEvaluatorCollection hazardousUsageEvaluators = new HazardousUsageEvaluatorCollection( cachedDeserializationMethodNames.Select( methodName => new HazardousUsageEvaluator(methodName, DoNotUseInsecureDeserializerWithoutBinderBase.HazardousIfNull))); context.RegisterCompilationStartAction( (CompilationStartAnalysisContext compilationStartAnalysisContext) => { WellKnownTypeProvider wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(compilationStartAnalysisContext.Compilation); if (!wellKnownTypeProvider.TryGetTypeByMetadataName( this.DeserializerTypeMetadataName, out INamedTypeSymbol deserializerTypeSymbol)) { return; } compilationStartAnalysisContext.RegisterOperationBlockStartAction( (OperationBlockStartAnalysisContext operationBlockStartAnalysisContext) => { PooledHashSet <IOperation> rootOperationsNeedingAnalysis = PooledHashSet <IOperation> .GetInstance(); operationBlockStartAnalysisContext.RegisterOperationAction( (OperationAnalysisContext operationAnalysisContext) => { IInvocationOperation invocationOperation = (IInvocationOperation)operationAnalysisContext.Operation; if (invocationOperation.Instance?.Type == deserializerTypeSymbol && cachedDeserializationMethodNames.Contains(invocationOperation.TargetMethod.Name)) { lock (rootOperationsNeedingAnalysis) { rootOperationsNeedingAnalysis.Add(operationAnalysisContext.Operation.GetRoot()); } } }, OperationKind.Invocation); operationBlockStartAnalysisContext.RegisterOperationAction( (OperationAnalysisContext operationAnalysisContext) => { IMethodReferenceOperation methodReferenceOperation = (IMethodReferenceOperation)operationAnalysisContext.Operation; if (methodReferenceOperation.Instance?.Type == deserializerTypeSymbol && cachedDeserializationMethodNames.Contains( methodReferenceOperation.Method.MetadataName)) { lock (rootOperationsNeedingAnalysis) { rootOperationsNeedingAnalysis.Add(operationAnalysisContext.Operation.GetRoot()); } } }, OperationKind.MethodReference); operationBlockStartAnalysisContext.RegisterOperationBlockEndAction( (OperationBlockAnalysisContext operationBlockAnalysisContext) => { PooledDictionary <(Location Location, IMethodSymbol Method), HazardousUsageEvaluationResult> allResults = null; try { lock (rootOperationsNeedingAnalysis) { if (!rootOperationsNeedingAnalysis.Any()) { return; } // Only instantiated if there are any results to report. List <ControlFlowGraph> cfgs = new List <ControlFlowGraph>(); var interproceduralAnalysisConfig = InterproceduralAnalysisConfiguration.Create( operationBlockAnalysisContext.Options, SupportedDiagnostics, defaultInterproceduralAnalysisKind: InterproceduralAnalysisKind.None, cancellationToken: operationBlockAnalysisContext.CancellationToken, defaultMaxInterproceduralMethodCallChain: 1); // By default, we only want to track method calls one level down. foreach (IOperation rootOperation in rootOperationsNeedingAnalysis) { ImmutableDictionary <(Location Location, IMethodSymbol Method), HazardousUsageEvaluationResult> dfaResult = PropertySetAnalysis.GetOrComputeHazardousUsages( rootOperation.GetEnclosingControlFlowGraph(), operationBlockAnalysisContext.Compilation, operationBlockAnalysisContext.OwningSymbol, this.DeserializerTypeMetadataName, DoNotUseInsecureDeserializerWithoutBinderBase.ConstructorMapper, propertyMappers, hazardousUsageEvaluators, interproceduralAnalysisConfig); if (dfaResult.IsEmpty) { continue; } if (allResults == null) { allResults = PooledDictionary <(Location Location, IMethodSymbol Method), HazardousUsageEvaluationResult> .GetInstance(); } foreach (KeyValuePair <(Location Location, IMethodSymbol Method), HazardousUsageEvaluationResult> kvp in dfaResult) { allResults.Add(kvp.Key, kvp.Value); } } } if (allResults == null) { return; } foreach (KeyValuePair <(Location Location, IMethodSymbol Method), HazardousUsageEvaluationResult> kvp in allResults) { DiagnosticDescriptor descriptor; switch (kvp.Value) { case HazardousUsageEvaluationResult.Flagged: descriptor = this.BinderDefinitelyNotSetDescriptor; break; case HazardousUsageEvaluationResult.MaybeFlagged: descriptor = this.BinderMaybeNotSetDescriptor; break; default: Debug.Fail($"Unhandled result value {kvp.Value}"); continue; } operationBlockAnalysisContext.ReportDiagnostic( Diagnostic.Create( descriptor, kvp.Key.Location, kvp.Key.Method.ToDisplayString( SymbolDisplayFormat.MinimallyQualifiedFormat))); } } finally { rootOperationsNeedingAnalysis.Free(); allResults?.Free(); } }); }); }); }
private void DefineNamespaceScopes(IMethodBody methodBody) { var module = Module; bool isVisualBasic = module.GenerateVisualBasicStylePdb; IMethodDefinition method = methodBody.MethodDefinition; var namespaceScopes = methodBody.ImportScope; // NOTE: All extern aliases are stored on the outermost namespace scope. PooledHashSet <string> lazyDeclaredExternAliases = null; if (!isVisualBasic) { foreach (var import in GetLastScope(namespaceScopes).GetUsedNamespaces(Context)) { if (import.TargetNamespaceOpt == null && import.TargetTypeOpt == null) { Debug.Assert(import.AliasOpt != null); Debug.Assert(import.TargetAssemblyOpt == null); if (lazyDeclaredExternAliases == null) { lazyDeclaredExternAliases = PooledHashSet <string> .GetInstance(); } lazyDeclaredExternAliases.Add(import.AliasOpt); } } } // file and namespace level for (IImportScope scope = namespaceScopes; scope != null; scope = scope.Parent) { foreach (UsedNamespaceOrType import in scope.GetUsedNamespaces(Context)) { var importString = TryEncodeImport(import, lazyDeclaredExternAliases, isProjectLevel: false); if (importString != null) { UsingNamespace(importString, method); } } } lazyDeclaredExternAliases?.Free(); // project level if (isVisualBasic) { string defaultNamespace = module.DefaultNamespace; if (defaultNamespace != null) { // VB marks the default/root namespace with an asterisk UsingNamespace("*" + defaultNamespace, module); } foreach (string assemblyName in module.LinkedAssembliesDebugInfo) { UsingNamespace("&" + assemblyName, module); } foreach (UsedNamespaceOrType import in module.GetImports(Context)) { var importString = TryEncodeImport(import, null, isProjectLevel: true); if (importString != null) { UsingNamespace(importString, method); } } // VB current namespace -- VB appends the namespace of the container without prefixes UsingNamespace(GetOrCreateSerializedNamespaceName(method.ContainingNamespace), method); } }
private static TBlockAnalysisData RunCore( ImmutableArray <BasicBlock> blocks, DataFlowAnalyzer <TBlockAnalysisData> analyzer, int firstBlockOrdinal, int lastBlockOrdinal, TBlockAnalysisData initialAnalysisData, ArrayBuilder <BasicBlock> unreachableBlocksToVisit, SortedSet <int> outOfRangeBlocksToVisit, PooledDictionary <ControlFlowRegion, bool> continueDispatchAfterFinally, PooledHashSet <ControlFlowRegion> dispatchedExceptionsFromRegions, CancellationToken cancellationToken) { var toVisit = new SortedSet <int>(); var firstBlock = blocks[firstBlockOrdinal]; analyzer.SetCurrentAnalysisData(firstBlock, initialAnalysisData, cancellationToken); toVisit.Add(firstBlock.Ordinal); var processedBlocks = PooledHashSet <BasicBlock> .GetInstance(); TBlockAnalysisData resultAnalysisData = default; do { cancellationToken.ThrowIfCancellationRequested(); BasicBlock current; if (toVisit.Count > 0) { var min = toVisit.Min; toVisit.Remove(min); current = blocks[min]; } else { int index; current = null; for (index = 0; index < unreachableBlocksToVisit.Count; index++) { var unreachableBlock = unreachableBlocksToVisit[index]; if (unreachableBlock.Ordinal >= firstBlockOrdinal && unreachableBlock.Ordinal <= lastBlockOrdinal) { current = unreachableBlock; break; } } if (current == null) { continue; } unreachableBlocksToVisit.RemoveAt(index); if (processedBlocks.Contains(current)) { // Already processed from a branch from another unreachable block. continue; } analyzer.SetCurrentAnalysisData(current, analyzer.GetEmptyAnalysisData(), cancellationToken); } if (current.Ordinal < firstBlockOrdinal || current.Ordinal > lastBlockOrdinal) { outOfRangeBlocksToVisit.Add(current.Ordinal); continue; } if (current.Ordinal == current.EnclosingRegion.FirstBlockOrdinal) { // We are revisiting first block of a region, so we need to again dispatch exceptions from region. dispatchedExceptionsFromRegions.Remove(current.EnclosingRegion); } TBlockAnalysisData fallThroughAnalysisData = analyzer.AnalyzeBlock(current, cancellationToken); bool fallThroughSuccessorIsReachable = true; if (current.ConditionKind != ControlFlowConditionKind.None) { TBlockAnalysisData conditionalSuccessorAnalysisData; (fallThroughAnalysisData, conditionalSuccessorAnalysisData) = analyzer.AnalyzeConditionalBranch(current, fallThroughAnalysisData, cancellationToken); bool conditionalSuccesorIsReachable = true; if (current.BranchValue.ConstantValue.HasValue && current.BranchValue.ConstantValue.Value is bool constant) { if (constant == (current.ConditionKind == ControlFlowConditionKind.WhenTrue)) { fallThroughSuccessorIsReachable = false; } else { conditionalSuccesorIsReachable = false; } } if (conditionalSuccesorIsReachable || analyzer.AnalyzeUnreachableBlocks) { FollowBranch(current, current.ConditionalSuccessor, conditionalSuccessorAnalysisData); } } else { fallThroughAnalysisData = analyzer.AnalyzeNonConditionalBranch(current, fallThroughAnalysisData, cancellationToken); } if (fallThroughSuccessorIsReachable || analyzer.AnalyzeUnreachableBlocks) { ControlFlowBranch branch = current.FallThroughSuccessor; FollowBranch(current, branch, fallThroughAnalysisData); if (current.EnclosingRegion.Kind == ControlFlowRegionKind.Finally && current.Ordinal == lastBlockOrdinal) { continueDispatchAfterFinally[current.EnclosingRegion] = branch.Semantics != ControlFlowBranchSemantics.Throw && branch.Semantics != ControlFlowBranchSemantics.Rethrow && current.FallThroughSuccessor.Semantics == ControlFlowBranchSemantics.StructuredExceptionHandling; } } if (current.Ordinal == lastBlockOrdinal) { resultAnalysisData = fallThroughAnalysisData; } // We are using very simple approach: // If try block is reachable, we should dispatch an exception from it, even if it is empty. // To simplify implementation, we dispatch exception from every reachable basic block and rely // on dispatchedExceptionsFromRegions cache to avoid doing duplicate work. DispatchException(current.EnclosingRegion); processedBlocks.Add(current); }while (toVisit.Count != 0 || unreachableBlocksToVisit.Count != 0); return(resultAnalysisData); // Local functions. void FollowBranch(BasicBlock current, ControlFlowBranch branch, TBlockAnalysisData currentAnalsisData) { if (branch == null) { return; } switch (branch.Semantics) { case ControlFlowBranchSemantics.None: case ControlFlowBranchSemantics.ProgramTermination: case ControlFlowBranchSemantics.StructuredExceptionHandling: case ControlFlowBranchSemantics.Error: Debug.Assert(branch.Destination == null); return; case ControlFlowBranchSemantics.Throw: case ControlFlowBranchSemantics.Rethrow: Debug.Assert(branch.Destination == null); StepThroughFinally(current.EnclosingRegion, destinationOrdinal: lastBlockOrdinal, ref currentAnalsisData); return; case ControlFlowBranchSemantics.Regular: case ControlFlowBranchSemantics.Return: Debug.Assert(branch.Destination != null); if (StepThroughFinally(current.EnclosingRegion, branch.Destination.Ordinal, ref currentAnalsisData)) { var destination = branch.Destination; var currentDestinationData = analyzer.GetCurrentAnalysisData(destination); var mergedAnalysisData = analyzer.Merge(currentDestinationData, currentAnalsisData, cancellationToken); // We need to analyze the destination block if both the following conditions are met: // 1. Either the current block is reachable both destination and current are non-reachable // 2. Either the new analysis data for destination has changed or destination block hasn't // been processed. if ((current.IsReachable || !destination.IsReachable) && (!analyzer.IsEqual(currentDestinationData, mergedAnalysisData) || !processedBlocks.Contains(destination))) { analyzer.SetCurrentAnalysisData(destination, mergedAnalysisData, cancellationToken); toVisit.Add(branch.Destination.Ordinal); } } return; default: throw ExceptionUtilities.UnexpectedValue(branch.Semantics); } } // Returns whether we should proceed to the destination after finallies were taken care of. bool StepThroughFinally(ControlFlowRegion region, int destinationOrdinal, ref TBlockAnalysisData currentAnalysisData) { while (!region.ContainsBlock(destinationOrdinal)) { Debug.Assert(region.Kind != ControlFlowRegionKind.Root); ControlFlowRegion enclosing = region.EnclosingRegion; if (region.Kind == ControlFlowRegionKind.Try && enclosing.Kind == ControlFlowRegionKind.TryAndFinally) { Debug.Assert(enclosing.NestedRegions[0] == region); Debug.Assert(enclosing.NestedRegions[1].Kind == ControlFlowRegionKind.Finally); if (!StepThroughSingleFinally(enclosing.NestedRegions[1], ref currentAnalysisData)) { // The point that continues dispatch is not reachable. Cancel the dispatch. return(false); } } region = enclosing; } return(true); } // Returns whether we should proceed with dispatch after finally was taken care of. bool StepThroughSingleFinally(ControlFlowRegion @finally, ref TBlockAnalysisData currentAnalysisData) { Debug.Assert(@finally.Kind == ControlFlowRegionKind.Finally); var previousAnalysisData = analyzer.GetCurrentAnalysisData(blocks[@finally.FirstBlockOrdinal]); var mergedAnalysisData = analyzer.Merge(previousAnalysisData, currentAnalysisData, cancellationToken); if (!analyzer.IsEqual(previousAnalysisData, mergedAnalysisData)) { // For simplicity, we do a complete walk of the finally/filter region in isolation // to make sure that the resume dispatch point is reachable from its beginning. // It could also be reachable through invalid branches into the finally and we don't want to consider // these cases for regular finally handling. currentAnalysisData = RunCore(blocks, analyzer, @finally.FirstBlockOrdinal, @finally.LastBlockOrdinal, mergedAnalysisData, unreachableBlocksToVisit, outOfRangeBlocksToVisit: toVisit, continueDispatchAfterFinally, dispatchedExceptionsFromRegions, cancellationToken); } if (!continueDispatchAfterFinally.TryGetValue(@finally, out bool dispatch)) { dispatch = false; continueDispatchAfterFinally.Add(@finally, false); } return(dispatch); } void DispatchException(ControlFlowRegion fromRegion) { do { if (!dispatchedExceptionsFromRegions.Add(fromRegion)) { return; } ControlFlowRegion enclosing = fromRegion.Kind == ControlFlowRegionKind.Root ? null : fromRegion.EnclosingRegion; if (fromRegion.Kind == ControlFlowRegionKind.Try) { switch (enclosing.Kind) { case ControlFlowRegionKind.TryAndFinally: Debug.Assert(enclosing.NestedRegions[0] == fromRegion); Debug.Assert(enclosing.NestedRegions[1].Kind == ControlFlowRegionKind.Finally); var currentAnalysisData = analyzer.GetCurrentAnalysisData(blocks[fromRegion.FirstBlockOrdinal]); if (!StepThroughSingleFinally(enclosing.NestedRegions[1], ref currentAnalysisData)) { // The point that continues dispatch is not reachable. Cancel the dispatch. return; } break; case ControlFlowRegionKind.TryAndCatch: Debug.Assert(enclosing.NestedRegions[0] == fromRegion); DispatchExceptionThroughCatches(enclosing, startAt: 1); break; default: throw ExceptionUtilities.UnexpectedValue(enclosing.Kind); } } else if (fromRegion.Kind == ControlFlowRegionKind.Filter) { // If filter throws, dispatch is resumed at the next catch with an original exception Debug.Assert(enclosing.Kind == ControlFlowRegionKind.FilterAndHandler); ControlFlowRegion tryAndCatch = enclosing.EnclosingRegion; Debug.Assert(tryAndCatch.Kind == ControlFlowRegionKind.TryAndCatch); int index = tryAndCatch.NestedRegions.IndexOf(enclosing, startIndex: 1); if (index > 0) { DispatchExceptionThroughCatches(tryAndCatch, startAt: index + 1); fromRegion = tryAndCatch; continue; } throw ExceptionUtilities.Unreachable; } fromRegion = enclosing; }while (fromRegion != null); } void DispatchExceptionThroughCatches(ControlFlowRegion tryAndCatch, int startAt) { // For simplicity, we do not try to figure out whether a catch clause definitely // handles all exceptions. Debug.Assert(tryAndCatch.Kind == ControlFlowRegionKind.TryAndCatch); Debug.Assert(startAt > 0); Debug.Assert(startAt <= tryAndCatch.NestedRegions.Length); for (int i = startAt; i < tryAndCatch.NestedRegions.Length; i++) { ControlFlowRegion @catch = tryAndCatch.NestedRegions[i]; switch (@catch.Kind) { case ControlFlowRegionKind.Catch: toVisit.Add(@catch.FirstBlockOrdinal); break; case ControlFlowRegionKind.FilterAndHandler: BasicBlock entryBlock = blocks[@catch.FirstBlockOrdinal]; Debug.Assert(@catch.NestedRegions[0].Kind == ControlFlowRegionKind.Filter); Debug.Assert(entryBlock.Ordinal == @catch.NestedRegions[0].FirstBlockOrdinal); toVisit.Add(entryBlock.Ordinal); break; default: throw ExceptionUtilities.UnexpectedValue(@catch.Kind); } } } }
private void AnalyzeUnusedValueAssignments( OperationBlockAnalysisContext context, bool isComputingUnusedParams, PooledHashSet <SymbolUsageResult> symbolUsageResultsBuilder, out bool hasBlockWithAllUsedSymbolWrites) { hasBlockWithAllUsedSymbolWrites = false; foreach (var operationBlock in context.OperationBlocks) { if (!ShouldAnalyze(operationBlock, context.OwningSymbol)) { continue; } // First perform the fast, aggressive, imprecise operation-tree based analysis. // This analysis might flag some "used" symbol writes as "unused", but will not miss reporting any truly unused symbol writes. // This initial pass helps us reduce the number of methods for which we perform the slower second pass. // We perform the first fast pass only if there are no delegate creations/lambda methods. // This is due to the fact that tracking which local/parameter points to which delegate creation target // at any given program point needs needs flow analysis (second pass). if (!_hasDelegateCreationOrAnonymousFunction) { var resultFromOperationBlockAnalysis = SymbolUsageAnalysis.Run(operationBlock, context.OwningSymbol, context.CancellationToken); if (!resultFromOperationBlockAnalysis.HasUnreadSymbolWrites()) { // Assert that even slow pass (dataflow analysis) would have yielded no unused symbol writes. Debug.Assert(!SymbolUsageAnalysis.Run(context.GetControlFlowGraph(operationBlock), context.OwningSymbol, context.CancellationToken) .HasUnreadSymbolWrites()); hasBlockWithAllUsedSymbolWrites = true; continue; } } // Now perform the slower, precise, CFG based dataflow analysis to identify the actual unused symbol writes. var controlFlowGraph = context.GetControlFlowGraph(operationBlock); var symbolUsageResult = SymbolUsageAnalysis.Run(controlFlowGraph, context.OwningSymbol, context.CancellationToken); symbolUsageResultsBuilder.Add(symbolUsageResult); foreach (var(symbol, unreadWriteOperation) in symbolUsageResult.GetUnreadSymbolWrites()) { if (unreadWriteOperation == null) { // Null operation is used for initial write for the parameter from method declaration. // So, the initial value of the parameter is never read in this operation block. // However, we do not report this as an unused parameter here as a different operation block // might be reading the initial parameter value. // For example, a constructor with both a constructor initializer and body will have two different operation blocks // and a parameter must be unused across both these blocks to be marked unused. // However, we do report unused parameters for local function here. // Local function parameters are completely scoped to this operation block, and should be reported per-operation block. var unusedParameter = (IParameterSymbol)symbol; if (isComputingUnusedParams && unusedParameter.ContainingSymbol.IsLocalFunction()) { var hasReference = symbolUsageResult.SymbolsRead.Contains(unusedParameter); _symbolStartAnalyzer.ReportUnusedParameterDiagnostic(unusedParameter, hasReference, context.ReportDiagnostic, context.Options, context.CancellationToken); } continue; } if (ShouldReportUnusedValueDiagnostic(symbol, unreadWriteOperation, symbolUsageResult, out var properties)) { var diagnostic = DiagnosticHelper.Create(s_valueAssignedIsUnusedRule, _symbolStartAnalyzer._compilationAnalyzer.GetDefinitionLocationToFade(unreadWriteOperation), _options.UnusedValueAssignmentSeverity, additionalLocations: null, properties, symbol.Name); context.ReportDiagnostic(diagnostic); } } } return; // Local functions. bool ShouldReportUnusedValueDiagnostic( ISymbol symbol, IOperation unreadWriteOperation, SymbolUsageResult resultFromFlowAnalysis, out ImmutableDictionary <string, string> properties) { properties = null; if (_options.UnusedValueAssignmentSeverity == ReportDiagnostic.Suppress || symbol.GetSymbolType().IsErrorType()) { return(false); } // Flag to indicate if the symbol has no reads. var isUnusedLocalAssignment = symbol is ILocalSymbol localSymbol && !resultFromFlowAnalysis.SymbolsRead.Contains(localSymbol); var isRemovableAssignment = IsRemovableAssignmentWithoutSideEffects(unreadWriteOperation); if (isUnusedLocalAssignment && !isRemovableAssignment && _options.UnusedValueAssignmentPreference == UnusedValuePreference.UnusedLocalVariable) { // Meets current user preference of using unused local symbols for storing computation result. // Skip reporting diagnostic. return(false); } properties = s_propertiesMap[(_options.UnusedValueAssignmentPreference, isUnusedLocalAssignment, isRemovableAssignment)]; return(true); }
public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); context.RegisterCompilationStartAction( (CompilationStartAnalysisContext compilationContext) => { Compilation compilation = compilationContext.Compilation; TaintedDataConfig taintedDataConfig = TaintedDataConfig.GetOrCreate(compilation); TaintedDataSymbolMap <SourceInfo> sourceInfoSymbolMap = taintedDataConfig.GetSourceSymbolMap(this.SinkKind); if (sourceInfoSymbolMap.IsEmpty) { return; } TaintedDataSymbolMap <SinkInfo> sinkInfoSymbolMap = taintedDataConfig.GetSinkSymbolMap(this.SinkKind); if (sinkInfoSymbolMap.IsEmpty) { return; } compilationContext.RegisterOperationBlockStartAction( operationBlockStartContext => { ISymbol owningSymbol = operationBlockStartContext.OwningSymbol; AnalyzerOptions options = operationBlockStartContext.Options; CancellationToken cancellationToken = operationBlockStartContext.CancellationToken; if (options.IsConfiguredToSkipAnalysis(TaintedDataEnteringSinkDescriptor, owningSymbol, compilation)) { return; } WellKnownTypeProvider wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(compilation); Lazy <ControlFlowGraph?> controlFlowGraphFactory = new Lazy <ControlFlowGraph?>( () => operationBlockStartContext.OperationBlocks.GetControlFlowGraph()); Lazy <PointsToAnalysisResult?> pointsToFactory = new Lazy <PointsToAnalysisResult?>( () => { if (controlFlowGraphFactory.Value == null) { return(null); } InterproceduralAnalysisConfiguration interproceduralAnalysisConfiguration = InterproceduralAnalysisConfiguration.Create( options, SupportedDiagnostics, controlFlowGraphFactory.Value, operationBlockStartContext.Compilation, defaultInterproceduralAnalysisKind: InterproceduralAnalysisKind.ContextSensitive); return(PointsToAnalysis.TryGetOrComputeResult( controlFlowGraphFactory.Value, owningSymbol, options, wellKnownTypeProvider, PointsToAnalysisKind.Complete, interproceduralAnalysisConfiguration, interproceduralAnalysisPredicate: null)); }); Lazy <(PointsToAnalysisResult?, ValueContentAnalysisResult?)> valueContentFactory = new Lazy <(PointsToAnalysisResult?, ValueContentAnalysisResult?)>( () => { if (controlFlowGraphFactory.Value == null) { return(null, null); } InterproceduralAnalysisConfiguration interproceduralAnalysisConfiguration = InterproceduralAnalysisConfiguration.Create( options, SupportedDiagnostics, controlFlowGraphFactory.Value, operationBlockStartContext.Compilation, defaultInterproceduralAnalysisKind: InterproceduralAnalysisKind.ContextSensitive); ValueContentAnalysisResult?valuecontentAnalysisResult = ValueContentAnalysis.TryGetOrComputeResult( controlFlowGraphFactory.Value, owningSymbol, options, wellKnownTypeProvider, PointsToAnalysisKind.Complete, interproceduralAnalysisConfiguration, out _, out PointsToAnalysisResult? p); return(p, valuecontentAnalysisResult); }); PooledHashSet <IOperation> rootOperationsNeedingAnalysis = PooledHashSet <IOperation> .GetInstance(); operationBlockStartContext.RegisterOperationAction( operationAnalysisContext => { IPropertyReferenceOperation propertyReferenceOperation = (IPropertyReferenceOperation)operationAnalysisContext.Operation; if (sourceInfoSymbolMap.IsSourceProperty(propertyReferenceOperation.Property)) { lock (rootOperationsNeedingAnalysis) { rootOperationsNeedingAnalysis.Add(propertyReferenceOperation.GetRoot()); } } }, OperationKind.PropertyReference); if (sourceInfoSymbolMap.RequiresParameterReferenceAnalysis) { operationBlockStartContext.RegisterOperationAction( operationAnalysisContext => { IParameterReferenceOperation parameterReferenceOperation = (IParameterReferenceOperation)operationAnalysisContext.Operation; if (sourceInfoSymbolMap.IsSourceParameter(parameterReferenceOperation.Parameter, wellKnownTypeProvider)) { lock (rootOperationsNeedingAnalysis) { rootOperationsNeedingAnalysis.Add(parameterReferenceOperation.GetRoot()); } } }, OperationKind.ParameterReference); } operationBlockStartContext.RegisterOperationAction( operationAnalysisContext => { IInvocationOperation invocationOperation = (IInvocationOperation)operationAnalysisContext.Operation; if (sourceInfoSymbolMap.IsSourceMethod( invocationOperation.TargetMethod, invocationOperation.Arguments, pointsToFactory, valueContentFactory, out _)) { lock (rootOperationsNeedingAnalysis) { rootOperationsNeedingAnalysis.Add(invocationOperation.GetRoot()); } } }, OperationKind.Invocation); if (TaintedDataConfig.HasTaintArraySource(SinkKind)) { operationBlockStartContext.RegisterOperationAction( operationAnalysisContext => { IArrayInitializerOperation arrayInitializerOperation = (IArrayInitializerOperation)operationAnalysisContext.Operation; if (arrayInitializerOperation.GetAncestor <IArrayCreationOperation>(OperationKind.ArrayCreation)?.Type is IArrayTypeSymbol arrayTypeSymbol && sourceInfoSymbolMap.IsSourceConstantArrayOfType(arrayTypeSymbol, arrayInitializerOperation)) { lock (rootOperationsNeedingAnalysis) { rootOperationsNeedingAnalysis.Add(operationAnalysisContext.Operation.GetRoot()); } } }, OperationKind.ArrayInitializer); } operationBlockStartContext.RegisterOperationBlockEndAction( operationBlockAnalysisContext => { try { lock (rootOperationsNeedingAnalysis) { if (!rootOperationsNeedingAnalysis.Any()) { return; } if (controlFlowGraphFactory.Value == null) { return; } foreach (IOperation rootOperation in rootOperationsNeedingAnalysis) { TaintedDataAnalysisResult?taintedDataAnalysisResult = TaintedDataAnalysis.TryGetOrComputeResult( controlFlowGraphFactory.Value, operationBlockAnalysisContext.Compilation, operationBlockAnalysisContext.OwningSymbol, operationBlockAnalysisContext.Options, TaintedDataEnteringSinkDescriptor, sourceInfoSymbolMap, taintedDataConfig.GetSanitizerSymbolMap(this.SinkKind), sinkInfoSymbolMap); if (taintedDataAnalysisResult == null) { return; } foreach (TaintedDataSourceSink sourceSink in taintedDataAnalysisResult.TaintedDataSourceSinks) { if (!sourceSink.SinkKinds.Contains(this.SinkKind)) { continue; } foreach (SymbolAccess sourceOrigin in sourceSink.SourceOrigins) { // Something like: // CA3001: Potential SQL injection vulnerability was found where '{0}' in method '{1}' may be tainted by user-controlled data from '{2}' in method '{3}'. Diagnostic diagnostic = Diagnostic.Create( this.TaintedDataEnteringSinkDescriptor, sourceSink.Sink.Location, additionalLocations: new Location[] { sourceOrigin.Location }, messageArgs: new object[] { sourceSink.Sink.Symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), sourceSink.Sink.AccessingMethod.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), sourceOrigin.Symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), sourceOrigin.AccessingMethod.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat) }); operationBlockAnalysisContext.ReportDiagnostic(diagnostic); } } } } } finally { rootOperationsNeedingAnalysis.Free(compilationContext.CancellationToken); } }); }); }); }
private void AnalyzeUnusedValueAssignments( OperationBlockAnalysisContext context, bool isComputingUnusedParams, PooledHashSet <SymbolUsageResult> symbolUsageResultsBuilder, out bool hasBlockWithAllUsedSymbolWrites, out bool hasOperationNoneDescendant) { hasBlockWithAllUsedSymbolWrites = false; hasOperationNoneDescendant = false; foreach (var operationBlock in context.OperationBlocks) { if (!ShouldAnalyze(operationBlock, context.OwningSymbol, ref hasOperationNoneDescendant)) { continue; } // First perform the fast, aggressive, imprecise operation-tree based analysis. // This analysis might flag some "used" symbol writes as "unused", but will not miss reporting any truly unused symbol writes. // This initial pass helps us reduce the number of methods for which we perform the slower second pass. // We perform the first fast pass only if there are no delegate creations/lambda methods. // This is due to the fact that tracking which local/parameter points to which delegate creation target // at any given program point needs needs flow analysis (second pass). if (!_hasDelegateCreationOrAnonymousFunction) { var resultFromOperationBlockAnalysis = SymbolUsageAnalysis.Run(operationBlock, context.OwningSymbol, context.CancellationToken); if (!resultFromOperationBlockAnalysis.HasUnreadSymbolWrites()) { // Assert that even slow pass (dataflow analysis) would have yielded no unused symbol writes. Debug.Assert(!SymbolUsageAnalysis.Run(context.GetControlFlowGraph(operationBlock), context.OwningSymbol, context.CancellationToken) .HasUnreadSymbolWrites()); hasBlockWithAllUsedSymbolWrites = true; continue; } } // Now perform the slower, precise, CFG based dataflow analysis to identify the actual unused symbol writes. var controlFlowGraph = context.GetControlFlowGraph(operationBlock); var symbolUsageResult = SymbolUsageAnalysis.Run(controlFlowGraph, context.OwningSymbol, context.CancellationToken); symbolUsageResultsBuilder.Add(symbolUsageResult); foreach (var(symbol, unreadWriteOperation) in symbolUsageResult.GetUnreadSymbolWrites()) { if (unreadWriteOperation == null) { // Null operation is used for initial write for the parameter from method declaration. // So, the initial value of the parameter is never read in this operation block. // However, we do not report this as an unused parameter here as a different operation block // might be reading the initial parameter value. // For example, a constructor with both a constructor initializer and body will have two different operation blocks // and a parameter must be unused across both these blocks to be marked unused. // However, we do report unused parameters for local function here. // Local function parameters are completely scoped to this operation block, and should be reported per-operation block. var unusedParameter = (IParameterSymbol)symbol; if (isComputingUnusedParams && unusedParameter.ContainingSymbol.IsLocalFunction()) { var hasReference = symbolUsageResult.SymbolsRead.Contains(unusedParameter); bool shouldReport; switch (unusedParameter.RefKind) { case RefKind.Out: // Do not report out parameters of local functions. // If they are unused in the caller, we will flag the // out argument at the local function callsite. shouldReport = false; break; case RefKind.Ref: // Report ref parameters only if they have no read/write references. // Note that we always have one write for the parameter input value from the caller. shouldReport = !hasReference && symbolUsageResult.GetSymbolWriteCount(unusedParameter) == 1; break; default: shouldReport = true; break; } if (shouldReport) { _symbolStartAnalyzer.ReportUnusedParameterDiagnostic(unusedParameter, hasReference, context.ReportDiagnostic, context.Options, context.CancellationToken); } } continue; } if (ShouldReportUnusedValueDiagnostic(symbol, unreadWriteOperation, symbolUsageResult, out var properties)) { var diagnostic = DiagnosticHelper.Create(s_valueAssignedIsUnusedRule, _symbolStartAnalyzer._compilationAnalyzer.GetDefinitionLocationToFade(unreadWriteOperation), _options.UnusedValueAssignmentSeverity, additionalLocations: null, properties, symbol.Name); context.ReportDiagnostic(diagnostic); } } } return; // Local functions. bool ShouldReportUnusedValueDiagnostic( ISymbol symbol, IOperation unreadWriteOperation, SymbolUsageResult resultFromFlowAnalysis, out ImmutableDictionary <string, string> properties) { Debug.Assert(!(symbol is ILocalSymbol local) || !local.IsRef); properties = null; // Bail out in following cases: // 1. End user has configured the diagnostic to be suppressed. // 2. Symbol has error type, hence the diagnostic could be noised // 3. Static local symbols. Assignment to static locals // is not unnecessary as the assigned value can be used on the next invocation. // 4. Ignore special discard symbol names (see https://github.com/dotnet/roslyn/issues/32923). if (_options.UnusedValueAssignmentSeverity == ReportDiagnostic.Suppress || symbol.GetSymbolType().IsErrorType() || (symbol.IsStatic && symbol.Kind == SymbolKind.Local) || IsSymbolWithSpecialDiscardName(symbol)) { return(false); } // Flag to indicate if the symbol has no reads. var isUnusedLocalAssignment = symbol is ILocalSymbol localSymbol && !resultFromFlowAnalysis.SymbolsRead.Contains(localSymbol); var isRemovableAssignment = IsRemovableAssignmentWithoutSideEffects(unreadWriteOperation); if (isUnusedLocalAssignment && !isRemovableAssignment && _options.UnusedValueAssignmentPreference == UnusedValuePreference.UnusedLocalVariable) { // Meets current user preference of using unused local symbols for storing computation result. // Skip reporting diagnostic. return(false); } properties = s_propertiesMap[(_options.UnusedValueAssignmentPreference, isUnusedLocalAssignment, isRemovableAssignment)]; return(true); }
public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); context.RegisterCompilationStartAction( (CompilationStartAnalysisContext compilationContext) => { TaintedDataConfig taintedDataConfig = TaintedDataConfig.GetOrCreate(compilationContext.Compilation); TaintedDataSymbolMap <SourceInfo> sourceInfoSymbolMap = taintedDataConfig.GetSourceSymbolMap(this.SinkKind); if (sourceInfoSymbolMap.IsEmpty) { return; } TaintedDataSymbolMap <SinkInfo> sinkInfoSymbolMap = taintedDataConfig.GetSinkSymbolMap(this.SinkKind); if (sinkInfoSymbolMap.IsEmpty) { return; } compilationContext.RegisterOperationBlockStartAction( operationBlockStartContext => { ISymbol owningSymbol = operationBlockStartContext.OwningSymbol; if (owningSymbol.IsConfiguredToSkipAnalysis(operationBlockStartContext.Options, TaintedDataEnteringSinkDescriptor, operationBlockStartContext.Compilation, operationBlockStartContext.CancellationToken)) { return; } PooledHashSet <IOperation> rootOperationsNeedingAnalysis = PooledHashSet <IOperation> .GetInstance(); operationBlockStartContext.RegisterOperationAction( operationAnalysisContext => { IPropertyReferenceOperation propertyReferenceOperation = (IPropertyReferenceOperation)operationAnalysisContext.Operation; IOperation rootOperation = operationAnalysisContext.Operation.GetRoot(); if (sourceInfoSymbolMap.IsSourceProperty(propertyReferenceOperation.Property)) { lock (rootOperationsNeedingAnalysis) { rootOperationsNeedingAnalysis.Add(rootOperation); } } }, OperationKind.PropertyReference); operationBlockStartContext.RegisterOperationAction( operationAnalysisContext => { IInvocationOperation invocationOperation = (IInvocationOperation)operationAnalysisContext.Operation; IOperation rootOperation = operationAnalysisContext.Operation.GetRoot(); PooledDictionary <PointsToCheck, ImmutableHashSet <string> > evaluateWithPointsToAnalysis = null; PooledDictionary <ValueContentCheck, ImmutableHashSet <string> > evaluateWithValueContentAnalysis = null; PointsToAnalysisResult pointsToAnalysisResult = null; ValueContentAnalysisResult valueContentAnalysisResult = null; if (rootOperation.TryGetEnclosingControlFlowGraph(out ControlFlowGraph cfg)) { pointsToAnalysisResult = PointsToAnalysis.TryGetOrComputeResult( cfg, owningSymbol, operationAnalysisContext.Options, WellKnownTypeProvider.GetOrCreate(operationAnalysisContext.Compilation), InterproceduralAnalysisConfiguration.Create( operationAnalysisContext.Options, SupportedDiagnostics, defaultInterproceduralAnalysisKind: InterproceduralAnalysisKind.ContextSensitive, cancellationToken: operationAnalysisContext.CancellationToken), interproceduralAnalysisPredicateOpt: null); if (pointsToAnalysisResult == null) { return; } } if (sourceInfoSymbolMap.RequiresValueContentAnalysis) { valueContentAnalysisResult = ValueContentAnalysis.TryGetOrComputeResult( cfg, owningSymbol, operationAnalysisContext.Options, WellKnownTypeProvider.GetOrCreate(operationAnalysisContext.Compilation), InterproceduralAnalysisConfiguration.Create( operationAnalysisContext.Options, SupportedDiagnostics, defaultInterproceduralAnalysisKind: InterproceduralAnalysisKind.ContextSensitive, cancellationToken: operationAnalysisContext.CancellationToken), out var copyAnalysisResult, out pointsToAnalysisResult); if (valueContentAnalysisResult == null) { return; } } try { if (sourceInfoSymbolMap.IsSourceMethod( invocationOperation.TargetMethod, invocationOperation.Arguments, invocationOperation.Arguments.Select(o => pointsToAnalysisResult[o.Kind, o.Syntax]).ToImmutableArray(), invocationOperation.Arguments.Select(o => valueContentAnalysisResult[o.Kind, o.Syntax]).ToImmutableArray(), out _)) { lock (rootOperationsNeedingAnalysis) { rootOperationsNeedingAnalysis.Add(rootOperation); } } } finally { evaluateWithPointsToAnalysis?.Free(); evaluateWithValueContentAnalysis?.Free(); } }, OperationKind.Invocation); if (taintedDataConfig.HasTaintArraySource(SinkKind)) { operationBlockStartContext.RegisterOperationAction( operationAnalysisContext => { IArrayInitializerOperation arrayInitializerOperation = (IArrayInitializerOperation)operationAnalysisContext.Operation; if (arrayInitializerOperation.GetAncestor <IArrayCreationOperation>(OperationKind.ArrayCreation)?.Type is IArrayTypeSymbol arrayTypeSymbol && sourceInfoSymbolMap.IsSourceConstantArrayOfType(arrayTypeSymbol)) { lock (rootOperationsNeedingAnalysis) { rootOperationsNeedingAnalysis.Add(operationAnalysisContext.Operation.GetRoot()); } } }, OperationKind.ArrayInitializer); } operationBlockStartContext.RegisterOperationBlockEndAction( operationBlockAnalysisContext => { try { lock (rootOperationsNeedingAnalysis) { if (!rootOperationsNeedingAnalysis.Any()) { return; } foreach (IOperation rootOperation in rootOperationsNeedingAnalysis) { if (!rootOperation.TryGetEnclosingControlFlowGraph(out var cfg)) { continue; } TaintedDataAnalysisResult taintedDataAnalysisResult = TaintedDataAnalysis.TryGetOrComputeResult( cfg, operationBlockAnalysisContext.Compilation, operationBlockAnalysisContext.OwningSymbol, operationBlockAnalysisContext.Options, TaintedDataEnteringSinkDescriptor, sourceInfoSymbolMap, taintedDataConfig.GetSanitizerSymbolMap(this.SinkKind), sinkInfoSymbolMap, operationBlockAnalysisContext.CancellationToken); if (taintedDataAnalysisResult == null) { return; } foreach (TaintedDataSourceSink sourceSink in taintedDataAnalysisResult.TaintedDataSourceSinks) { if (!sourceSink.SinkKinds.Contains(this.SinkKind)) { continue; } foreach (SymbolAccess sourceOrigin in sourceSink.SourceOrigins) { // Something like: // CA3001: Potential SQL injection vulnerability was found where '{0}' in method '{1}' may be tainted by user-controlled data from '{2}' in method '{3}'. Diagnostic diagnostic = Diagnostic.Create( this.TaintedDataEnteringSinkDescriptor, sourceSink.Sink.Location, additionalLocations: new Location[] { sourceOrigin.Location }, messageArgs: new object[] { sourceSink.Sink.Symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), sourceSink.Sink.AccessingMethod.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), sourceOrigin.Symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), sourceOrigin.AccessingMethod.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat) }); operationBlockAnalysisContext.ReportDiagnostic(diagnostic); } } } } } finally { rootOperationsNeedingAnalysis.Free(); } }); }); }); }
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(); }
/// <summary> /// Create the optimized plan for the location of lambda methods and whether scopes need access to parent scopes /// </summary> internal void ComputeLambdaScopesAndFrameCaptures(ParameterSymbol thisParam) { RemoveUnneededReferences(thisParam); VisitClosures(ScopeTree, (scope, closure) => { if (closure.CapturedVariables.Count > 0) { (Scope innermost, Scope outermost) = FindLambdaScopeRange(closure, scope); RecordClosureScope(innermost, outermost, closure); } }); (Scope innermost, Scope outermost) FindLambdaScopeRange(Closure closure, Scope closureScope) { Scope innermost = null; Scope outermost = null; var capturedVars = PooledHashSet <Symbol> .GetInstance(); capturedVars.AddAll(closure.CapturedVariables); // If any of the captured variables are local functions we'll need // to add the captured variables of that local function to the current // set. This has the effect of ensuring that if the local function // captures anything "above" the current scope then parent frame // is itself captured (so that the current lambda can call that // local function). foreach (var captured in closure.CapturedVariables) { if (captured is LocalFunctionSymbol localFunc) { var(found, _) = GetVisibleClosure(closureScope, localFunc); capturedVars.AddAll(found.CapturedVariables); } } for (var curScope = closureScope; curScope != null && capturedVars.Count > 0; curScope = curScope.Parent) { if (!(capturedVars.Overlaps(curScope.DeclaredVariables) || capturedVars.Overlaps(curScope.Closures.Select(c => c.OriginalMethodSymbol)))) { continue; } outermost = curScope; if (innermost == null) { innermost = curScope; } capturedVars.RemoveAll(curScope.DeclaredVariables); capturedVars.RemoveAll(curScope.Closures.Select(c => c.OriginalMethodSymbol)); } // If any captured variables are left, they're captured above method scope if (capturedVars.Count > 0) { outermost = null; } capturedVars.Free(); return(innermost, outermost); } void RecordClosureScope(Scope innermost, Scope outermost, Closure closure) { // 1) if there is innermost scope, lambda goes there as we cannot go any higher. // 2) scopes in [innermostScope, outermostScope) chain need to have access to the parent scope. // // Example: // if a lambda captures a method's parameter and `this`, // its innermost scope depth is 0 (method locals and parameters) // and outermost scope is -1 // Such lambda will be placed in a closure frame that corresponds to the method's outer block // and this frame will also lift original `this` as a field when created by its parent. // Note that it is completely irrelevant how deeply the lexical scope of the lambda was originally nested. if (innermost != null) { LambdaScopes.Add(closure.OriginalMethodSymbol, innermost.BoundNode); // Disable struct closures on methods converted to delegates, as well as on async and iterator methods. var markAsNoStruct = !CanTakeRefParameters(closure.OriginalMethodSymbol); if (markAsNoStruct) { ScopesThatCantBeStructs.Add(innermost.BoundNode); } while (innermost != outermost) { NeedsParentFrame.Add(innermost.BoundNode); innermost = innermost.Parent; if (markAsNoStruct && innermost != null) { ScopesThatCantBeStructs.Add(innermost.BoundNode); } } } } }
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 AddAliasedNames(CompilationUnitSyntax compilationUnit) { // Using `position: 0` gets all the global aliases defined in other files pulled in here. var scopes = _semanticModel.GetImportScopes(position: 0, _cancellationToken); foreach (var scope in scopes) { foreach (var alias in scope.Aliases) { var name = alias.Target.Name; if (!string.IsNullOrEmpty(name)) { _aliasedNames.Add(name); } } } foreach (var usingDirective in compilationUnit.Usings) { AddAliasedName(usingDirective); } foreach (var member in compilationUnit.Members) { if (member is BaseNamespaceDeclarationSyntax namespaceDeclaration) { AddAliasedNames(namespaceDeclaration); } } return; void AddAliasedName(UsingDirectiveSyntax usingDirective) { if (usingDirective.Alias is not null && usingDirective.Name.GetRightmostName() is IdentifierNameSyntax identifierName) { var identifierAlias = identifierName.Identifier.ValueText; if (!string.IsNullOrEmpty(identifierAlias)) { _aliasedNames.Add(identifierAlias); } } } void AddAliasedNames(BaseNamespaceDeclarationSyntax namespaceDeclaration) { foreach (var usingDirective in namespaceDeclaration.Usings) { AddAliasedName(usingDirective); } foreach (var member in namespaceDeclaration.Members) { if (member is BaseNamespaceDeclarationSyntax memberNamespace) { AddAliasedNames(memberNamespace); } } } }
public static void ConcurrentAdd <T>(this PooledHashSet <T> pooledHashSet, T value) { lock (pooledHashSet) pooledHashSet.Add(value); }
/// <summary> /// Must be called only after <see cref="Closure.CapturedEnvironments"/> /// has been calculated. /// /// Finds the most optimal capture environment to place a closure in. /// This roughly corresponds to the 'highest' Scope in the tree where all /// the captured variables for this closure are in scope. This minimizes /// the number of indirections we may have to traverse to access captured /// variables. /// </summary> private void ComputeLambdaScopesAndFrameCaptures(ParameterSymbol thisParam) { VisitClosures(ScopeTree, (scope, closure) => { if (closure.CapturedVariables.Count > 0) { (Scope innermost, Scope outermost) = FindLambdaScopeRange(closure, scope); RecordClosureScope(innermost, outermost, closure); } }); (Scope innermost, Scope outermost) FindLambdaScopeRange(Closure closure, Scope closureScope) { // If the closure only captures this, put the method directly in the // top-level method's containing type if (closure.CapturedVariables.Count == 1 && closure.CapturedVariables.Single() is ParameterSymbol param && param.IsThis) { return(null, null); } Scope innermost = null; Scope outermost = null; var capturedVars = PooledHashSet <Symbol> .GetInstance(); capturedVars.AddAll(closure.CapturedVariables); // If any of the captured variables are local functions we'll need // to add the captured variables of that local function to the current // set. This has the effect of ensuring that if the local function // captures anything "above" the current scope then parent frame // is itself captured (so that the current lambda can call that // local function). foreach (var captured in closure.CapturedVariables) { if (captured is LocalFunctionSymbol localFunc) { var(found, _) = GetVisibleClosure(closureScope, localFunc); capturedVars.AddAll(found.CapturedVariables); } } for (var curScope = closureScope; curScope != null && capturedVars.Count > 0; curScope = curScope.Parent) { if (!(capturedVars.RemoveAll(curScope.DeclaredVariables) || capturedVars.RemoveAll(curScope.Closures.Select(c => c.OriginalMethodSymbol)))) { continue; } outermost = curScope; if (innermost == null) { innermost = curScope; } } // If any captured variables are left, they're captured above method scope if (capturedVars.Count > 0) { outermost = null; } capturedVars.Free(); return(innermost, outermost); } void RecordClosureScope(Scope innermost, Scope outermost, Closure closure) { // 1) if there is innermost scope, lambda goes there as we cannot go any higher. // 2) scopes in [innermostScope, outermostScope) chain need to have access to the parent scope. // // Example: // if a lambda captures a method's parameter and `this`, // its innermost scope is the root Scope (method locals and parameters) // and outermost Scope is null // Such lambda will be placed in a closure frame that corresponds to the method's outer block // and this frame will also lift original `this` as a field when created by its parent. // Note that it is completely irrelevant how deeply the lexical scope of the lambda was originally nested. if (innermost != null) { closure.ContainingEnvironmentOpt = innermost.DeclaredEnvironments[0]; while (innermost != outermost) { NeedsParentFrame.Add(innermost.BoundNode); innermost = innermost.Parent; } } } }