示例#1
0
        private string FinishFixingName(string name)
        {
            // Edge case: prefix "as", suffix "sa", name "asa"
            if (Suffix.Length + Prefix.Length >= name.Length)
            {
                return(name);
            }

            name = name.Substring(Prefix.Length, name.Length - Suffix.Length - Prefix.Length);
            IEnumerable <string> words = new[] { name };

            if (!string.IsNullOrEmpty(WordSeparator))
            {
                words = name.Split(new[] { WordSeparator }, StringSplitOptions.RemoveEmptyEntries);

                if (words.Count() == 1) // Only Split if words have not been split before
                {
                    bool     isWord   = true;
                    var      parts    = StringBreaker.GetParts(name, isWord);
                    string[] newWords = new string[parts.Count];
                    for (int i = 0; i < parts.Count; i++)
                    {
                        newWords[i] = name.Substring(parts[i].Start, parts[i].End - parts[i].Start);
                    }
                    words = newWords;
                }
            }

            words = ApplyCapitalization(words);

            return(Prefix + string.Join(WordSeparator, words) + Suffix);
        }
示例#2
0
 public TextChunk(string text, bool allowFuzzingMatching)
 {
     this.Text              = text;
     this.CharacterSpans    = StringBreaker.BreakIntoCharacterParts(text);
     this.SimilarityChecker = allowFuzzingMatching
         ? WordSimilarityChecker.Allocate(text, substringsAreSimilar: false)
         : null;
 }
            internal static ImmutableArray <Words> GetBaseNames(ITypeSymbol type, bool pluralize)
            {
                var baseName = TryRemoveInterfacePrefix(type);
                var parts    = StringBreaker.GetWordParts(baseName);
                var result   = GetInterleavedPatterns(parts, baseName, pluralize);

                parts.Free();
                return(result);
            }
示例#4
0
            internal static ImmutableArray <IEnumerable <string> > GetBaseNames(ITypeSymbol type)
            {
                var baseName = TryRemoveInterfacePrefix(type);

                using (var breaks = StringBreaker.BreakIntoWordParts(baseName))
                {
                    return(GetInterleavedPatterns(breaks, baseName));
                }
            }
示例#5
0
        public static IdentifierNameParts CreateIdentifierNameParts(ISymbol symbol, ImmutableArray <NamingRule> rules)
        {
            var baseName = RemovePrefixesAndSuffixes(symbol, rules, symbol.Name);

            var parts = StringBreaker.GetWordParts(baseName);
            var words = CreateWords(parts, baseName);

            return(new IdentifierNameParts(baseName, words));
        }
            internal static ImmutableArray <Words> GetBaseNames(ITypeSymbol type, bool pluralize)
            {
                var baseName = TryRemoveInterfacePrefix(type);

                using var parts = TemporaryArray <TextSpan> .Empty;
                StringBreaker.AddWordParts(baseName, ref parts.AsRef());
                var result = GetInterleavedPatterns(parts, baseName, pluralize);

                return(result);
            }
        public static IdentifierNameParts CreateIdentifierNameParts(ISymbol symbol, ImmutableArray <NamingRule> rules)
        {
            var baseName = RemovePrefixesAndSuffixes(symbol, rules, symbol.Name);

            using var parts = TemporaryArray <TextSpan> .Empty;
            StringBreaker.AddWordParts(baseName, ref parts.AsRef());
            var words = CreateWords(parts, baseName);

            return(new IdentifierNameParts(baseName, words));
        }
            public TextChunk(string text, bool allowFuzzingMatching)
            {
                this.Text              = text;
                this.PatternHumps      = StringBreaker.GetCharacterParts(text);
                this.SimilarityChecker = allowFuzzingMatching
                    ? WordSimilarityChecker.Allocate(text, substringsAreSimilar: false)
                    : null;

                IsLowercase = !ContainsUpperCaseLetter(text);
            }
            public TextChunk(string text, bool allowFuzzingMatching)
            {
                this.Text    = text;
                PatternHumps = TemporaryArray <TextSpan> .Empty;
                StringBreaker.AddCharacterParts(text, ref PatternHumps);

                this.SimilarityChecker = allowFuzzingMatching
                    ? WordSimilarityChecker.Allocate(text, substringsAreSimilar: false)
                    : null;

                IsLowercase = !ContainsUpperCaseLetter(text);
            }
