private string GetName(string source, string methodName, DkmVariableInfoFlags argumentFlags, string[] typeArguments, string[] argumentValues = null) { Debug.Assert((argumentFlags & (DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types)) == argumentFlags, "Unexpected argumentFlags", "argumentFlags = {0}", argumentFlags); var instructionDecoder = CSharpInstructionDecoder.Instance; var method = GetConstructedMethod(source, methodName, typeArguments, instructionDecoder); var includeParameterTypes = argumentFlags.Includes(DkmVariableInfoFlags.Types); var includeParameterNames = argumentFlags.Includes(DkmVariableInfoFlags.Names); ArrayBuilder <string> builder = null; if (argumentValues != null) { builder = ArrayBuilder <string> .GetInstance(); builder.AddRange(argumentValues); } var name = instructionDecoder.GetName(method, includeParameterTypes, includeParameterNames, builder); builder?.Free(); return(name); }
/// <summary> /// Updates the currently stored 'best result' if the current result is better. /// Returns 'true' if no further work is required and we can break early, or /// 'false' if we need to keep on going. /// /// If 'weight' is better than 'bestWeight' and matchSpanToAdd is not null, then /// matchSpanToAdd will be added to matchedSpansInReverse. /// </summary> private bool UpdateBestResultIfBetter( int?weight, ArrayBuilder <TextSpan> matchedSpansInReverse, ref int?bestWeight, ref ArrayBuilder <TextSpan> bestMatchedSpansInReverse, TextSpan?matchSpanToAdd) { if (!IsBetter(weight, bestWeight)) { // Even though we matched this current candidate hump we failed to match // the remainder of the pattern. Continue to the next candidate hump // to see if our pattern character will match it and potentially succeed. matchedSpansInReverse?.Free(); // We need to keep going. return(false); } if (matchSpanToAdd != null) { matchedSpansInReverse?.Add(matchSpanToAdd.Value); } // This was result was better than whatever previous best result we had was. // Free and overwrite the existing best results, and keep going. bestWeight = weight; bestMatchedSpansInReverse?.Free(); bestMatchedSpansInReverse = matchedSpansInReverse; // We found a path that allowed us to match everything contiguously // from the beginning. This is the best match possible. So we can // just break out now and return this result. return(weight == CamelCaseMaxWeight); }
private static void FreeDeconstructionVariables(ArrayBuilder<DeconstructionVariable> variables) { foreach (var v in variables) { if (v.HasNestedVariables) { FreeDeconstructionVariables(v.NestedVariables); } } variables.Free(); }
private void GenerateImpl() { SetInitialDebugDocument(); // Synthesized methods should have a sequence point // at offset 0 to ensure correct stepping behavior. if (_emitPdbSequencePoints && _method.IsImplicitlyDeclared) { _builder.DefineInitialHiddenSequencePoint(); } try { EmitStatement(_boundBody); if (_indirectReturnState == IndirectReturnState.Needed) { // it is unfortunate that return was not handled while we were in scope of the method // it can happen in rare cases involving exception handling (for example all returns were from a try) // in such case we can still handle return here. HandleReturn(); } if (!_diagnostics.HasAnyErrors()) { _builder.Realize(); } } catch (EmitCancelledException) { Debug.Assert(_diagnostics.HasAnyErrors()); } _synthesizedLocalOrdinals.Free(); Debug.Assert(!(_expressionTemps?.Count > 0), "leaking expression temps?"); _expressionTemps?.Free(); _savedSequencePoints?.Free(); }
protected override DiagnosticInfo ResolveInfo() { var diagnosticsBuilder = ArrayBuilder <TypeParameterDiagnosticInfo> .GetInstance(); var warningsBuilder = ArrayBuilder <TypeParameterDiagnosticInfo> .GetInstance(); ArrayBuilder <TypeParameterDiagnosticInfo> useSiteDiagnosticsBuilder = null; // CheckTypeConstraints should only add nullability warnings to warningsBuilder. ConstraintsHelper.CheckTypeConstraints( _type, _conversions, _compilation, diagnosticsBuilder, warningsBuilder, ref useSiteDiagnosticsBuilder); // If there are multiple constraint check warnings, we'll report the first one only. var diagnostic = (warningsBuilder.Count == 0) ? null : warningsBuilder[0].DiagnosticInfo; useSiteDiagnosticsBuilder?.Free(); warningsBuilder.Free(); diagnosticsBuilder.Free(); return(diagnostic); }
private ImmutableArray <MergedNamespaceOrTypeDeclaration> MakeChildren() { ArrayBuilder <SingleNamespaceDeclaration> namespaces = null; ArrayBuilder <SingleTypeDeclaration> types = null; #region MyRegion ArrayBuilder <TemplateDeclaration> templates = null; #endregion bool allNamespacesHaveSameName = true; bool allTypesHaveSameIdentity = true; foreach (var decl in _declarations) { foreach (var child in decl.Children) { // it is either a type (more likely) var asType = child as SingleTypeDeclaration; if (asType != null) { // handle types if (types == null) { types = ArrayBuilder <SingleTypeDeclaration> .GetInstance(); } else if (allTypesHaveSameIdentity && !asType.Identity.Equals(types[0].Identity)) { allTypesHaveSameIdentity = false; } types.Add(asType); continue; } // or it is a namespace var asNamespace = child as SingleNamespaceDeclaration; if (asNamespace != null) { // handle namespace if (namespaces == null) { namespaces = ArrayBuilder <SingleNamespaceDeclaration> .GetInstance(); } else if (allNamespacesHaveSameName && !asNamespace.Name.Equals(namespaces[0].Name)) { allNamespacesHaveSameName = false; } namespaces.Add(asNamespace); continue; } #region PackageTemplate - MergedNamespaceDeclaration.MakeChildren() // ...or a template? var asTemplate = child as TemplateDeclaration; if (asTemplate != null) { if (templates == null) { templates = ArrayBuilder <TemplateDeclaration> .GetInstance(); } templates.Add(asTemplate); continue; } #endregion // Not sure if we can get here, perhaps, if we have errors, // but we care only about types and namespaces anyways. } } var children = ArrayBuilder <MergedNamespaceOrTypeDeclaration> .GetInstance(); if (namespaces != null) { if (allNamespacesHaveSameName) { children.Add(MergedNamespaceDeclaration.Create(namespaces.ToImmutableAndFree())); } else { var namespaceGroups = namespaces.ToDictionary(n => n.Name, StringOrdinalComparer.Instance); namespaces.Free(); foreach (var namespaceGroup in namespaceGroups.Values) { children.Add(MergedNamespaceDeclaration.Create(namespaceGroup)); } } } if (types != null) { if (allTypesHaveSameIdentity) { children.Add(new MergedTypeDeclaration(types.ToImmutableAndFree())); } else { var typeGroups = types.ToDictionary(t => t.Identity); types.Free(); foreach (var typeGroup in typeGroups.Values) { children.Add(new MergedTypeDeclaration(typeGroup)); } } } #region Package Template - MergedNamespaceDeclaration MakeChildren() if (templates != null) { foreach (var templateDeclaration in templates) { children.Add(new MergedTemplateDeclaration(templateDeclaration)); } templates.Free(); } #endregion return(children.ToImmutableAndFree()); }
public void Free() => MatchedSpansInReverse?.Free();
public void Dispose() { _spans?.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; }
/// <summary> /// Determine if "type" inherits from or implements "baseType", ignoring constructed types, and dealing /// only with original types. /// </summary> private static bool InheritsFromOrImplementsIgnoringConstruction( this TypeSymbol type, NamedTypeSymbol baseType, CSharpCompilation compilation, ref HashSet <DiagnosticInfo> useSiteDiagnostics, ConsList <TypeSymbol> basesBeingResolved = null) { Debug.Assert(type.IsDefinition); Debug.Assert(baseType.IsDefinition); PooledHashSet <NamedTypeSymbol> interfacesLookedAt = null; ArrayBuilder <NamedTypeSymbol> baseInterfaces = null; bool baseTypeIsInterface = baseType.IsInterface; if (baseTypeIsInterface) { interfacesLookedAt = PooledHashSet <NamedTypeSymbol> .GetInstance(); baseInterfaces = ArrayBuilder <NamedTypeSymbol> .GetInstance(); } PooledHashSet <NamedTypeSymbol> visited = null; var current = type; bool result = false; while ((object)current != null) { Debug.Assert(current.IsDefinition); if (baseTypeIsInterface == current.IsInterfaceType() && current == (object)baseType) { result = true; break; } if (baseTypeIsInterface) { getBaseInterfaces(current, baseInterfaces, interfacesLookedAt, basesBeingResolved); } // NOTE(cyrusn): The base type of an 'original' type may not be 'original'. i.e. // "class Goo : IBar<int>". We must map it back to the 'original' when as we walk up // the base type hierarchy. var next = current.GetNextBaseTypeNoUseSiteDiagnostics(basesBeingResolved, compilation, ref visited); if ((object)next == null) { current = null; } else { current = (TypeSymbol)next.OriginalDefinition; current.AddUseSiteDiagnostics(ref useSiteDiagnostics); } } visited?.Free(); if (!result && baseTypeIsInterface) { Debug.Assert(!result); while (baseInterfaces.Count != 0) { NamedTypeSymbol currentBase = baseInterfaces.Pop(); if (!currentBase.IsInterface) { continue; } Debug.Assert(currentBase.IsDefinition); if (currentBase == (object)baseType) { result = true; break; } getBaseInterfaces(currentBase, baseInterfaces, interfacesLookedAt, basesBeingResolved); } if (!result) { foreach (var candidate in interfacesLookedAt) { candidate.AddUseSiteDiagnostics(ref useSiteDiagnostics); } } } interfacesLookedAt?.Free(); baseInterfaces?.Free(); return(result);
/// <summary> /// Given a list of viable lookup results (based on the name, arity, and containing symbol), /// attempt to select one. /// </summary> private ImmutableArray <Symbol> ProcessCrefMemberLookupResults( ImmutableArray <Symbol> symbols, int arity, MemberCrefSyntax memberSyntax, TypeArgumentListSyntax typeArgumentListSyntax, BaseCrefParameterListSyntax parameterListSyntax, out Symbol ambiguityWinner, DiagnosticBag diagnostics) { Debug.Assert(!symbols.IsEmpty); if (parameterListSyntax == null) { return(ProcessParameterlessCrefMemberLookupResults(symbols, arity, memberSyntax, typeArgumentListSyntax, out ambiguityWinner, diagnostics)); } ArrayBuilder <Symbol> candidates = ArrayBuilder <Symbol> .GetInstance(); GetCrefOverloadResolutionCandidates(symbols, arity, typeArgumentListSyntax, candidates); ImmutableArray <ParameterSymbol> parameterSymbols = BindCrefParameters(parameterListSyntax, diagnostics); ImmutableArray <Symbol> results = PerformCrefOverloadResolution(candidates, parameterSymbols, arity, memberSyntax, out ambiguityWinner, diagnostics); candidates.Free(); // NOTE: This diagnostic is just a hint that might help fix a broken cref, so don't do // any work unless there are no viable candidates. if (results.Length == 0) { for (int i = 0; i < parameterSymbols.Length; i++) { if (ContainsNestedTypeOfUnconstructedGenericType(parameterSymbols[i].Type)) { // This warning is new in Roslyn, because our better-defined semantics for // cref lookup disallow some things that were possible in dev12. // // Consider the following code: // // public class C<T> // { // public class Inner { } // // public void M(Inner i) { } // // /// <see cref="M"/> // /// <see cref="C{T}.M"/> // /// <see cref="C{Q}.M"/> // /// <see cref="C{Q}.M(C{Q}.Inner)"/> // /// <see cref="C{Q}.M(Inner)"/> // WRN_UnqualifiedNestedTypeInCref // public void N() { } // } // // Dev12 binds all of the crefs as "M:C`1.M(C{`0}.Inner)". // Roslyn accepts all but the last. The issue is that the context for performing // the lookup is not C<Q>, but C<T>. Consequently, Inner binds to C<T>.Inner and // then overload resolution fails because C<T>.Inner does not match C<Q>.Inner, // the parameter type of C<Q>.M. Since we could not agree that the old behavior // was desirable (other than for backwards compatibility) and since mimicking it // would have been expensive, we settled on introducing a new warning that at // least hints to the user how then can work around the issue (i.e. by qualifying // Inner as C{Q}.Inner). Additional details are available in DevDiv #743425. // // CONSIDER: We could actually put the qualified form in the warning message, // but that would probably just make it more frustrating (i.e. if the compiler // knows exactly what I mean, why do I have to type it). // // NOTE: This is not a great location (whole parameter instead of problematic type), // but it's better than nothing. diagnostics.Add(ErrorCode.WRN_UnqualifiedNestedTypeInCref, parameterListSyntax.Parameters[i].Location); break; } } } return(results); }
public void Dispose() => _buffer?.Free();
private SyntaxTriviaList RewriteTrivia( SyntaxTriviaList triviaList, int depth, bool isTrailing, bool indentAfterLineBreak, bool mustHaveSeparator, int lineBreaksAfter) { ArrayBuilder <SyntaxTrivia> currentTriviaList = ArrayBuilder <SyntaxTrivia> .GetInstance(triviaList.Count); try { foreach (var trivia in triviaList) { if (trivia.IsKind(SyntaxKind.WhitespaceTrivia) || trivia.IsKind(SyntaxKind.EndOfLineTrivia) || trivia.FullWidth == 0) { continue; } var needsSeparator = (currentTriviaList.Count > 0 && NeedsSeparatorBetween(currentTriviaList.Last())) || (currentTriviaList.Count == 0 && isTrailing); var needsLineBreak = NeedsLineBreakBefore(trivia, isTrailing) || (currentTriviaList.Count > 0 && NeedsLineBreakBetween(currentTriviaList.Last(), trivia, isTrailing)); if (needsLineBreak && !_afterLineBreak) { currentTriviaList.Add(GetCarriageReturnLineFeed()); _afterLineBreak = true; _afterIndentation = false; } if (_afterLineBreak) { if (!_afterIndentation && NeedsIndentAfterLineBreak(trivia)) { currentTriviaList.Add(this.GetIndentation(GetDeclarationDepth(trivia))); _afterIndentation = true; } } else if (needsSeparator) { currentTriviaList.Add(GetSpace()); _afterLineBreak = false; _afterIndentation = false; } if (trivia.HasStructure) { var tr = this.VisitStructuredTrivia(trivia); currentTriviaList.Add(tr); } else if (trivia.IsKind(SyntaxKind.DocumentationCommentExteriorTrivia)) { // recreate exterior to remove any leading whitespace currentTriviaList.Add(s_trimmedDocCommentExtertior); } else { currentTriviaList.Add(trivia); } if (NeedsLineBreakAfter(trivia, isTrailing) && (currentTriviaList.Count == 0 || !EndsInLineBreak(currentTriviaList.Last()))) { currentTriviaList.Add(GetCarriageReturnLineFeed()); _afterLineBreak = true; _afterIndentation = false; } } if (lineBreaksAfter > 0) { if (currentTriviaList.Count > 0 && EndsInLineBreak(currentTriviaList.Last())) { lineBreaksAfter--; } for (int i = 0; i < lineBreaksAfter; i++) { currentTriviaList.Add(GetCarriageReturnLineFeed()); _afterLineBreak = true; _afterIndentation = false; } } else if (indentAfterLineBreak && _afterLineBreak && !_afterIndentation) { currentTriviaList.Add(this.GetIndentation(depth)); _afterIndentation = true; } else if (mustHaveSeparator) { currentTriviaList.Add(GetSpace()); _afterLineBreak = false; _afterIndentation = false; } if (currentTriviaList.Count == 0) { return(default(SyntaxTriviaList)); } else if (currentTriviaList.Count == 1) { return(SyntaxFactory.TriviaList(currentTriviaList.First())); } else { return(SyntaxFactory.TriviaList(currentTriviaList)); } } finally { currentTriviaList.Free(); } }
private BoundIndexerAccess TransformIndexerAccess(BoundIndexerAccess indexerAccess, ArrayBuilder <BoundExpression> stores, ArrayBuilder <LocalSymbol> temps) { var receiverOpt = indexerAccess.ReceiverOpt; Debug.Assert(receiverOpt != null); BoundExpression transformedReceiver; if (CanChangeValueBetweenReads(receiverOpt)) { BoundExpression rewrittenReceiver = VisitExpression(receiverOpt); BoundAssignmentOperator assignmentToTemp; // SPEC VIOLATION: It is not very clear when receiver of constrained callvirt is dereferenced - when pushed (in lexical order), // SPEC VIOLATION: or when actual call is executed. The actual behavior seems to be implementation specific in different JITs. // SPEC VIOLATION: To not depend on that, the right thing to do here is to store the value of the variable // SPEC VIOLATION: when variable has reference type (regular temp), and store variable's location when it has a value type. (ref temp) // SPEC VIOLATION: in a case of unconstrained generic type parameter a runtime test (default(T) == null) would be needed // SPEC VIOLATION: However, for compatibility with Dev12 we will continue treating all generic type parameters, constrained or not, // SPEC VIOLATION: as value types. var variableRepresentsLocation = rewrittenReceiver.Type.IsValueType || rewrittenReceiver.Type.Kind == SymbolKind.TypeParameter; var receiverTemp = _factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp, refKind: variableRepresentsLocation ? RefKind.Ref : RefKind.None); transformedReceiver = receiverTemp; stores.Add(assignmentToTemp); temps.Add(receiverTemp.LocalSymbol); } else { transformedReceiver = VisitExpression(receiverOpt); } // Dealing with the arguments is a bit tricky because they can be named out-of-order arguments; // we have to preserve both the source-code order of the side effects and the side effects // only being executed once. // // This is a subtly different problem than the problem faced by the conventional call // rewriter; with the conventional call rewriter we already know that the side effects // will only be executed once because the arguments are only being pushed on the stack once. // In a compound equality operator on an indexer the indices are placed on the stack twice. // That is to say, if you have: // // C().M(z : Z(), x : X(), y : Y()) // // then we can rewrite that into // // tempc = C() // tempz = Z() // tempc.M(X(), Y(), tempz) // // See, we can optimize away two of the temporaries, for x and y. But we cannot optimize away any of the // temporaries in // // C().Collection[z : Z(), x : X(), y : Y()] += 1; // // because we have to ensure not just that Z() happens first, but in addition that X() and Y() are only // called once. We have to generate this as // // tempc = C().Collection // tempz = Z() // tempx = X() // tempy = Y() // tempc[tempx, tempy, tempz] = tempc[tempx, tempy, tempz] + 1; // // Fortunately arguments to indexers are never ref or out, so we don't need to worry about that. // However, we can still do the optimization where constants are not stored in // temporaries; if we have // // C().Collection[z : 123, y : Y(), x : X()] += 1; // // Then we can generate that as // // tempc = C().Collection // tempx = X() // tempy = Y() // tempc[tempx, tempy, 123] = tempc[tempx, tempy, 123] + 1; ImmutableArray <BoundExpression> rewrittenArguments = VisitList(indexerAccess.Arguments); SyntaxNode syntax = indexerAccess.Syntax; PropertySymbol indexer = indexerAccess.Indexer; ImmutableArray <RefKind> argumentRefKinds = indexerAccess.ArgumentRefKindsOpt; bool expanded = indexerAccess.Expanded; ImmutableArray <int> argsToParamsOpt = indexerAccess.ArgsToParamsOpt; ImmutableArray <ParameterSymbol> parameters = indexer.Parameters; BoundExpression[] actualArguments = new BoundExpression[parameters.Length]; // The actual arguments that will be passed; one actual argument per formal parameter. ArrayBuilder <BoundAssignmentOperator> storesToTemps = ArrayBuilder <BoundAssignmentOperator> .GetInstance(rewrittenArguments.Length); ArrayBuilder <RefKind> refKinds = ArrayBuilder <RefKind> .GetInstance(parameters.Length, RefKind.None); // Step one: Store everything that is non-trivial into a temporary; record the // stores in storesToTemps and make the actual argument a reference to the temp. // Do not yet attempt to deal with params arrays or optional arguments. BuildStoresToTemps( expanded, argsToParamsOpt, parameters, argumentRefKinds, rewrittenArguments, forceLambdaSpilling: true, // lambdas must produce exactly one delegate so they must be spilled into a temp actualArguments, refKinds, storesToTemps); // Step two: If we have a params array, build the array and fill in the argument. if (expanded) { BoundExpression array = BuildParamsArray(syntax, indexer, argsToParamsOpt, rewrittenArguments, parameters, actualArguments[actualArguments.Length - 1]); BoundAssignmentOperator storeToTemp; var boundTemp = _factory.StoreToTemp(array, out storeToTemp); stores.Add(storeToTemp); temps.Add(boundTemp.LocalSymbol); actualArguments[actualArguments.Length - 1] = boundTemp; } // Step three: Now fill in the optional arguments. (Dev11 uses the getter for optional arguments in // compound assignments, but for deconstructions we use the setter if the getter is missing.) var accessor = indexer.GetOwnOrInheritedGetMethod() ?? indexer.GetOwnOrInheritedSetMethod(); InsertMissingOptionalArguments(syntax, accessor.Parameters, actualArguments, refKinds); // For a call, step four would be to optimize away some of the temps. However, we need them all to prevent // duplicate side-effects, so we'll skip that step. rewrittenArguments = actualArguments.AsImmutableOrNull(); foreach (BoundAssignmentOperator tempAssignment in storesToTemps) { temps.Add(((BoundLocal)tempAssignment.Left).LocalSymbol); stores.Add(tempAssignment); } storesToTemps.Free(); argumentRefKinds = GetRefKindsOrNull(refKinds); refKinds.Free(); // This is a temporary object that will be rewritten away before the lowering completes. return(new BoundIndexerAccess( syntax, transformedReceiver, indexer, rewrittenArguments, default(ImmutableArray <string>), argumentRefKinds, false, default(ImmutableArray <int>), null, indexerAccess.UseSetterForDefaultArgumentGeneration, indexerAccess.Type)); }
public void Dispose() { Debug.Assert(_stack != null); _stack.Free(); _stack = null; }
public void Dispose() => _complexItemBuilder.Free();
public override BoundNode VisitTypeOrInstanceInitializers(BoundTypeOrInstanceInitializers node) { ImmutableArray <BoundStatement> originalStatements = node.Statements; ArrayBuilder <BoundStatement> statements = ArrayBuilder <BoundStatement> .GetInstance(node.Statements.Length); foreach (var initializer in originalStatements) { if (IsFieldOrPropertyInitializer(initializer)) { statements.Add(RewriteExpressionStatement((BoundExpressionStatement)initializer, suppressInstrumentation: true)); } else { statements.Add(VisitStatement(initializer)); } } int optimizedInitializers = 0; bool optimize = _compilation.Options.OptimizationLevel == OptimizationLevel.Release; for (int i = 0; i < statements.Count; i++) { if (statements[i] == null || (optimize && IsFieldOrPropertyInitializer(originalStatements[i]) && ShouldOptimizeOutInitializer(statements[i]))) { optimizedInitializers++; if (!_factory.CurrentMethod.IsStatic) { // NOTE: Dev11 removes static initializers if ONLY all of them are optimized out statements[i] = null; } } } ImmutableArray <BoundStatement> rewrittenStatements; if (optimizedInitializers == statements.Count) { // all are optimized away rewrittenStatements = ImmutableArray <BoundStatement> .Empty; statements.Free(); } else { // instrument remaining statements int remaining = 0; for (int i = 0; i < statements.Count; i++) { BoundStatement rewritten = statements[i]; if (rewritten != null) { if (IsFieldOrPropertyInitializer(originalStatements[i])) { var original = (BoundExpressionStatement)originalStatements[i]; if (Instrument && !original.WasCompilerGenerated) { rewritten = _instrumenter.InstrumentFieldOrPropertyInitializer(original, rewritten); } } statements[remaining] = rewritten; remaining++; } } statements.Count = remaining; rewrittenStatements = statements.ToImmutableAndFree(); } return(new BoundStatementList(node.Syntax, rewrittenStatements, node.HasErrors)); }
/// <summary> /// Get the import strings for a given method, following forward pointers as necessary. /// </summary> /// <returns> /// For each namespace enclosing the method, a list of import strings, innermost to outermost. /// There should always be at least one entry, for the global namespace. /// </returns> public static ImmutableArray <ImmutableArray <string> > GetCSharpGroupedImportStrings <TArg>( int methodToken, TArg arg, Func <int, TArg, byte[]> getMethodCustomDebugInfo, Func <int, TArg, ImmutableArray <string> > getMethodImportStrings, out ImmutableArray <string> externAliasStrings) { externAliasStrings = default(ImmutableArray <string>); ImmutableArray <short> groupSizes = default(ImmutableArray <short>); bool seenForward = false; RETRY: byte[] bytes = getMethodCustomDebugInfo(methodToken, arg); if (bytes == null) { return(default(ImmutableArray <ImmutableArray <string> >)); } foreach (CustomDebugInfoRecord record in GetCustomDebugInfoRecords(bytes)) { switch (record.Kind) { case CustomDebugInfoKind.UsingInfo: if (!groupSizes.IsDefault) { throw new InvalidOperationException(string.Format("Expected at most one Using record for method {0}", FormatMethodToken(methodToken))); } groupSizes = DecodeUsingRecord(record.Data); break; case CustomDebugInfoKind.ForwardInfo: if (!externAliasStrings.IsDefault) { throw new InvalidOperationException(string.Format("Did not expect both Forward and ForwardToModule records for method {0}", FormatMethodToken(methodToken))); } methodToken = DecodeForwardRecord(record.Data); // Follow at most one forward link (as in FUNCBRECEE::ensureNamespaces). // NOTE: Dev11 may produce chains of forward links (e.g. for System.Collections.Immutable). if (!seenForward) { seenForward = true; goto RETRY; } break; case CustomDebugInfoKind.ForwardToModuleInfo: if (!externAliasStrings.IsDefault) { throw new InvalidOperationException(string.Format("Expected at most one ForwardToModule record for method {0}", FormatMethodToken(methodToken))); } int moduleInfoMethodToken = DecodeForwardToModuleRecord(record.Data); ImmutableArray <string> allModuleInfoImportStrings = getMethodImportStrings(moduleInfoMethodToken, arg); Debug.Assert(!allModuleInfoImportStrings.IsDefault); ArrayBuilder <string> externAliasBuilder = ArrayBuilder <string> .GetInstance(); foreach (string importString in allModuleInfoImportStrings) { if (IsCSharpExternAliasInfo(importString)) { externAliasBuilder.Add(importString); } } externAliasStrings = externAliasBuilder.ToImmutableAndFree(); break; } } if (groupSizes.IsDefault) { // This can happen in malformed PDBs (e.g. chains of forwards). return(default(ImmutableArray <ImmutableArray <string> >)); } ImmutableArray <string> importStrings = getMethodImportStrings(methodToken, arg); Debug.Assert(!importStrings.IsDefault); ArrayBuilder <ImmutableArray <string> > resultBuilder = ArrayBuilder <ImmutableArray <string> > .GetInstance(groupSizes.Length); ArrayBuilder <string> groupBuilder = ArrayBuilder <string> .GetInstance(); int pos = 0; foreach (short groupSize in groupSizes) { for (int i = 0; i < groupSize; i++, pos++) { if (pos >= importStrings.Length) { throw new InvalidOperationException(string.Format("Group size indicates more imports than there are import strings (method {0}).", FormatMethodToken(methodToken))); } string importString = importStrings[pos]; if (IsCSharpExternAliasInfo(importString)) { throw new InvalidOperationException(string.Format("Encountered extern alias info before all import strings were consumed (method {0}).", FormatMethodToken(methodToken))); } groupBuilder.Add(importString); } resultBuilder.Add(groupBuilder.ToImmutable()); groupBuilder.Clear(); } if (externAliasStrings.IsDefault) { Debug.Assert(groupBuilder.Count == 0); // Extern alias detail strings (prefix "Z") are not included in the group counts. for (; pos < importStrings.Length; pos++) { string importString = importStrings[pos]; if (!IsCSharpExternAliasInfo(importString)) { throw new InvalidOperationException(string.Format("Expected only extern alias info strings after consuming the indicated number of imports (method {0}).", FormatMethodToken(methodToken))); } groupBuilder.Add(importString); } externAliasStrings = groupBuilder.ToImmutableAndFree(); } else { groupBuilder.Free(); if (pos < importStrings.Length) { throw new InvalidOperationException(string.Format("Group size indicates fewer imports than there are import strings (method {0}).", FormatMethodToken(methodToken))); } } return(resultBuilder.ToImmutableAndFree()); }
/// <summary> /// Get the import strings for a given method, following forward pointers as necessary. /// </summary> /// <returns> /// For each namespace enclosing the method, a list of import strings, innermost to outermost. /// There should always be at least one entry, for the global namespace. /// </returns> public static ImmutableArray <ImmutableArray <string> > GetCSharpGroupedImportStrings(this ISymUnmanagedReader reader, int methodToken, out ImmutableArray <string> externAliasStrings) { externAliasStrings = default(ImmutableArray <string>); ImmutableArray <short> groupSizes = default(ImmutableArray <short>); bool seenForward = false; RETRY: var bytes = reader.GetCustomDebugInfo(methodToken); if (bytes == null) { return(default(ImmutableArray <ImmutableArray <string> >)); } int offset = 0; byte globalVersion; byte unusedGlobalCount; ReadGlobalHeader(bytes, ref offset, out globalVersion, out unusedGlobalCount); CheckVersion(globalVersion, methodToken); while (offset < bytes.Length) { byte version; CustomDebugInfoKind kind; int size; ReadRecordHeader(bytes, ref offset, out version, out kind, out size); CheckVersion(version, methodToken); if (kind == CustomDebugInfoKind.UsingInfo) { if (!groupSizes.IsDefault) { throw new InvalidOperationException(string.Format("Expected at most one Using record for method {0}", FormatMethodToken(methodToken))); } ReadUsingRecord(bytes, ref offset, size, out groupSizes); } else if (kind == CustomDebugInfoKind.ForwardInfo) { if (!externAliasStrings.IsDefault) { throw new InvalidOperationException(string.Format("Did not expect both Forward and ForwardToModule records for method {0}", FormatMethodToken(methodToken))); } ReadForwardRecord(bytes, ref offset, size, out methodToken); if (!seenForward) // Follow at most one forward link. { seenForward = true; goto RETRY; } } else if (kind == CustomDebugInfoKind.ForwardToModuleInfo) { if (!externAliasStrings.IsDefault) { throw new InvalidOperationException(string.Format("Expected at most one ForwardToModule record for method {0}", FormatMethodToken(methodToken))); } int moduleInfoMethodToken; ReadForwardToModuleRecord(bytes, ref offset, size, out moduleInfoMethodToken); ImmutableArray <string> allModuleInfoImportStrings = GetImportStrings(reader.GetBaselineMethod(moduleInfoMethodToken)); ArrayBuilder <string> externAliasBuilder = ArrayBuilder <string> .GetInstance(); foreach (string importString in allModuleInfoImportStrings) { if (IsCSharpExternAliasInfo(importString)) { externAliasBuilder.Add(importString); } } externAliasStrings = externAliasBuilder.ToImmutableAndFree(); } else { SkipRecord(bytes, ref offset, size); } } if (groupSizes.IsDefault) { throw new InvalidOperationException(string.Format("Didn't find usings info for method {0}", FormatMethodToken(methodToken))); } ImmutableArray <string> importStrings = GetImportStrings(reader.GetBaselineMethod(methodToken)); int numImportStrings = importStrings.Length; ArrayBuilder <ImmutableArray <string> > resultBuilder = ArrayBuilder <ImmutableArray <string> > .GetInstance(groupSizes.Length); ArrayBuilder <string> groupBuilder = ArrayBuilder <string> .GetInstance(); int pos = 0; foreach (short groupSize in groupSizes) { for (int i = 0; i < groupSize; i++, pos++) { if (pos >= numImportStrings) { throw new InvalidOperationException(string.Format("Group size indicates more imports than there are import strings (method {0}).", FormatMethodToken(methodToken))); } string importString = importStrings[pos]; if (IsCSharpExternAliasInfo(importString)) { throw new InvalidOperationException(string.Format("Encountered extern alias info before all import strings were consumed (method {0}).", FormatMethodToken(methodToken))); } groupBuilder.Add(importString); } resultBuilder.Add(groupBuilder.ToImmutable()); groupBuilder.Clear(); } if (externAliasStrings.IsDefault) { Debug.Assert(groupBuilder.Count == 0); // Extern alias detail strings (prefix "Z") are not included in the group counts. for (; pos < numImportStrings; pos++) { string importString = importStrings[pos]; if (!IsCSharpExternAliasInfo(importString)) { throw new InvalidOperationException(string.Format("Expected only extern alias info strings after consuming the indicated number of imports (method {0}).", FormatMethodToken(methodToken))); } groupBuilder.Add(importString); } externAliasStrings = groupBuilder.ToImmutableAndFree(); } else { groupBuilder.Free(); if (pos < numImportStrings) { throw new InvalidOperationException(string.Format("Group size indicates fewer imports than there are import strings (method {0}).", FormatMethodToken(methodToken))); } } return(resultBuilder.ToImmutableAndFree()); }
private ParameterInfo[] ParseParameterList() { // Consume the opening parenthesis or bracket Debug.Assert(PeekNextChar() == '(' || PeekNextChar() == '['); ++_index; var nextChar = PeekNextChar(); if (nextChar == ')' || nextChar == ']') { // Empty parameter list ++_index; return s_noParameters; } var builder = new ArrayBuilder<ParameterInfo>(); while (true) { var parameter = ParseParameter(); if (parameter != null) { builder.Add(parameter.Value); } else { builder.Free(); return null; } if (PeekNextChar() == ',') { ++_index; } else { break; } } nextChar = PeekNextChar(); if (nextChar == ')' || nextChar == ']') { // Consume the closing parenthesis or bracket ++_index; } else { // Malformed parameter list: missing close parenthesis or bracket builder.Free(); return null; } return builder.ToArrayAndFree(); }
/// <summary> /// The strategy of this rewrite is to do rewrite "locally". /// We analyze arguments of the concat in a shallow fashion assuming that /// lowering and optimizations (including this one) is already done for the arguments. /// Based on the arguments we select the most appropriate pattern for the current node. /// /// NOTE: it is not guaranteed that the node that we chose will be the most optimal since we have only /// local information - i.e. we look at the arguments, but we do not know about siblings. /// When we move to the parent, the node may be rewritten by this or some another optimization. /// /// Example: /// result = ( "abc" + "def" + null ?? expr1 + "moo" + "baz" ) + expr2 /// /// Will rewrite into: /// result = Concat("abcdef", expr2) /// /// However there will be transient nodes like Concat(expr1 + "moo") that will not be present in the /// resulting tree. /// /// </summary> private BoundExpression RewriteStringConcatenation(SyntaxNode syntax, BinaryOperatorKind operatorKind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol type) { Debug.Assert( operatorKind == BinaryOperatorKind.StringConcatenation || operatorKind == BinaryOperatorKind.StringAndObjectConcatenation || operatorKind == BinaryOperatorKind.ObjectAndStringConcatenation); if (_inExpressionLambda) { return(RewriteStringConcatInExpressionLambda(syntax, operatorKind, loweredLeft, loweredRight, type)); } // Convert both sides to a string (calling ToString if necessary) loweredLeft = ConvertConcatExprToString(syntax, loweredLeft); loweredRight = ConvertConcatExprToString(syntax, loweredRight); Debug.Assert(loweredLeft.Type.IsStringType() || loweredLeft.ConstantValue?.IsNull == true || loweredLeft.Type.IsErrorType()); Debug.Assert(loweredRight.Type.IsStringType() || loweredRight.ConstantValue?.IsNull == true || loweredRight.Type.IsErrorType()); // try fold two args without flattening. var folded = TryFoldTwoConcatOperands(syntax, loweredLeft, loweredRight); if (folded != null) { return(folded); } // flatten and merge - ( expr1 + "A" ) + ("B" + expr2) ===> (expr1 + "AB" + expr2) ArrayBuilder <BoundExpression> leftFlattened = ArrayBuilder <BoundExpression> .GetInstance(); ArrayBuilder <BoundExpression> rightFlattened = ArrayBuilder <BoundExpression> .GetInstance(); FlattenConcatArg(loweredLeft, leftFlattened); FlattenConcatArg(loweredRight, rightFlattened); if (leftFlattened.Any() && rightFlattened.Any()) { folded = TryFoldTwoConcatOperands(syntax, leftFlattened.Last(), rightFlattened.First()); if (folded != null) { rightFlattened[0] = folded; leftFlattened.RemoveLast(); } } leftFlattened.AddRange(rightFlattened); rightFlattened.Free(); BoundExpression result; switch (leftFlattened.Count) { case 0: result = _factory.StringLiteral(string.Empty); break; case 1: // All code paths which reach here (through TryFoldTwoConcatOperands) have already called // RewriteStringConcatenationOneExpr if necessary result = leftFlattened[0]; break; case 2: var left = leftFlattened[0]; var right = leftFlattened[1]; result = RewriteStringConcatenationTwoExprs(syntax, left, right); break; case 3: { var first = leftFlattened[0]; var second = leftFlattened[1]; var third = leftFlattened[2]; result = RewriteStringConcatenationThreeExprs(syntax, first, second, third); } break; case 4: { var first = leftFlattened[0]; var second = leftFlattened[1]; var third = leftFlattened[2]; var fourth = leftFlattened[3]; result = RewriteStringConcatenationFourExprs(syntax, first, second, third, fourth); } break; default: result = RewriteStringConcatenationManyExprs(syntax, leftFlattened.ToImmutable()); break; } leftFlattened.Free(); return(result); }
private TypeInfo[] ParseTypeArgumentList(ISymbol bindingContext) { Debug.Assert(PeekNextChar() == '<'); ++_index; var builder = new ArrayBuilder<TypeInfo>(); while (true) { var type = ParseType(bindingContext); if (type == null) { builder.Free(); return null; } builder.Add(type.Value); if (PeekNextChar() == ',') { ++_index; } else { break; } } if (PeekNextChar() == '>') { ++_index; } else { builder.Free(); return null; } return builder.ToArrayAndFree(); }
/// <summary> /// In the expanded form of a compound assignment (or increment/decrement), the LHS appears multiple times. /// If we aren't careful, this can result in repeated side-effects. This creates (ordered) temps for all of the /// subexpressions that could result in side-effects and returns a side-effect-free expression that can be used /// in place of the LHS in the expanded form. /// </summary> /// <param name="originalLHS">The LHS sub-expression of the compound assignment (or increment/decrement).</param> /// <param name="stores">Populated with a list of assignment expressions that initialize the temporary locals.</param> /// <param name="temps">Populated with a list of temporary local symbols.</param> /// <param name="isDynamicAssignment">True if the compound assignment is a dynamic operation.</param> /// <returns> /// A side-effect-free expression representing the LHS. /// The returned node needs to be lowered but its children are already lowered. /// </returns> private BoundExpression TransformCompoundAssignmentLHS(BoundExpression originalLHS, ArrayBuilder <BoundExpression> stores, ArrayBuilder <LocalSymbol> temps, bool isDynamicAssignment) { // There are five possible cases. // // Case 1: receiver.Prop += value is transformed into // temp = receiver // temp.Prop = temp.Prop + value // and a later rewriting will turn that into calls to getters and setters. // // Case 2: collection[i1, i2, i3] += value is transformed into // tc = collection // t1 = i1 // t2 = i2 // t3 = i3 // tc[t1, t2, t3] = tc[t1, t2, t3] + value // and again, a later rewriting will turn that into getters and setters of the indexer. // // Case 3: local += value (and param += value) needs no temporaries; it simply // becomes local = local + value. // // Case 4: staticField += value needs no temporaries either. However, classInst.field += value becomes // temp = classInst // temp.field = temp.field + value // // Case 5: otherwise, it must be structVariable.field += value or array[index] += value. Either way // we have a variable on the left. Transform it into: // ref temp = ref variable // temp = temp + value switch (originalLHS.Kind) { case BoundKind.PropertyAccess: { // We need to stash away the receiver so that it does not get evaluated twice. // If the receiver is classified as a value of reference type then we can simply say // // R temp = receiver // temp.prop = temp.prop + rhs // // But if the receiver is classified as a variable of struct type then we // cannot make a copy of the value; we need to make sure that we mutate // the original receiver, not the copy. We have to generate // // ref R temp = ref receiver // temp.prop = temp.prop + rhs // // The rules of C# (in section 7.17.1) require that if you have receiver.prop // as the target of an assignment such that receiver is a value type, it must // be classified as a variable. If we've gotten this far in the rewriting, // assume that was the case. var prop = (BoundPropertyAccess)originalLHS; // If the property is static or is the receiver is of kind "Base" or "this", then we can just generate prop = prop + value if (prop.ReceiverOpt == null || prop.PropertySymbol.IsStatic || !NeedsTemp(prop.ReceiverOpt)) { return(prop); } Debug.Assert(prop.ReceiverOpt.Kind != BoundKind.TypeExpression); // Can we ever avoid storing the receiver in a temp? If the receiver is a variable then it // might be modified by the computation of the getter, the value, or the operation. // The receiver cannot be a null constant or constant of value type. It could be a // constant of string type, but there are no mutable properties of a string. // Similarly, there are no mutable properties of a Type object, so the receiver // cannot be a typeof(T) expression. The only situation we know of is where we could // optimize away the temp is if the receiver is a readonly field of reference type, // we are not in a constructor, and the receiver of the *field*, if any, is also idempotent. // It doesn't seem worthwhile to pursue an optimization for this exceedingly rare case. BoundExpression rewrittenReceiver = VisitExpression(prop.ReceiverOpt); if (rewrittenReceiver.Type.IsTypeParameter() && rewrittenReceiver.Type.IsReferenceType) { var memberContainingType = prop.PropertySymbol.ContainingType; // From the verifier prospective type parameters do not contain fields or methods. // the instance must be boxed/constrained to access the member even if known to be a reference // It makes sense to box reference receiver before storing into a temp - no need to box/constrain twice. rewrittenReceiver = BoxReceiver(rewrittenReceiver, memberContainingType); } BoundAssignmentOperator assignmentToTemp; var receiverTemp = this.factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp, refKind: rewrittenReceiver.Type.IsReferenceType ? RefKind.None : RefKind.Ref); stores.Add(assignmentToTemp); temps.Add(receiverTemp.LocalSymbol); // CONSIDER: this is a temporary object that will be rewritten away before this lowering completes. // Mitigation: this will only produce short-lived garbage for compound assignments and increments/decrements of properties. return(new BoundPropertyAccess(prop.Syntax, receiverTemp, prop.PropertySymbol, prop.ResultKind, prop.Type)); } case BoundKind.DynamicMemberAccess: { var memberAccess = (BoundDynamicMemberAccess)originalLHS; if (!NeedsTemp(memberAccess.Receiver)) { return(memberAccess); } // store receiver to temp: var rewrittenReceiver = VisitExpression(memberAccess.Receiver); BoundAssignmentOperator assignmentToTemp; var receiverTemp = this.factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp); stores.Add(assignmentToTemp); temps.Add(receiverTemp.LocalSymbol); return(new BoundDynamicMemberAccess(memberAccess.Syntax, receiverTemp, memberAccess.TypeArgumentsOpt, memberAccess.Name, memberAccess.Invoked, memberAccess.Indexed, memberAccess.Type)); } case BoundKind.IndexerAccess: { BoundIndexerAccess indexerAccess = (BoundIndexerAccess)originalLHS; var receiverOpt = indexerAccess.ReceiverOpt; Debug.Assert(receiverOpt != null); BoundExpression transformedReceiver; if (NeedsTemp(receiverOpt)) { BoundExpression rewrittenReceiver = VisitExpression(receiverOpt); if (rewrittenReceiver.Type.IsTypeParameter() && rewrittenReceiver.Type.IsReferenceType) { var memberContainingType = indexerAccess.Indexer.ContainingType; // From the verifier prospective type parameters do not contain fields or methods. // the instance must be boxed/constrained to access the member even if known to be a reference // It makes sense to box reference receiver before storing into a temp - no need to box/constrain twice. rewrittenReceiver = BoxReceiver(rewrittenReceiver, memberContainingType); } BoundAssignmentOperator assignmentToTemp; var receiverTemp = this.factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp, refKind: rewrittenReceiver.Type.IsReferenceType ? RefKind.None : RefKind.Ref); transformedReceiver = receiverTemp; stores.Add(assignmentToTemp); temps.Add(receiverTemp.LocalSymbol); } else { transformedReceiver = VisitExpression(receiverOpt); } // Dealing with the arguments is a bit tricky because they can be named out-of-order arguments; // we have to preserve both the source-code order of the side effects and the side effects // only being executed once. // // This is a subtly different problem than the problem faced by the conventional call // rewriter; with the conventional call rewriter we already know that the side effects // will only be executed once because the arguments are only being pushed on the stack once. // In a compound equality operator on an indexer the indices are placed on the stack twice. // That is to say, if you have: // // C().M(z : Z(), x : X(), y : Y()) // // then we can rewrite that into // // tempc = C() // tempz = Z() // tempc.M(X(), Y(), tempz) // // See, we can optimize away two of the temporaries, for x and y. But we cannot optimize away any of the // temporaries in // // C().Collection[z : Z(), x : X(), y : Y()] += 1; // // because we have to ensure not just that Z() happens first, but in additioan that X() and Y() are only // called once. We have to generate this as // // tempc = C().Collection // tempz = Z() // tempx = X() // tempy = Y() // tempc[tempx, tempy, tempz] = tempc[tempx, tempy, tempz] + 1; // // Fortunately arguments to indexers are never ref or out, so we don't need to worry about that. // However, we can still do the optimization where constants are not stored in // temporaries; if we have // // C().Collection[z : 123, y : Y(), x : X()] += 1; // // Then we can generate that as // // tempc = C().Collection // tempx = X() // tempy = Y() // tempc[tempx, tempy, 123] = tempc[tempx, tempy, 123] + 1; ImmutableArray <BoundExpression> rewrittenArguments = VisitList(indexerAccess.Arguments); CSharpSyntaxNode syntax = indexerAccess.Syntax; PropertySymbol indexer = indexerAccess.Indexer; ImmutableArray <RefKind> argumentRefKinds = indexerAccess.ArgumentRefKindsOpt; bool expanded = indexerAccess.Expanded; ImmutableArray <int> argsToParamsOpt = indexerAccess.ArgsToParamsOpt; ImmutableArray <ParameterSymbol> parameters = indexer.Parameters; BoundExpression[] actualArguments = new BoundExpression[parameters.Length]; // The actual arguments that will be passed; one actual argument per formal parameter. ArrayBuilder <BoundAssignmentOperator> storesToTemps = ArrayBuilder <BoundAssignmentOperator> .GetInstance(rewrittenArguments.Length); ArrayBuilder <RefKind> refKinds = ArrayBuilder <RefKind> .GetInstance(parameters.Length, RefKind.None); // Step one: Store everything that is non-trivial into a temporary; record the // stores in storesToTemps and make the actual argument a reference to the temp. // Do not yet attempt to deal with params arrays or optional arguments. BuildStoresToTemps(expanded, argsToParamsOpt, argumentRefKinds, rewrittenArguments, actualArguments, refKinds, storesToTemps); // Step two: If we have a params array, build the array and fill in the argument. if (expanded) { BoundExpression array = BuildParamsArray(syntax, indexer, argsToParamsOpt, rewrittenArguments, parameters, actualArguments[actualArguments.Length - 1]); BoundAssignmentOperator storeToTemp; var boundTemp = this.factory.StoreToTemp(array, out storeToTemp); stores.Add(storeToTemp); temps.Add(boundTemp.LocalSymbol); actualArguments[actualArguments.Length - 1] = boundTemp; } // Step three: Now fill in the optional arguments. (Dev11 uses the // getter for optional arguments in compound assignments.) var getMethod = indexer.GetOwnOrInheritedGetMethod(); Debug.Assert((object)getMethod != null); InsertMissingOptionalArguments(syntax, getMethod.Parameters, actualArguments); // For a call, step four would be to optimize away some of the temps. However, we need them all to prevent // duplicate side-effects, so we'll skip that step. if (indexer.ContainingType.IsComImport) { RewriteArgumentsForComCall(parameters, actualArguments, refKinds, temps); } rewrittenArguments = actualArguments.AsImmutableOrNull(); foreach (BoundAssignmentOperator tempAssignment in storesToTemps) { temps.Add(((BoundLocal)tempAssignment.Left).LocalSymbol); stores.Add(tempAssignment); } storesToTemps.Free(); argumentRefKinds = GetRefKindsOrNull(refKinds); refKinds.Free(); // CONSIDER: this is a temporary object that will be rewritten away before this lowering completes. // Mitigation: this will only produce short-lived garbage for compound assignments and increments/decrements of indexers. return(new BoundIndexerAccess( syntax, transformedReceiver, indexer, rewrittenArguments, default(ImmutableArray <string>), argumentRefKinds, false, default(ImmutableArray <int>), indexerAccess.Type)); } case BoundKind.Local: case BoundKind.Parameter: case BoundKind.ThisReference: // a special kind of parameter // No temporaries are needed. Just generate local = local + value return(originalLHS); case BoundKind.DeclarationExpression: var rewrittenDeclExpr = (BoundExpression)VisitDeclarationExpression((BoundDeclarationExpression)originalLHS); switch (rewrittenDeclExpr.Kind) { case BoundKind.Local: return(rewrittenDeclExpr); case BoundKind.Sequence: var sequence = (BoundSequence)rewrittenDeclExpr; stores.AddRange(sequence.SideEffects); temps.AddRange(sequence.Locals); if (sequence.Value.Kind == BoundKind.Local) { return(sequence.Value); } break; } throw ExceptionUtilities.Unreachable; case BoundKind.FieldAccess: { // * If the field is static then no temporaries are needed. // * If the field is not static and the receiver is of reference type then generate t = r; t.f = t.f + value // * If the field is not static and the receiver is a variable of value type then we'll fall into the // general variable case below. var fieldAccess = (BoundFieldAccess)originalLHS; BoundExpression receiverOpt = fieldAccess.ReceiverOpt; //If the receiver is static or is the receiver is of kind "Base" or "this", then we can just generate field = field + value if (receiverOpt == null || fieldAccess.FieldSymbol.IsStatic || !NeedsTemp(receiverOpt)) { return(fieldAccess); } if (receiverOpt.Type.IsReferenceType) { Debug.Assert(receiverOpt.Kind != BoundKind.TypeExpression); BoundExpression rewrittenReceiver = VisitExpression(receiverOpt); if (rewrittenReceiver.Type.IsTypeParameter()) { var memberContainingType = fieldAccess.FieldSymbol.ContainingType; // From the verifier prospective type parameters do not contain fields or methods. // the instance must be "boxed" to access the field // It makes sense to box receiver before storing into a temp - no need to box twice. rewrittenReceiver = BoxReceiver(rewrittenReceiver, memberContainingType); } BoundAssignmentOperator assignmentToTemp; var receiverTemp = this.factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp); stores.Add(assignmentToTemp); temps.Add(receiverTemp.LocalSymbol); return(new BoundFieldAccess(fieldAccess.Syntax, receiverTemp, fieldAccess.FieldSymbol, null)); } break; } case BoundKind.DynamicIndexerAccess: { var indexerAccess = (BoundDynamicIndexerAccess)originalLHS; BoundExpression loweredReceiver; if (NeedsTemp(indexerAccess.ReceiverOpt)) { BoundAssignmentOperator assignmentToTemp; var temp = this.factory.StoreToTemp(VisitExpression(indexerAccess.ReceiverOpt), out assignmentToTemp); stores.Add(assignmentToTemp); temps.Add(temp.LocalSymbol); loweredReceiver = temp; } else { loweredReceiver = indexerAccess.ReceiverOpt; } var arguments = indexerAccess.Arguments; var loweredArguments = new BoundExpression[arguments.Length]; for (int i = 0; i < arguments.Length; i++) { if (NeedsTemp(arguments[i])) { BoundAssignmentOperator assignmentToTemp; var temp = this.factory.StoreToTemp(VisitExpression(arguments[i]), out assignmentToTemp, refKind: indexerAccess.ArgumentRefKindsOpt.RefKinds(i)); stores.Add(assignmentToTemp); temps.Add(temp.LocalSymbol); loweredArguments[i] = temp; } else { loweredArguments[i] = arguments[i]; } } return(new BoundDynamicIndexerAccess( indexerAccess.Syntax, loweredReceiver, loweredArguments.AsImmutableOrNull(), indexerAccess.ArgumentNamesOpt, indexerAccess.ArgumentRefKindsOpt, indexerAccess.ApplicableIndexers, indexerAccess.Type)); } case BoundKind.ArrayAccess: if (isDynamicAssignment) { // In non-dynamic array[index] op= R we emit: // T& tmp = &array[index]; // *tmp = *L op R; // where T is the type of L. // // If L is an array access, the assignment is dynamic, the compile-time of the array is dynamic[] // and the runtime type of the array is not object[] (but e.g. string[]) the pointer approach is broken. // T is Object in such case and we can't take a read-write pointer of type Object& to an array element of non-object type. // // In this case we rewrite the assignment as follows: // // E t_array = array; // I t_index = index; (possibly more indices) // T value = t_array[t_index]; // t_array[t_index] = value op R; var arrayAccess = (BoundArrayAccess)originalLHS; var loweredArray = VisitExpression(arrayAccess.Expression); var loweredIndices = VisitList(arrayAccess.Indices); return(SpillArrayElementAccess(loweredArray, loweredIndices, stores, temps)); } break; case BoundKind.PointerElementAccess: case BoundKind.PointerIndirectionOperator: case BoundKind.RefValueOperator: break; default: throw ExceptionUtilities.UnexpectedValue(originalLHS.Kind); } // We made no transformation above. Either we have array[index] += value or // structVariable.field += value; either way we have a potentially complicated variable- // producing expression on the left. Generate // ref temp = ref variable; temp = temp + value // Rewrite the variable. Here we depend on the fact that the only forms // rewritten here are rewritten the same for lvalues and rvalues. BoundExpression rewrittenVariable = VisitExpression(originalLHS); BoundAssignmentOperator assignmentToTemp2; var variableTemp = this.factory.StoreToTemp(rewrittenVariable, out assignmentToTemp2, refKind: RefKind.Ref); stores.Add(assignmentToTemp2); temps.Add(variableTemp.LocalSymbol); return(variableTemp); }
private IEnumerable<CSharpAttributeData> GetCustomAttributesToEmitIterator( ImmutableArray<CSharpAttributeData> userDefined, ArrayBuilder<SynthesizedAttributeData> synthesized, bool isReturnType, bool emittingAssemblyAttributesInNetModule) { CheckDefinitionInvariant(); if (synthesized != null) { foreach (var attribute in synthesized) { // only synthesize attributes that are emitted: Debug.Assert(attribute.ShouldEmitAttribute(this, isReturnType, emittingAssemblyAttributesInNetModule)); yield return attribute; } synthesized.Free(); } for (int i = 0; i < userDefined.Length; i++) { CSharpAttributeData attribute = userDefined[i]; if (this.Kind == SymbolKind.Assembly) { // We need to filter out duplicate assembly attributes (i.e. attributes that // bind to the same constructor and have identical arguments) and invalid // InternalsVisibleTo attributes. if (((SourceAssemblySymbol)this).IsIndexOfOmittedAssemblyAttribute(i)) { continue; } } if (attribute.ShouldEmitAttribute(this, isReturnType, emittingAssemblyAttributesInNetModule)) { yield return attribute; } } }
public void Free() { _temps.Free(); _map.Free(); }
public void Free() { Operations?.Free(); }
internal void Free() { init.Free(); }
public void FreePooledObjects() { stack0.Free(); stack1.Free(); discriminatorStack.Free(); }
internal static SourceText ToSourceTextAndFree(ArrayBuilder<SourceText> segments, SourceText original, bool adjustSegments) { if (adjustSegments) { TrimInaccessibleText(segments); ReduceSegmentCountIfNecessary(segments); } if (segments.Count == 0) { segments.Free(); return SourceText.From(string.Empty, original.Encoding, original.ChecksumAlgorithm); } else if (segments.Count == 1) { SourceText result = segments[0]; segments.Free(); return result; } else { return new CompositeText(segments.ToImmutableAndFree(), original.Encoding, original.ChecksumAlgorithm); } }
internal static string GetSerializedTypeName(this ITypeReference typeReference, EmitContext context, ref bool isAssemblyQualified) { var pooled = PooledStringBuilder.GetInstance(); StringBuilder sb = pooled.Builder; IArrayTypeReference arrType = typeReference as IArrayTypeReference; if (arrType != null) { typeReference = arrType.GetElementType(context); bool isAssemQual = false; AppendSerializedTypeName(sb, typeReference, ref isAssemQual, context); if (arrType.IsSZArray) { sb.Append("[]"); } else { sb.Append('['); if (arrType.Rank == 1) { sb.Append('*'); } sb.Append(',', (int)arrType.Rank - 1); sb.Append(']'); } goto done; } IPointerTypeReference pointer = typeReference as IPointerTypeReference; if (pointer != null) { typeReference = pointer.GetTargetType(context); bool isAssemQual = false; AppendSerializedTypeName(sb, typeReference, ref isAssemQual, context); sb.Append('*'); goto done; } IManagedPointerTypeReference reference = typeReference as IManagedPointerTypeReference; if (reference != null) { typeReference = reference.GetTargetType(context); bool isAssemQual = false; AppendSerializedTypeName(sb, typeReference, ref isAssemQual, context); sb.Append('&'); goto done; } INamespaceTypeReference namespaceType = typeReference.AsNamespaceTypeReference; if (namespaceType != null) { var name = namespaceType.NamespaceName; if (name.Length != 0) { sb.Append(name); sb.Append('.'); } sb.Append(GetMangledAndEscapedName(namespaceType)); goto done; } if (typeReference.IsTypeSpecification()) { ITypeReference uninstantiatedTypeReference = typeReference.GetUninstantiatedGenericType(); ArrayBuilder <ITypeReference> consolidatedTypeArguments = ArrayBuilder <ITypeReference> .GetInstance(); typeReference.GetConsolidatedTypeArguments(consolidatedTypeArguments, context); bool uninstantiatedTypeIsAssemblyQualified = false; sb.Append(GetSerializedTypeName(uninstantiatedTypeReference, context, ref uninstantiatedTypeIsAssemblyQualified)); sb.Append('['); bool first = true; foreach (ITypeReference argument in consolidatedTypeArguments) { if (first) { first = false; } else { sb.Append(','); } bool isAssemQual = true; AppendSerializedTypeName(sb, argument, ref isAssemQual, context); } consolidatedTypeArguments.Free(); sb.Append(']'); goto done; } INestedTypeReference nestedType = typeReference.AsNestedTypeReference; if (nestedType != null) { bool nestedTypeIsAssemblyQualified = false; sb.Append(GetSerializedTypeName(nestedType.GetContainingType(context), context, ref nestedTypeIsAssemblyQualified)); sb.Append('+'); sb.Append(GetMangledAndEscapedName(nestedType)); goto done; } // TODO: error done: if (isAssemblyQualified) { AppendAssemblyQualifierIfNecessary(sb, UnwrapTypeReference(typeReference, context), out isAssemblyQualified, context); } return(pooled.ToStringAndFree()); }
private PatternMatch?NonFuzzyMatchPatternChunk( string candidate, TextChunk patternChunk, bool punctuationStripped) { var candidateLength = candidate.Length; var caseInsensitiveIndex = _compareInfo.IndexOf(candidate, patternChunk.Text, CompareOptions.IgnoreCase); if (caseInsensitiveIndex == 0) { // We found the pattern at the start of the candidate. This is either an exact or // prefix match. if (patternChunk.Text.Length == candidateLength) { // Lengths were the same, this is either a case insensitive or sensitive exact match. return(new PatternMatch( PatternMatchKind.Exact, punctuationStripped, isCaseSensitive: candidate == patternChunk.Text, matchedSpan: GetMatchedSpan(0, candidateLength))); } else { // Lengths were the same, this is either a case insensitive or sensitive prefix match. return(new PatternMatch( PatternMatchKind.Prefix, punctuationStripped, isCaseSensitive: _compareInfo.IsPrefix(candidate, patternChunk.Text), matchedSpan: GetMatchedSpan(0, patternChunk.Text.Length))); } } ArrayBuilder <TextSpan> candidateHumpsOpt = null; try { var patternIsLowercase = patternChunk.IsLowercase; if (caseInsensitiveIndex > 0) { // We found the pattern somewhere in the candidate. This could be a substring match. // However, we don't want to be overaggressive in returning just any substring results. // So do a few more checks to make sure this is a good result. if (!patternIsLowercase) { // Pattern contained uppercase letters. This is a strong indication from the // user that they expect the same letters to be uppercase in the result. As // such, only return this if we can find this pattern exactly in the candidate. var caseSensitiveIndex = _compareInfo.IndexOf(candidate, patternChunk.Text, CompareOptions.None); if (caseSensitiveIndex > 0) { if (char.IsUpper(candidate[caseInsensitiveIndex])) { return(new PatternMatch( PatternMatchKind.StartOfWordSubstring, punctuationStripped, isCaseSensitive: true, matchedSpan: GetMatchedSpan(caseInsensitiveIndex, patternChunk.Text.Length))); } else { return(new PatternMatch( PatternMatchKind.NonLowercaseSubstring, punctuationStripped, isCaseSensitive: true, matchedSpan: GetMatchedSpan(caseSensitiveIndex, patternChunk.Text.Length))); } } } else { // Pattern was all lowercase. This can lead to lots of hits. For example, "bin" in // "CombineUnits". Instead, we want it to match "Operator[|Bin|]ary" first rather than // Com[|bin|]eUnits // If the lowercase search string matched what looks to be the start of a word then that's a // reasonable hit. This is equivalent to 'bin' matching 'Operator[|Bin|]ary' if (char.IsUpper(candidate[caseInsensitiveIndex])) { return(new PatternMatch(PatternMatchKind.StartOfWordSubstring, punctuationStripped, isCaseSensitive: false, matchedSpan: GetMatchedSpan(caseInsensitiveIndex, patternChunk.Text.Length))); } // Now do the more expensive check to see if we're at the start of a word. This is to catch // word matches like CombineBinary. We want to find the hit against '[|Bin|]ary' not // 'Com[|bin|]e' candidateHumpsOpt = StringBreaker.GetWordParts(candidate); for (int i = 0, n = candidateHumpsOpt.Count; i < n; i++) { var hump = TextSpan.FromBounds(candidateHumpsOpt[i].Start, candidateLength); if (PartStartsWith(candidate, hump, patternChunk.Text, CompareOptions.IgnoreCase)) { return(new PatternMatch(PatternMatchKind.StartOfWordSubstring, punctuationStripped, isCaseSensitive: PartStartsWith(candidate, hump, patternChunk.Text, CompareOptions.None), matchedSpan: GetMatchedSpan(hump.Start, patternChunk.Text.Length))); } } } } // Didn't have an exact/prefix match, or a high enough quality substring match. // See if we can find a camel case match. if (candidateHumpsOpt == null) { candidateHumpsOpt = StringBreaker.GetWordParts(candidate); } // Didn't have an exact/prefix match, or a high enough quality substring match. // See if we can find a camel case match. var match = TryCamelCaseMatch(candidate, patternChunk, punctuationStripped, patternIsLowercase, candidateHumpsOpt); if (match != null) { return(match); } // If pattern was all lowercase, we allow it to match an all lowercase section of the candidate. But // only after we've tried all other forms first. This is the weakest of all matches. For example, if // user types 'bin' we want to match 'OperatorBinary' (start of word) or 'BinaryInformationNode' (camel // humps) before matching 'Combine'. // // We only do this for strings longer than three characters to avoid too many false positives when the // user has only barely started writing a word. if (patternIsLowercase && caseInsensitiveIndex > 0 && patternChunk.Text.Length >= 3) { var caseSensitiveIndex = _compareInfo.IndexOf(candidate, patternChunk.Text, CompareOptions.None); if (caseSensitiveIndex > 0) { return(new PatternMatch( PatternMatchKind.LowercaseSubstring, punctuationStripped, isCaseSensitive: true, matchedSpan: GetMatchedSpan(caseSensitiveIndex, patternChunk.Text.Length))); } } return(null); } finally { candidateHumpsOpt?.Free(); } }
internal void Free() { ArrayBuilder <V> arrayBuilder = _value as ArrayBuilder <V>; arrayBuilder?.Free(); }
private void GetFrameName( DkmInspectionContext inspectionContext, DkmWorkList workList, DkmStackWalkFrame frame, DkmVariableInfoFlags argumentFlags, DkmCompletionRoutine <DkmGetFrameNameAsyncResult> completionRoutine, TMethodSymbol method) { var includeParameterTypes = argumentFlags.Includes(DkmVariableInfoFlags.Types); var includeParameterNames = argumentFlags.Includes(DkmVariableInfoFlags.Names); if (argumentFlags.Includes(DkmVariableInfoFlags.Values)) { // No need to compute the Expandable bit on // argument values since that can be expensive. inspectionContext = DkmInspectionContext.Create( inspectionContext.InspectionSession, inspectionContext.RuntimeInstance, inspectionContext.Thread, inspectionContext.Timeout, inspectionContext.EvaluationFlags | DkmEvaluationFlags.NoExpansion, inspectionContext.FuncEvalFlags, inspectionContext.Radix, inspectionContext.Language, inspectionContext.ReturnValue, inspectionContext.AdditionalVisualizationData, inspectionContext.AdditionalVisualizationDataPriority, inspectionContext.ReturnValues); // GetFrameArguments returns an array of formatted argument values. We'll pass // ourselves (GetFrameName) as the continuation of the GetFrameArguments call. inspectionContext.GetFrameArguments( workList, frame, result => { // DkmGetFrameArgumentsAsyncResult.Arguments will throw if ErrorCode != 0. var argumentValues = (result.ErrorCode == 0) ? result.Arguments : null; try { ArrayBuilder <string> builder = null; if (argumentValues != null) { builder = ArrayBuilder <string> .GetInstance(); foreach (var argument in argumentValues) { var formattedArgument = argument as DkmSuccessEvaluationResult; // Not expecting Expandable bit, at least not from this EE. Debug.Assert((formattedArgument == null) || (formattedArgument.Flags & DkmEvaluationResultFlags.Expandable) == 0); builder.Add(formattedArgument?.Value); } } var frameName = _instructionDecoder.GetName(method, includeParameterTypes, includeParameterNames, argumentValues: builder); builder?.Free(); completionRoutine(new DkmGetFrameNameAsyncResult(frameName)); } catch (Exception e) { completionRoutine(DkmGetFrameNameAsyncResult.CreateErrorResult(e)); } finally { if (argumentValues != null) { foreach (var argument in argumentValues) { argument.Close(); } } } }); } else { var frameName = _instructionDecoder.GetName(method, includeParameterTypes, includeParameterNames, argumentValues: null); completionRoutine(new DkmGetFrameNameAsyncResult(frameName)); } }
private PatternMatch?NonFuzzyMatchPatternChunk( string candidate, TextChunk patternChunk, bool punctuationStripped) { var candidateLength = candidate.Length; var caseInsensitiveIndex = _compareInfo.IndexOf(candidate, patternChunk.Text, CompareOptions.IgnoreCase); if (caseInsensitiveIndex == 0) { // We found the pattern at the start of the candidate. This is either an exact or // prefix match. if (patternChunk.Text.Length == candidateLength) { // Lengths were the same, this is either a case insensitive or sensitive exact match. return(new PatternMatch( PatternMatchKind.Exact, punctuationStripped, isCaseSensitive: candidate == patternChunk.Text, matchedSpan: GetMatchedSpan(0, candidateLength))); } else { // Lengths were the same, this is either a case insensitive or sensitive prefix match. return(new PatternMatch( PatternMatchKind.Prefix, punctuationStripped, isCaseSensitive: _compareInfo.IsPrefix(candidate, patternChunk.Text), matchedSpan: GetMatchedSpan(0, patternChunk.Text.Length))); } } ArrayBuilder <TextSpan> candidateHumpsOpt = null; try { var patternIsLowercase = patternChunk.IsLowercase; if (caseInsensitiveIndex > 0) { // We found the pattern somewhere in the candidate. This could be a substring match. // However, we don't want to be overaggressive in returning just any substring results. // So do a few more checks to make sure this is a good result. if (!patternIsLowercase) { // Pattern contained uppercase letters. This is a strong indication from the // user that they expect the same letters to be uppercase in the result. As // such, only return this if we can find this pattern exactly in the candidate. var caseSensitiveIndex = _compareInfo.IndexOf(candidate, patternChunk.Text, CompareOptions.None); if (caseSensitiveIndex > 0) { return(new PatternMatch( PatternMatchKind.Substring, punctuationStripped, isCaseSensitive: true, matchedSpan: GetMatchedSpan(caseSensitiveIndex, patternChunk.Text.Length))); } } else { // Pattern was all lowercase. This can lead to lots of false positives. For // example, we don't want "bin" to match "CombineUnits". Instead, we want it // to match "BinaryOperator". As such, make sure our match looks like it's // starting an actual word in the candidate. // Do a quick check to avoid the expensive work of having to go get the candidate // humps. if (char.IsUpper(candidate[caseInsensitiveIndex])) { return(new PatternMatch(PatternMatchKind.Substring, punctuationStripped, isCaseSensitive: false, matchedSpan: GetMatchedSpan(caseInsensitiveIndex, patternChunk.Text.Length))); } candidateHumpsOpt = StringBreaker.GetWordParts(candidate); for (int i = 0, n = candidateHumpsOpt.Count; i < n; i++) { var hump = TextSpan.FromBounds(candidateHumpsOpt[i].Start, candidateLength); if (PartStartsWith(candidate, hump, patternChunk.Text, CompareOptions.IgnoreCase)) { return(new PatternMatch(PatternMatchKind.Substring, punctuationStripped, isCaseSensitive: PartStartsWith(candidate, hump, patternChunk.Text, CompareOptions.None), matchedSpan: GetMatchedSpan(hump.Start, patternChunk.Text.Length))); } } } } // Didn't have an exact/prefix match, or a high enough quality substring match. // See if we can find a camel case match. if (candidateHumpsOpt == null) { candidateHumpsOpt = StringBreaker.GetWordParts(candidate); } // Didn't have an exact/prefix match, or a high enough quality substring match. // See if we can find a camel case match. return(TryCamelCaseMatch( candidate, patternChunk, punctuationStripped, patternIsLowercase, candidateHumpsOpt)); } finally { candidateHumpsOpt?.Free(); } }
/// <summary> /// At this point, we have a list of viable symbols and no parameter list with which to perform /// overload resolution. We'll just return the first symbol, giving a diagnostic if there are /// others. /// Caveat: If there are multiple candidates and only one is from source, then the source symbol /// wins and no diagnostic is reported. /// </summary> private ImmutableArray <Symbol> ProcessParameterlessCrefMemberLookupResults( ImmutableArray <Symbol> symbols, int arity, MemberCrefSyntax memberSyntax, TypeArgumentListSyntax typeArgumentListSyntax, out Symbol ambiguityWinner, DiagnosticBag diagnostics) { // If the syntax indicates arity zero, then we match methods of any arity. // However, if there are both generic and non-generic methods, then the // generic methods should be ignored. if (symbols.Length > 1 && arity == 0) { bool hasNonGenericMethod = false; bool hasGenericMethod = false; foreach (Symbol s in symbols) { if (s.Kind != SymbolKind.Method) { continue; } if (((MethodSymbol)s).Arity == 0) { hasNonGenericMethod = true; } else { hasGenericMethod = true; } if (hasGenericMethod && hasNonGenericMethod) { break; //Nothing else to be learned. } } if (hasNonGenericMethod && hasGenericMethod) { symbols = symbols.WhereAsArray(s => s.Kind != SymbolKind.Method || ((MethodSymbol)s).Arity == 0); } } Debug.Assert(!symbols.IsEmpty); Symbol symbol = symbols[0]; // If there's ambiguity, prefer source symbols. // Logic is similar to ResultSymbol, but separate because the error handling is totally different. if (symbols.Length > 1) { // Size is known, but IndexOfSymbolFromCurrentCompilation expects a builder. ArrayBuilder <Symbol> unwrappedSymbols = ArrayBuilder <Symbol> .GetInstance(symbols.Length); foreach (Symbol wrapped in symbols) { unwrappedSymbols.Add(UnwrapAliasNoDiagnostics(wrapped)); } BestSymbolInfo secondBest; BestSymbolInfo best = GetBestSymbolInfo(unwrappedSymbols, out secondBest); Debug.Assert(!best.IsNone); Debug.Assert(!secondBest.IsNone); unwrappedSymbols.Free(); int symbolIndex = 0; if (best.IsFromCompilation) { symbolIndex = best.Index; symbol = symbols[symbolIndex]; // NOTE: symbols, not unwrappedSymbols. } if (symbol.Kind == SymbolKind.TypeParameter) { CrefSyntax crefSyntax = GetRootCrefSyntax(memberSyntax); diagnostics.Add(ErrorCode.WRN_BadXMLRefTypeVar, crefSyntax.Location, crefSyntax.ToString()); } else if (secondBest.IsFromCompilation == best.IsFromCompilation) { CrefSyntax crefSyntax = GetRootCrefSyntax(memberSyntax); int otherIndex = symbolIndex == 0 ? 1 : 0; diagnostics.Add(ErrorCode.WRN_AmbiguousXMLReference, crefSyntax.Location, crefSyntax.ToString(), symbol, symbols[otherIndex]); ambiguityWinner = ConstructWithCrefTypeParameters(arity, typeArgumentListSyntax, symbol); return(symbols.SelectAsArray(sym => ConstructWithCrefTypeParameters(arity, typeArgumentListSyntax, sym))); } } else if (symbol.Kind == SymbolKind.TypeParameter) { CrefSyntax crefSyntax = GetRootCrefSyntax(memberSyntax); diagnostics.Add(ErrorCode.WRN_BadXMLRefTypeVar, crefSyntax.Location, crefSyntax.ToString()); } ambiguityWinner = null; return(ImmutableArray.Create <Symbol>(ConstructWithCrefTypeParameters(arity, typeArgumentListSyntax, symbol))); }