示例#10
0
 public void REMCommentsAreIdentified()
 {
     Assert.Equal(
         new IToken[]
     {
         new CommentToken(" Test", 0),
         new UnprocessedContentToken("WScript.Echo 1", 1)
     },
         StringBreaker.SegmentString(
             "REM Test\nWScript.Echo 1"
             ),
         new TokenSetComparer()
         );
 }
示例#11
0
 public void NonLineReturningWhiteSpaceBetweenCommentsIsIgnored()
 {
     Assert.Equal(
         new IToken[]
     {
         new CommentToken(" Comment 1", 0),
         new CommentToken(" Comment 2", 1)
     },
         StringBreaker.SegmentString(
             "' Comment 1\n ' Comment 2"
             ),
         new TokenSetComparer()
         );
 }
示例#12
0
 public void EmptyContentEscapedVariableNameIsSetToNumericValue()
 {
     Assert.Equal(
         new IToken[]
     {
         new EscapedNameToken("[]", 0),
         new UnprocessedContentToken(" = 1", 0)
     },
         StringBreaker.SegmentString(
             "[] = 1"
             ),
         new TokenSetComparer()
         );
 }
示例#13
0
 public void VariableSetToStringContentIncludedQuotedContent()
 {
     Assert.Equal(
         new IToken[]
     {
         new UnprocessedContentToken("strValue = ", 0),
         new StringToken("Test string with \"quoted\" content", 0),
         new UnprocessedContentToken("\n", 0)
     },
         StringBreaker.SegmentString(
             "strValue = \"Test string with \"\"quoted\"\" content\"\n"
             ),
         new TokenSetComparer()
         );
 }
示例#14
0
        public static string GetLocalName(this INamedTypeSymbol containingType)
        {
            var parts = StringBreaker.BreakIntoWordParts(containingType.Name);

            for (var i = parts.Count - 1; i >= 0; i--)
            {
                var p = parts[i];
                if (char.IsLetter(containingType.Name[p.Start]))
                {
                    return(containingType.Name.Substring(p.Start, p.Length).ToCamelCase());
                }
            }

            return("v");
        }
示例#15
0
 public void InlineREMCommentsAreIdentified()
 {
     Assert.Equal(
         new IToken[]
     {
         new UnprocessedContentToken("WScript.Echo 1", 0),
         new EndOfStatementSameLineToken(0),
         new InlineCommentToken(" Test", 0)
     },
         StringBreaker.SegmentString(
             "WScript.Echo 1 REM Test"
             ),
         new TokenSetComparer()
         );
 }
            internal static ImmutableArray <Words> GetBaseNames(IAliasSymbol alias)
            {
                var name = alias.Name;

                if (alias.Target.IsType && (((INamedTypeSymbol)alias.Target).IsInterfaceType() &&
                                            CanRemoveInterfacePrefix(name)))
                {
                    name = name.Substring(1);
                }

                var breaks = StringBreaker.GetWordParts(name);
                var result = GetInterleavedPatterns(breaks, name, pluralize: false);

                breaks.Free();
                return(result);
            }
示例#17
0
 public void WhitespaceBetweenStringTokenAndCommentDoesNotPreventEndOfStatementBeingInserted()
 {
     Assert.Equal(
         new IToken[]
     {
         new UnprocessedContentToken("a = ", 0),
         new StringToken("", 0),
         new EndOfStatementSameLineToken(0),
         new CommentToken(" Comment", 0)
     },
         StringBreaker.SegmentString(
             "a = \"\" ' Comment"
             ),
         new TokenSetComparer()
         );
 }
            internal static ImmutableArray <Words> GetBaseNames(IAliasSymbol alias)
            {
                var name = alias.Name;

                if (alias.Target.IsType &&
                    ((INamedTypeSymbol)alias.Target).IsInterfaceType() &&
                    CanRemoveInterfacePrefix(name))
                {
                    name = name.Substring(1);
                }

                using var breaks = TemporaryArray <TextSpan> .Empty;
                StringBreaker.AddWordParts(name, ref breaks.AsRef());
                var result = GetInterleavedPatterns(breaks, name, pluralize: false);

                return(result);
            }
示例#19
0
 public void InlineCommentsAreIdentifiedAsSuchWhenAfterMultipleLinesOfContent()
 {
     // The StringBreaker will insert an EndOfStatementSameLineToken between the UnprocessedContentToken and InlineCommentToken
     // since that the later processes rely on end-of-statement tokens, even before an inline comment
     Assert.Equal(
         new IToken[]
     {
         new UnprocessedContentToken("\nWScript.Echo 1", 0),
         new EndOfStatementSameLineToken(0),
         new InlineCommentToken(" Test", 1)
     },
         StringBreaker.SegmentString(
             "\nWScript.Echo 1 ' Test"
             ),
         new TokenSetComparer()
         );
 }
        private static IEnumerable <IToken> GetTokens(string scriptContent)
        {
            // Break down content into String, Comment and UnprocessedContent tokens
            var tokens = StringBreaker.SegmentString(scriptContent);

            // Break down further into String, Comment, Atom and AbstractEndOfStatement tokens
            var atomTokens = new List <IToken>();

            foreach (var token in tokens)
            {
                if (token is UnprocessedContentToken)
                {
                    atomTokens.AddRange(TokenBreaker.BreakUnprocessedToken((UnprocessedContentToken)token));
                }
                else
                {
                    atomTokens.Add(token);
                }
            }

            return(NumberRebuilder.Rebuild(OperatorCombiner.Combine(atomTokens)).ToList());
        }
示例#21
0
        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>
 /// Get the individual words in the parameter name.  This way we can generate
 /// appropriate field/property names based on the user's preference.
 /// </summary>
 private List <string> GetParameterWordParts(IParameterSymbol parameter)
 => CreateWords(StringBreaker.BreakIntoWordParts(parameter.Name), parameter.Name);
示例#23
0
        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();
            }
        }
示例#24
0
 private static IList <string> BreakIntoCharacterParts(string identifier)
 => PartListToSubstrings(identifier, StringBreaker.BreakIntoCharacterParts(identifier));
示例#25
0
 private static IList <string> BreakIntoWordParts(string identifier)
 {
     return(PartListToSubstrings(identifier, StringBreaker.BreakIntoWordParts(identifier)));
 }
示例#26
0
        private static IList <SyntaxNode> CreateEqualsMethodStatements(
            ISyntaxFactoryService factory,
            Compilation compilation,
            INamedTypeSymbol containingType,
            IEnumerable <ISymbol> members,
            CancellationToken cancellationToken)
        {
            var statements = new List <SyntaxNode>();

            var    parts     = StringBreaker.BreakIntoWordParts(containingType.Name);
            string localName = "v";

            for (int i = parts.Count - 1; i >= 0; i--)
            {
                var p = parts[i];
                if (char.IsLetter(containingType.Name[p.Start]))
                {
                    localName = containingType.Name.Substring(p.Start, p.Length).ToCamelCase();
                    break;
                }
            }

            var localNameExpression = factory.CreateIdentifierName(localName);

            var objNameExpression = factory.CreateIdentifierName(ObjName);

            var expressions = new List <SyntaxNode>();

            if (containingType.IsValueType)
            {
#if false
                if (!(obj is MyType))
                {
                    return(false);
                }
#endif
                var ifStatement = factory.CreateIfStatement(
                    factory.CreateLogicalNotExpression(
                        factory.CreateIsExpression(
                            objNameExpression,
                            containingType)),
                    new[] { factory.CreateReturnStatement(factory.CreateFalseExpression()) });

#if false
                var myType = (MyType)obj;
#endif
                var localDeclaration = factory.CreateLocalDeclarationStatement(
                    factory.CreateVariableDeclarator(localName, factory.CreateCastExpression(containingType, objNameExpression)));

                statements.Add(ifStatement);
                statements.Add(localDeclaration);
            }
            else
            {
#if false
                var myType = obj as MyType;
#endif
                var localDeclaration = factory.CreateLocalDeclarationStatement(
                    factory.CreateVariableDeclarator(localName, factory.CreateAsExpression(objNameExpression, containingType)));

                statements.Add(localDeclaration);

#if false
                myType != null
#endif
                expressions.Add(factory.CreateReferenceNotEqualsExpression(localNameExpression, factory.CreateNullExpression()));

                if (HasExistingBaseEqualsMethod(containingType, cancellationToken))
                {
#if false
                    base.Equals(obj)
#endif
                    expressions.Add(factory.CreateInvocationExpression(
                                        factory.CreateMemberAccessExpression(
                                            factory.CreateBaseExpression(),
                                            factory.CreateIdentifierName(EqualsName)),
                                        objNameExpression));
                }
            }

            foreach (var member in members)
            {
                var symbolNameExpression = factory.CreateIdentifierName(member.Name);
                var thisSymbol           = factory.CreateMemberAccessExpression(factory.CreateThisExpression(), symbolNameExpression).WithAdditionalAnnotations(Simplification.Simplifier.Annotation);
                var otherSymbol          = factory.CreateMemberAccessExpression(localNameExpression, symbolNameExpression);

#if false
                EqualityComparer <SType> .Default.Equals(this.S1, myType.S1)
#endif
                var expression =
                    factory.CreateInvocationExpression(
                        factory.CreateMemberAccessExpression(
                            GetDefaultEqualityComparer(factory, compilation, member),
                            factory.CreateIdentifierName(EqualsName)),
                        thisSymbol,
                        otherSymbol);

                expressions.Add(expression);
            }

#if false
            return(myType != null && base.Equals(obj) && EqualityComparer <int> .Default.Equals(this.S1, myType.S1) &&...);
#endif
            statements.Add(factory.CreateReturnStatement(
                               expressions.Aggregate(factory.CreateLogicalAndExpression)));

            return(statements);
        }
示例#27
0
 private static ImmutableArray <string> BreakIntoWordParts(string identifier)
 => PartListToSubstrings(identifier, StringBreaker.GetWordParts(identifier));
示例#28
0
        private static ImmutableArray <SyntaxNode> CreateEqualsMethodStatements(
            SyntaxGenerator factory,
            Compilation compilation,
            INamedTypeSymbol containingType,
            IEnumerable <ISymbol> members,
            CancellationToken cancellationToken)
        {
            var iequatableType = compilation.GetTypeByMetadataName("System.IEquatable`1");
            var statements     = ArrayBuilder <SyntaxNode> .GetInstance();

            // Come up with a good name for the local variable we're going to compare against.
            // For example, if the class name is "CustomerOrder" then we'll generate:
            //
            //      var order = obj as CustomerOrder;

            var parts     = StringBreaker.BreakIntoWordParts(containingType.Name);
            var localName = "v";

            for (var i = parts.Count - 1; i >= 0; i--)
            {
                var p = parts[i];
                if (char.IsLetter(containingType.Name[p.Start]))
                {
                    localName = containingType.Name.Substring(p.Start, p.Length).ToCamelCase();
                    break;
                }
            }

            var localNameExpression = factory.IdentifierName(localName);

            var objNameExpression = factory.IdentifierName(ObjName);

            // These will be all the expressions that we'll '&&' together inside the final
            // return statement of 'Equals'.
            var expressions = new List <SyntaxNode>();

            if (containingType.IsValueType)
            {
                // If we're a value type, then we need an is-check first to make sure
                // the object is our type:
                //
                //      if (!(obj is MyType))
                //      {
                //          return false;
                //      }
                var ifStatement = factory.IfStatement(
                    factory.LogicalNotExpression(
                        factory.IsTypeExpression(
                            objNameExpression,
                            containingType)),
                    new[] { factory.ReturnStatement(factory.FalseLiteralExpression()) });

                // Next, we cast the argument to our type:
                //
                //      var myType = (MyType)obj;

                var localDeclaration = factory.LocalDeclarationStatement(localName, factory.CastExpression(containingType, objNameExpression));

                statements.Add(ifStatement);
                statements.Add(localDeclaration);
            }
            else
            {
                // It's not a value type, we can just use "as" to test the parameter is the right type:
                //
                //      var myType = obj as MyType;

                var localDeclaration = factory.LocalDeclarationStatement(localName, factory.TryCastExpression(objNameExpression, containingType));

                statements.Add(localDeclaration);

                // Ensure that the parameter we got was not null (which also ensures the 'as' test
                // succeeded):
                //
                //      myType != null
                expressions.Add(factory.ReferenceNotEqualsExpression(localNameExpression, factory.NullLiteralExpression()));
                if (HasExistingBaseEqualsMethod(containingType, cancellationToken))
                {
                    // If we're overriding something that also provided an overridden 'Equals',
                    // then ensure the base type thinks it is equals as well.
                    //
                    //      base.Equals(obj)
                    expressions.Add(factory.InvocationExpression(
                                        factory.MemberAccessExpression(
                                            factory.BaseExpression(),
                                            factory.IdentifierName(EqualsName)),
                                        objNameExpression));
                }
            }

            // Now, iterate over all the supplied members and ensure that our instance
            // and the parameter think they are equals.  Specialize how we do this for
            // common types.  Fall-back to EqualityComparer<SType>.Default.Equals for
            // everything else.
            foreach (var member in members)
            {
                var symbolNameExpression = factory.IdentifierName(member.Name);
                var thisSymbol           = factory.MemberAccessExpression(factory.ThisExpression(), symbolNameExpression)
                                           .WithAdditionalAnnotations(Simplification.Simplifier.Annotation);
                var otherSymbol = factory.MemberAccessExpression(localNameExpression, symbolNameExpression);

                var memberType = member.GetSymbolType();

                if (IsPrimitiveValueType(memberType))
                {
                    // If we have one of the well known primitive types, then just use '==' to compare
                    // the values.
                    //
                    //      this.a == other.a
                    expressions.Add(factory.ValueEqualsExpression(thisSymbol, otherSymbol));
                    continue;
                }

                var valueIEquatable = memberType?.IsValueType == true && ImplementsIEquatable(memberType, iequatableType);
                if (valueIEquatable || memberType?.IsTupleType == true)
                {
                    // If it's a value type and implements IEquatable<T>, Or if it's a tuple, then
                    // just call directly into .Equals. This keeps the code simple and avoids an
                    // unnecessary null check.
                    //
                    //      this.a.Equals(other.a)
                    expressions.Add(factory.InvocationExpression(
                                        factory.MemberAccessExpression(thisSymbol, nameof(object.Equals)),
                                        otherSymbol));
                    continue;
                }

                // Otherwise call EqualityComparer<SType>.Default.Equals(this.a, other.a).
                // This will do the appropriate null checks as well as calling directly
                // into IEquatable<T>.Equals implementations if available.

                expressions.Add(factory.InvocationExpression(
                                    factory.MemberAccessExpression(
                                        GetDefaultEqualityComparer(factory, compilation, member),
                                        factory.IdentifierName(EqualsName)),
                                    thisSymbol,
                                    otherSymbol));
            }

            // Now combine all the comparison expressions together into one final statement like:
            //
            //      return myType != null &&
            //             base.Equals(obj) &&
            //             this.S1 == myType.S1;
            statements.Add(factory.ReturnStatement(
                               expressions.Aggregate(factory.LogicalAndExpression)));

            return(statements.ToImmutableAndFree());
        }