/// <summary> /// Adds all members of a class to a dictionary, taking into account partial classes. /// </summary> /// <param name="parentClass"> /// The class to collect. /// </param> /// <returns> /// Returns the dictionary of class members. /// </returns> public static Dictionary<string, List<CsElement>> CollectClassMembers(ClassBase parentClass) { Param.AssertNotNull(parentClass, "parentClass"); Dictionary<string, List<CsElement>> members = new Dictionary<string, List<CsElement>>(); if (parentClass.Declaration.ContainsModifier(CsTokenType.Partial)) { foreach (ClassBase @class in parentClass.PartialElementList) { CollectClassMembersAux(@class, members); } } else { CollectClassMembersAux(parentClass, members); } return members; }
/// <summary> /// Finds the given class member in the given class. /// </summary> /// <param name="word"> /// The word to check. /// </param> /// <param name="parentClass"> /// The class the word appears in. /// </param> /// <param name="members"> /// The collection of members of the parent class. /// </param> /// <param name="interfaces"> /// True if interface implementations should be included. /// </param> /// <returns> /// Returns the class members that match against the given name. /// </returns> public static ICollection <CsElement> FindClassMember(string word, ClassBase parentClass, Dictionary <string, List <CsElement> > members, bool interfaces) { Param.AssertNotNull(word, "word"); Param.AssertNotNull(parentClass, "parentClass"); Param.AssertNotNull(members, "members"); Param.Ignore(interfaces); // If the word is the same as the class name, then this is a constructor and we // don't want to match against it. if (word != parentClass.Declaration.Name) { ICollection <CsElement> matches = MatchClassMember(word, members, interfaces); if (matches != null && matches.Count > 0) { return(matches); } } return(null); }
/// <summary> /// Adds all members of a class to a dictionary, taking into account partial classes. /// </summary> /// <param name="parentClass"> /// The class to collect. /// </param> /// <returns> /// Returns the dictionary of class members. /// </returns> public static Dictionary <string, List <CsElement> > CollectClassMembers(ClassBase parentClass) { Param.AssertNotNull(parentClass, "parentClass"); Dictionary <string, List <CsElement> > members = new Dictionary <string, List <CsElement> >(); if (parentClass.Declaration.ContainsModifier(CsTokenType.Partial)) { foreach (ClassBase @class in parentClass.PartialElementList) { CollectClassMembersAux(@class, members); } } else { CollectClassMembersAux(parentClass, members); } return(members); }
/// <summary> /// Parses the given statement list. /// </summary> /// <param name="statements"> /// The list of statements to parse. /// </param> /// <param name="parentElement"> /// The element that contains the statements. /// </param> /// <param name="parentClass"> /// The class that the element belongs to. /// </param> /// <param name="members"> /// The collection of members of the parent class. /// </param> private void CheckClassMemberRulesForStatements( ICollection <Statement> statements, CsElement parentElement, ClassBase parentClass, Dictionary <string, List <CsElement> > members) { Param.AssertNotNull(statements, "statements"); Param.AssertNotNull(parentElement, "parentElement"); Param.Ignore(parentClass); Param.Ignore(members); // Loop through each of the statements. foreach (Statement statement in statements) { if (statement.ChildStatements.Count > 0) { // Parse the sub-statements. this.CheckClassMemberRulesForStatements(statement.ChildStatements, parentElement, parentClass, members); } // Parse the expressions in the statement. this.CheckClassMemberRulesForExpressions(statement.ChildExpressions, null, parentElement, parentClass, members); } }
/// <summary> /// Checks the items within the given element. /// </summary> /// <param name="element"> /// The element to check. /// </param> /// <param name="parentClass"> /// The class that the element belongs to. /// </param> /// <param name="members"> /// The collection of members of the parent class. /// </param> /// <returns> /// Returns false if the analyzer should quit. /// </returns> private bool CheckClassMemberRulesForElements(CsElement element, ClassBase parentClass, Dictionary<string, List<CsElement>> members) { Param.AssertNotNull(element, "element"); Param.Ignore(parentClass); Param.Ignore(members); // Check whether processing has been cancelled by the user. if (this.Cancel) { return false; } if (element.ElementType == ElementType.Class || element.ElementType == ElementType.Struct || element.ElementType == ElementType.Interface) { parentClass = element as ClassBase; members = Utils.CollectClassMembers(parentClass); } foreach (CsElement child in element.ChildElements) { if (!child.Generated) { if (child.ElementType == ElementType.Method || child.ElementType == ElementType.Constructor || child.ElementType == ElementType.Destructor || child.ElementType == ElementType.Accessor || !child.HasBody) { // If the parent class is null, then this element is sitting outside of a class. // This is illegal in C# so the code will not compile, but we still attempt to // parse it. In this case there is no use of this prefixes since there is no class. if (parentClass != null) { this.CheckClassMemberRulesForStatements(child.ChildStatements, child, parentClass, members); } } else { if (child.ElementType == ElementType.Class || child.ElementType == ElementType.Struct) { ClassBase elementContainer = child as ClassBase; Debug.Assert(elementContainer != null, "The element is not a class."); this.CheckClassMemberRulesForElements(child, elementContainer, members); } else if (!this.CheckClassMemberRulesForElements(child, parentClass, members)) { return false; } } } } return true; }
/// <summary> /// Checks a token to see if it should be prefixed (with this. or maybe another prefix). /// </summary> /// <param name="expression"> /// The expression the word appears within. /// </param> /// <param name="parentClass"> /// The parent class that this element belongs to. /// </param> /// <param name="matchesForPassedMethod"> /// Matches for the passed-in version of the member name. /// </param> /// <param name="matchesForGenericMethod"> /// The matches for the generic version of the member name. /// </param> /// <param name="memberName"> /// The name of the member to check. /// </param> /// <returns> /// True if the prefix is required otherwise false. /// </returns> private static bool IsThisRequiredFromMemberList( Expression expression, ClassBase parentClass, IEnumerable<CsElement> matchesForPassedMethod, IEnumerable<CsElement> matchesForGenericMethod, string memberName) { if (matchesForPassedMethod != null) { return IsThisRequiredFromMemberList(matchesForPassedMethod); } if (matchesForGenericMethod != null) { return IsThisRequiredFromMemberList(matchesForGenericMethod); } if (parentClass.BaseClass != string.Empty) { if (Utils.IsExpressionInsideContainer( expression, typeof(NullConditionExpression), typeof(TypeofExpression), typeof(IsExpression), typeof(CastExpression), typeof(AsExpression), typeof(NewExpression), typeof(NewArrayExpression), typeof(MemberAccessExpression), typeof(DefaultValueExpression), typeof(VariableDeclarationExpression))) { return false; } if (expression.Parent is CatchStatement || expression.Parent is LabelStatement || expression.Parent is GotoStatement) { return false; } return true; } if (parentClass.BaseClass == string.Empty && memberName == "Equals") { return true; } return false; }
/// <summary> /// Returns True if this class or any of its Partial Classes has a BaseClass specified. /// </summary> /// <param name="classBase"> /// The class to check. /// </param> /// <returns> /// True if it finds a BaseClass. /// </returns> public static bool HasABaseClassSpecified(ClassBase classBase) { return(!string.IsNullOrEmpty(classBase.BaseClass) || (classBase.PartialElementList != null && classBase.PartialElementList.OfType <Class>().Any(d => !string.IsNullOrEmpty(d.BaseClass)))); }
/// <summary> /// Parses the given expression. /// </summary> /// <param name="expression"> /// The expression. /// </param> /// <param name="parentExpression"> /// The parent expression, if there is one. /// </param> /// <param name="parentElement"> /// The element that contains the expressions. /// </param> /// <param name="parentClass"> /// The class that the element belongs to. /// </param> /// <param name="members"> /// The collection of members of the parent class. /// </param> private void CheckClassMemberRulesForExpression( Expression expression, Expression parentExpression, CsElement parentElement, ClassBase parentClass, Dictionary <string, List <CsElement> > members) { Param.AssertNotNull(expression, "expression"); Param.Ignore(parentExpression); Param.AssertNotNull(parentElement, "parentElement"); Param.AssertNotNull(parentClass, "parentClass"); Param.AssertNotNull(members, "members"); if (expression.ExpressionType == ExpressionType.Literal) { LiteralExpression literalExpression = (LiteralExpression)expression; // Check to see whether this literal is preceded by a member access symbol. If not // then we want to check whether this is a reference to one of our class members. if (!IsLiteralTokenPrecededByMemberAccessSymbol(literalExpression.TokenNode, expression.Tokens.MasterList)) { // Process the literal. this.CheckClassMemberRulesForLiteralToken(literalExpression.TokenNode, expression, parentExpression, parentElement, parentClass, members); } } else { if (expression.ExpressionType == ExpressionType.Assignment && parentExpression != null && parentExpression.ExpressionType == ExpressionType.CollectionInitializer) { // When we encounter assignment expressions within collection initializer expressions, we ignore the expression // on the left-hand side of the assignment. This is because we know that the left-hand side refers to a property on // the type being initialized, not a property on the local class. Thus, it does not ever need to be prefixed by this. // Without this check we can get name collisions, such as: // public sealed class Person //// { //// public string FirstName { get; } //// public void CreateAnonymousType() //// { //// var anonymousType = new { FirstName = this.FirstName }; //// } //// } this.CheckClassMemberRulesForExpression(((AssignmentExpression)expression).RightHandSide, expression, parentElement, parentClass, members); } else if (expression.ChildExpressions.Count > 0) { // Check each child expression within this expression. this.CheckClassMemberRulesForExpressions(expression.ChildExpressions, expression, parentElement, parentClass, members); } // Check if this is an anonymous method expression, which contains a child statement list. if (expression.ExpressionType == ExpressionType.AnonymousMethod) { // Check the statements under this anonymous method. this.CheckClassMemberRulesForStatements(expression.ChildStatements, parentElement, parentClass, members); } else if (expression.ExpressionType == ExpressionType.MethodInvocation) { // Check each of the arguments passed into the method call. MethodInvocationExpression methodInvocation = (MethodInvocationExpression)expression; foreach (Argument argument in methodInvocation.Arguments) { // Check each expression within this child expression. if (argument.Expression.ExpressionType != ExpressionType.MethodInvocation) { this.CheckClassMemberRulesForExpression(argument.Expression, null, parentElement, parentClass, members); } } } } }
/// <summary> /// Calculates whether the base prefix is required. /// </summary> /// <param name="memberName"> /// The text of the method call to check. /// </param> /// <param name="parentClass"> /// The class this this member belongs to. /// </param> /// <param name="members"> /// All the members of this class. /// </param> /// <returns> /// True if base is required otherwise false. /// </returns> private bool IsBaseRequired(string memberName, ClassBase parentClass, Dictionary<string, List<CsElement>> members) { // An item is only allowed to start with base if there is an implementation of the // item in the local class and the caller is trying to explicitly call the base // class implementation instead of the local class implementation. bool memberNameHasGeneric = memberName.IndexOf('<') > -1; bool overrideOnTrimmedMethod = false; bool overrideOnGenericMethod = false; bool overrideOnPassedMethod = false; bool newOnPassedMethod = false; bool newOnGenericMethod = false; ICollection<CsElement> matchesForTrimmedMethod = null; ICollection<CsElement> matchesForGenericMethod = null; ICollection<CsElement> matchesForPassedMethod = Utils.FindClassMember(memberName, parentClass, members, true); if (memberNameHasGeneric) { string trimmedName = memberName.Substring(0, memberName.IndexOf('<')); matchesForTrimmedMethod = Utils.FindClassMember(trimmedName, parentClass, members, true); if (matchesForTrimmedMethod != null) { foreach (CsElement match in matchesForTrimmedMethod) { if (match.Declaration.ContainsModifier(CsTokenType.Override)) { overrideOnTrimmedMethod = true; break; } } } } else { matchesForGenericMethod = Utils.FindClassMember(memberName + "<T>", parentClass, members, true); if (matchesForGenericMethod != null) { foreach (CsElement match in matchesForGenericMethod) { if (match.Declaration.ContainsModifier(CsTokenType.Override)) { overrideOnGenericMethod = true; } if (match.Declaration.ContainsModifier(CsTokenType.New)) { newOnGenericMethod = true; } } } } // We check for a method marked override and a method marked new if (matchesForPassedMethod != null) { foreach (CsElement match in matchesForPassedMethod) { if (match.Declaration.ContainsModifier(CsTokenType.Override)) { overrideOnPassedMethod = true; } if (match.Declaration.ContainsModifier(CsTokenType.New)) { newOnPassedMethod = true; break; } } } bool baseIsRequired = memberNameHasGeneric && (overrideOnTrimmedMethod || newOnPassedMethod || overrideOnPassedMethod); if (!memberNameHasGeneric && (overrideOnPassedMethod || newOnGenericMethod || overrideOnGenericMethod || matchesForPassedMethod != null)) { baseIsRequired = true; } if (memberNameHasGeneric && (overrideOnTrimmedMethod || matchesForTrimmedMethod != null)) { baseIsRequired = true; } return baseIsRequired; }
/// <summary> /// Parses the given literal token. /// </summary> /// <param name="tokenNode"> /// The literal token node. /// </param> /// <param name="expression"> /// The expression that contains the token. /// </param> /// <param name="parentExpression"> /// The parent of the expression that contains the token. /// </param> /// <param name="parentElement"> /// The element that contains the expression. /// </param> /// <param name="parentClass"> /// The class that the element belongs to. /// </param> /// <param name="members"> /// The collection of members of the parent class. /// </param> private void CheckClassMemberRulesForLiteralToken( Node<CsToken> tokenNode, Expression expression, Expression parentExpression, CsElement parentElement, ClassBase parentClass, Dictionary<string, List<CsElement>> members) { Param.AssertNotNull(tokenNode, "token"); Param.AssertNotNull(expression, "expression"); Param.Ignore(parentExpression); Param.AssertNotNull(parentElement, "parentElement"); Param.Ignore(parentClass); Param.Ignore(members); // Skip types. We only care about named members. if (!(tokenNode.Value is TypeToken) || tokenNode.Value.CsTokenClass == CsTokenClass.GenericType) { // If the name starts with a dot, ignore it. if (!tokenNode.Value.Text.StartsWith(".", StringComparison.Ordinal)) { if (tokenNode.Value.Text == "base" && parentExpression != null) { CsToken name = Utils.ExtractBaseClassMemberName(parentExpression, tokenNode); if (name != null) { if (!this.IsBaseRequired(name.Text, parentClass, members)) { this.AddViolation(parentElement, name.Location, Rules.DoNotPrefixCallsWithBaseUnlessLocalImplementationExists, name); } } } else if (tokenNode.Value.Text != "this") { if (this.IsThisRequired(tokenNode, expression, parentClass, members)) { if ((parentClass.BaseClass != string.Empty) || (tokenNode.Value.Text == "Equals" || tokenNode.Value.Text == "ReferenceEquals")) { string className = parentClass.FullyQualifiedName.SubstringAfterLast('.'); if (parentClass.BaseClass != string.Empty) { className = className + ".' or '" + parentClass.BaseClass; } this.AddViolation(parentElement, tokenNode.Value.Location, Rules.PrefixCallsCorrectly, tokenNode.Value.Text, className); } else { this.AddViolation(parentElement, tokenNode.Value.Location, Rules.PrefixLocalCallsWithThis, tokenNode.Value.Text); } } } } } }
/// <summary> /// Gets the actual qualified namespace of the class. For nested types where A.B.C.D exists and C.D is the type it returns A.B rather than A.B.C. /// </summary> /// <param name="type"> /// The type to get the namespace for. /// </param> /// <returns> /// A string of the actual namespace. /// </returns> private static string GetActualQualifiedNamespace(ClassBase type) { Param.AssertNotNull(type, "type"); CsElement localType = type; while (localType.Parent is ClassBase) { localType = (CsElement)localType.Parent; } string fullyQualifiedNameOfParentClass = localType.FullyQualifiedName; int lastIndexOfDot = fullyQualifiedNameOfParentClass.LastIndexOf('.'); return lastIndexOfDot == -1 ? string.Empty : fullyQualifiedNameOfParentClass.Substring(0, lastIndexOfDot + 1); }
/// <summary> /// For a type Foo.Bar in namespace A.B this returns (A.B.Foo.Bar | B.Foo.Bar | Foo.Bar | Bar) and (A.B.Foo{E,F}.Bar | B.Foo{E,F}.Bar | Foo{E,F}.Bar | Bar) /// </summary> /// <param name="type"> /// The type to build the <see cref="Regex"/> for. /// </param> /// <param name="regexWithGenerics"> /// The <see cref="Regex"/> with generics. /// </param> /// <param name="regexWithoutGenerics"> /// The <see cref="Regex"/> without generics. /// </param> private static void BuildRegExForAllTypeOptions(ClassBase type, out string regexWithGenerics, out string regexWithoutGenerics) { Param.AssertNotNull(type, "type"); string[] typeNameParts = type.FullyQualifiedName.Split('.'); StringBuilder actualTypeNameWithoutGenerics = new StringBuilder(); StringBuilder typeNameWithGenerics = new StringBuilder(); for (int i = 0; i < typeNameParts.Length; ++i) { typeNameWithGenerics.Append(BuildTypeNameStringWithGenerics(typeNameParts[i])); actualTypeNameWithoutGenerics.Append(RemoveGenericsFromTypeName(typeNameParts[i])); if (i < typeNameParts.Length - 1) { typeNameWithGenerics.Append("."); actualTypeNameWithoutGenerics.Append("."); } } regexWithGenerics = BuildRegExStringFromTypeName(typeNameWithGenerics.ToString()); regexWithoutGenerics = BuildRegExStringFromTypeName(actualTypeNameWithoutGenerics.ToString()); }
/// <summary> /// Builds a regular expression that can be used to validate the name of the given type when /// used within a documentation <c>cref</c> attribute. /// </summary> /// <param name="type"> /// The type to match against. /// </param> /// <returns> /// Returns the regular expression object. /// </returns> private static string BuildCrefValidationStringForType(ClassBase type) { Param.AssertNotNull(type, "type"); StringBuilder typeNameWithParamsNumber = new StringBuilder(); string[] typeNameParts = type.FullyQualifiedName.Split('.'); // Start at index 1 to skip the 'Root' for (int i = 1; i < typeNameParts.Length; i++) { typeNameWithParamsNumber.Append(BuildTypeNameStringWithParamsNumber(typeNameParts[i])); if (i < typeNameParts.Length - 1) { typeNameWithParamsNumber.Append(@"\."); } } string regexWithGenerics; string regexWithoutGenerics; BuildRegExForAllTypeOptions(type, out regexWithGenerics, out regexWithoutGenerics); // Build the regex string to match all possible formats for the type name. return string.Format(CultureInfo.InvariantCulture, CrefRegex, typeNameWithParamsNumber, regexWithoutGenerics, regexWithGenerics); }
/// <summary> /// Checks the Xml header block of the given class for consistency with the class. /// </summary> /// <param name="classElement"> /// The element to parse. /// </param> /// <param name="settings"> /// The analyzer settings. /// </param> private void CheckClassElementHeader(ClassBase classElement, AnalyzerSettings settings) { Param.AssertNotNull(classElement, "classElement"); Param.Ignore(settings); AnalyzerSettings adjustedSettings = settings; adjustedSettings.RequireFields = false; this.CheckHeader(classElement, adjustedSettings, classElement.Declaration.ContainsModifier(CsTokenType.Partial)); }
/// <summary> /// The save. /// </summary> /// <param name="class"> /// The class. /// </param> private void Save(ClassBase @class) { // this is template this.IsCPPInHeader = @class.Declaration.Name.Contains('<'); // save attributes foreach (var attr in @class.Attributes) { this.Save(attr, this.headerWriter); } var nakedClassName = this.GetNameBase(@class.Declaration.Name); this.ClassContext = new ClassContext(@class, this.FullyQualifiedNames); if (@class.AccessModifier != AccessModifierType.Internal) { this.Save(@class.AccessModifier); this.headerWriter.Write(" "); } this.headerWriter.Write("ref "); this.headerWriter.Write(this.BuildDeclaretionTemplatePart(@class.Declaration.Name)); this.currentClassNamespace = nakedClassName; var typeKeyword = SharpToCppConverterHelper.GetTypeKeyword(@class); this.headerWriter.Write("{1} {0}", nakedClassName, typeKeyword); if (@class.HasToken(CsTokenType.Sealed)) { this.headerWriter.Write(" sealed"); } var baseClass = this.GetNameBase(@class.BaseClass, false); var hasColon = false; if (!string.IsNullOrEmpty(baseClass) && !baseClass.Equals(typeKeyword)) { this.headerWriter.Write(": public "); this.currentBaseClass = baseClass; this.Save( baseClass, this.headerWriter, SavingOptions.UseFullyQualifiedNames | SavingOptions.RemovePointer); hasColon = true; } // writer interfaces if (!hasColon && @class.ImplementedInterfaces != null && @class.ImplementedInterfaces.Count > 0) { this.headerWriter.Write(": "); } var first = !hasColon; foreach (var interfaceOfClass in @class.ImplementedInterfaces) { if (!first) { this.headerWriter.Write(", "); } this.Save( interfaceOfClass, this.headerWriter, SavingOptions.UseFullyQualifiedNames | SavingOptions.RemovePointer); first = false; } this.headerWriter.WriteLine(); this.headerWriter.WriteLine('{'); this.headerWriter.Indent++; // before save all static initializers this.SaveFieldInitializersIntoDefaultConstructor(false); this.@switch(@class.ChildElements); this.headerWriter.Indent--; this.headerWriter.WriteLine("};"); this.ClassContext = null; }
/// <summary> /// Returns True if this class or any of its Partial Classes has a BaseClass specified. /// </summary> /// <param name="classBase"> /// The class to check. /// </param> /// <returns> /// True if it finds a BaseClass. /// </returns> public static bool HasImplementedInterfaces(ClassBase classBase) { return(classBase.ImplementedInterfaces.Count > 0 || (classBase.PartialElementList != null && classBase.PartialElementList.OfType <Class>().Any(d => d.ImplementedInterfaces.Count > 0))); }
/// <summary> /// Parses the given expression. /// </summary> /// <param name="expression"> /// The expression. /// </param> /// <param name="parentExpression"> /// The parent expression, if there is one. /// </param> /// <param name="parentElement"> /// The element that contains the expressions. /// </param> /// <param name="parentClass"> /// The class that the element belongs to. /// </param> /// <param name="members"> /// The collection of members of the parent class. /// </param> private void CheckClassMemberRulesForExpression( Expression expression, Expression parentExpression, CsElement parentElement, ClassBase parentClass, Dictionary<string, List<CsElement>> members) { Param.AssertNotNull(expression, "expression"); Param.Ignore(parentExpression); Param.AssertNotNull(parentElement, "parentElement"); Param.AssertNotNull(parentClass, "parentClass"); Param.AssertNotNull(members, "members"); if (expression.ExpressionType == ExpressionType.Literal) { LiteralExpression literalExpression = (LiteralExpression)expression; // Check to see whether this literal is preceded by a member access symbol. If not // then we want to check whether this is a reference to one of our class members. if (!IsLiteralTokenPrecededByMemberAccessSymbol(literalExpression.TokenNode, expression.Tokens.MasterList)) { // Process the literal. this.CheckClassMemberRulesForLiteralToken(literalExpression.TokenNode, expression, parentExpression, parentElement, parentClass, members); } } else { if (expression.ExpressionType == ExpressionType.Assignment && parentExpression != null && parentExpression.ExpressionType == ExpressionType.CollectionInitializer) { // When we encounter assignment expressions within collection initializer expressions, we ignore the expression // on the left-hand side of the assignment. This is because we know that the left-hand side refers to a property on // the type being initialized, not a property on the local class. Thus, it does not ever need to be prefixed by this. // Without this check we can get name collisions, such as: // public sealed class Person //// { //// public string FirstName { get; } //// public void CreateAnonymousType() //// { //// var anonymousType = new { FirstName = this.FirstName }; //// } //// } this.CheckClassMemberRulesForExpression(((AssignmentExpression)expression).RightHandSide, expression, parentElement, parentClass, members); } else if (expression.ChildExpressions.Count > 0) { // Check each child expression within this expression. this.CheckClassMemberRulesForExpressions(expression.ChildExpressions, expression, parentElement, parentClass, members); } // Check if this is an anonymous method expression, which contains a child statement list. if (expression.ExpressionType == ExpressionType.AnonymousMethod) { // Check the statements under this anonymous method. this.CheckClassMemberRulesForStatements(expression.ChildStatements, parentElement, parentClass, members); } else if (expression.ExpressionType == ExpressionType.MethodInvocation) { // Check each of the arguments passed into the method call. MethodInvocationExpression methodInvocation = (MethodInvocationExpression)expression; foreach (Argument argument in methodInvocation.Arguments) { // Check each expression within this child expression. if (argument.Expression.ExpressionType != ExpressionType.MethodInvocation) { this.CheckClassMemberRulesForExpression(argument.Expression, null, parentElement, parentClass, members); } } } } }
/// <summary> /// Parses the list of expressions. /// </summary> /// <param name="expressions"> /// The list of expressions. /// </param> /// <param name="parentExpression"> /// The parent expression, if there is one. /// </param> /// <param name="parentElement"> /// The element that contains the expressions. /// </param> /// <param name="parentClass"> /// The class that the element belongs to. /// </param> /// <param name="members"> /// The collection of members of the parent class. /// </param> private void CheckClassMemberRulesForExpressions( IEnumerable<Expression> expressions, Expression parentExpression, CsElement parentElement, ClassBase parentClass, Dictionary<string, List<CsElement>> members) { Param.AssertNotNull(expressions, "expressions"); Param.AssertNotNull(parentElement, "parentElement"); Param.Ignore(parentExpression); Param.Ignore(parentClass); Param.Ignore(members); // Loop through each of the expressions in the list. foreach (Expression expression in expressions) { // If the expression is a variable declarator expression, we don't want to match against the identifier tokens. if (expression.ExpressionType == ExpressionType.VariableDeclarator) { VariableDeclaratorExpression declarator = expression as VariableDeclaratorExpression; if (declarator.Initializer != null) { this.CheckClassMemberRulesForExpression(declarator.Initializer, parentExpression, parentElement, parentClass, members); } } else { this.CheckClassMemberRulesForExpression(expression, parentExpression, parentElement, parentClass, members); } } }
/// <summary> /// Finds the given class member in the given class. /// </summary> /// <param name="word"> /// The word to check. /// </param> /// <param name="parentClass"> /// The class the word appears in. /// </param> /// <param name="members"> /// The collection of members of the parent class. /// </param> /// <param name="interfaces"> /// True if interface implementations should be included. /// </param> /// <returns> /// Returns the class members that match against the given name. /// </returns> public static ICollection<CsElement> FindClassMember(string word, ClassBase parentClass, Dictionary<string, List<CsElement>> members, bool interfaces) { Param.AssertNotNull(word, "word"); Param.AssertNotNull(parentClass, "parentClass"); Param.AssertNotNull(members, "members"); Param.Ignore(interfaces); // If the word is the same as the class name, then this is a constructor and we // don't want to match against it. if (word != parentClass.Declaration.Name) { ICollection<CsElement> matches = MatchClassMember(word, members, interfaces); if (matches != null && matches.Count > 0) { return matches; } } return null; }
/// <summary> /// Parses the given statement list. /// </summary> /// <param name="statements"> /// The list of statements to parse. /// </param> /// <param name="parentElement"> /// The element that contains the statements. /// </param> /// <param name="parentClass"> /// The class that the element belongs to. /// </param> /// <param name="members"> /// The collection of members of the parent class. /// </param> private void CheckClassMemberRulesForStatements( ICollection<Statement> statements, CsElement parentElement, ClassBase parentClass, Dictionary<string, List<CsElement>> members) { Param.AssertNotNull(statements, "statements"); Param.AssertNotNull(parentElement, "parentElement"); Param.Ignore(parentClass); Param.Ignore(members); // Loop through each of the statements. foreach (Statement statement in statements) { if (statement.ChildStatements.Count > 0) { // Parse the sub-statements. this.CheckClassMemberRulesForStatements(statement.ChildStatements, parentElement, parentClass, members); } // Parse the expressions in the statement. this.CheckClassMemberRulesForExpressions(statement.ChildExpressions, null, parentElement, parentClass, members); } }
/// <summary> /// Returns True if this class or any of its Partial Classes has a BaseClass specified. /// </summary> /// <param name="classBase"> /// The class to check. /// </param> /// <returns> /// True if it finds a BaseClass. /// </returns> public static bool HasABaseClassSpecified(ClassBase classBase) { return !string.IsNullOrEmpty(classBase.BaseClass) || (classBase.PartialElementList != null && classBase.PartialElementList.OfType<Class>().Any(d => !string.IsNullOrEmpty(d.BaseClass))); }
/// <summary> /// Checks a token to see if it should be prefixed (with this. or maybe another prefix). /// </summary> /// <param name="tokenNode"> /// The TokenNode to check. /// </param> /// <param name="expression"> /// The expression the word appears within. /// </param> /// <param name="parentClass"> /// The parent class that this element belongs to. /// </param> /// <param name="members"> /// The collection of members of the parent class. /// </param> /// <returns> /// True if the prefix is required otherwise false. /// </returns> private bool IsThisRequired(Node<CsToken> tokenNode, Expression expression, ClassBase parentClass, Dictionary<string, List<CsElement>> members) { string memberName = tokenNode.Value.Text; if (IsLocalMember(memberName, tokenNode.Value, expression) || IsObjectInitializerLeftHandSideExpression(expression) || memberName == "object" || tokenNode.Value.CsTokenType != CsTokenType.Other) { return false; } ICollection<CsElement> matchesForGenericMethod; ICollection<CsElement> matchesForPassedMethod = Utils.FindClassMember(memberName, parentClass, members, true); bool memberNameHasGeneric = memberName.IndexOf('<') > -1; if (memberNameHasGeneric) { matchesForGenericMethod = Utils.FindClassMember(memberName.Substring(0, memberName.IndexOf('<')) + "<T>", parentClass, members, true); return IsThisRequiredFromMemberList(expression, parentClass, matchesForPassedMethod, matchesForGenericMethod, memberName); } matchesForGenericMethod = Utils.FindClassMember(memberName + "<T>", parentClass, members, true); return IsThisRequiredFromMemberList(expression, parentClass, matchesForPassedMethod, matchesForGenericMethod, memberName); }
/// <summary> /// Returns True if this class or any of its Partial Classes has a BaseClass specified. /// </summary> /// <param name="classBase"> /// The class to check. /// </param> /// <returns> /// True if it finds a BaseClass. /// </returns> public static bool HasImplementedInterfaces(ClassBase classBase) { return classBase.ImplementedInterfaces.Count > 0 || (classBase.PartialElementList != null && classBase.PartialElementList.OfType<Class>().Any(d => d.ImplementedInterfaces.Count > 0)); }
/// <summary> /// Parses the list of expressions. /// </summary> /// <param name="expressions"> /// The list of expressions. /// </param> /// <param name="parentExpression"> /// The parent expression, if there is one. /// </param> /// <param name="parentElement"> /// The element that contains the expressions. /// </param> /// <param name="parentClass"> /// The class that the element belongs to. /// </param> /// <param name="members"> /// The collection of members of the parent class. /// </param> private void CheckClassMemberRulesForExpressions( IEnumerable <Expression> expressions, Expression parentExpression, CsElement parentElement, ClassBase parentClass, Dictionary <string, List <CsElement> > members) { Param.AssertNotNull(expressions, "expressions"); Param.AssertNotNull(parentElement, "parentElement"); Param.Ignore(parentExpression); Param.Ignore(parentClass); Param.Ignore(members); // Loop through each of the expressions in the list. foreach (Expression expression in expressions) { // If the expression is a variable declarator expression, we don't want to match against the identifier tokens. if (expression.ExpressionType == ExpressionType.VariableDeclarator) { VariableDeclaratorExpression declarator = expression as VariableDeclaratorExpression; if (declarator.Initializer != null) { this.CheckClassMemberRulesForExpression(declarator.Initializer, parentExpression, parentElement, parentClass, members); } } else { this.CheckClassMemberRulesForExpression(expression, parentExpression, parentElement, parentClass, members); } } }
/// <summary> /// Adds all members of a class to a dictionary. /// </summary> /// <param name="class"> /// The class to collect. /// </param> /// <param name="members"> /// Adds all members of the class to the given dictionary. /// </param> private static void CollectClassMembersAux(ClassBase @class, Dictionary<string, List<CsElement>> members) { Param.AssertNotNull(@class, "class"); Param.AssertNotNull(members, "members"); foreach (CsElement child in @class.ChildElements) { if (child.ElementType == ElementType.Field) { // Look through each of the declarators in the field. foreach (VariableDeclaratorExpression declarator in ((Field)child).VariableDeclarationStatement.Declarators) { AddClassMember(members, child, declarator.Identifier.Text); } } else if (child.ElementType == ElementType.Event) { // Look through each of the declarators in the event. foreach (EventDeclaratorExpression declarator in ((Event)child).Declarators) { AddClassMember(members, child, declarator.Identifier.Text); } } else if (child.ElementType != ElementType.EmptyElement) { AddClassMember(members, child, child.Declaration.Name); } } }
/// <summary> /// Calculates whether the base prefix is required. /// </summary> /// <param name="memberName"> /// The text of the method call to check. /// </param> /// <param name="parentClass"> /// The class this this member belongs to. /// </param> /// <param name="members"> /// All the members of this class. /// </param> /// <returns> /// True if base is required otherwise false. /// </returns> private bool IsBaseRequired(string memberName, ClassBase parentClass, Dictionary <string, List <CsElement> > members) { // An item is only allowed to start with base if there is an implementation of the // item in the local class and the caller is trying to explicitly call the base // class implementation instead of the local class implementation. bool memberNameHasGeneric = memberName.IndexOf('<') > -1; bool overrideOnTrimmedMethod = false; bool overrideOnGenericMethod = false; bool overrideOnPassedMethod = false; bool newOnPassedMethod = false; bool newOnGenericMethod = false; ICollection <CsElement> matchesForTrimmedMethod = null; ICollection <CsElement> matchesForGenericMethod = null; ICollection <CsElement> matchesForPassedMethod = Utils.FindClassMember(memberName, parentClass, members, true); if (memberNameHasGeneric) { string trimmedName = memberName.Substring(0, memberName.IndexOf('<')); matchesForTrimmedMethod = Utils.FindClassMember(trimmedName, parentClass, members, true); if (matchesForTrimmedMethod != null) { foreach (CsElement match in matchesForTrimmedMethod) { if (match.Declaration.ContainsModifier(CsTokenType.Override)) { overrideOnTrimmedMethod = true; break; } } } } else { matchesForGenericMethod = Utils.FindClassMember(memberName + "<T>", parentClass, members, true); if (matchesForGenericMethod != null) { foreach (CsElement match in matchesForGenericMethod) { if (match.Declaration.ContainsModifier(CsTokenType.Override)) { overrideOnGenericMethod = true; } if (match.Declaration.ContainsModifier(CsTokenType.New)) { newOnGenericMethod = true; } } } } // We check for a method marked override and a method marked new if (matchesForPassedMethod != null) { foreach (CsElement match in matchesForPassedMethod) { if (match.Declaration.ContainsModifier(CsTokenType.Override)) { overrideOnPassedMethod = true; } if (match.Declaration.ContainsModifier(CsTokenType.New)) { newOnPassedMethod = true; break; } } } bool baseIsRequired = memberNameHasGeneric && (overrideOnTrimmedMethod || newOnPassedMethod || overrideOnPassedMethod); if (!memberNameHasGeneric && (overrideOnPassedMethod || newOnGenericMethod || overrideOnGenericMethod || matchesForPassedMethod != null)) { baseIsRequired = true; } if (memberNameHasGeneric && (overrideOnTrimmedMethod || matchesForTrimmedMethod != null)) { baseIsRequired = true; } return(baseIsRequired); }
/// <summary> /// Initializes a new instance of the <see cref="ClassContext"/> class. /// </summary> /// <param name="class"> /// The class. /// </param> /// <param name="fullyQualifiedNameCache"> /// The fully qualified name cache. /// </param> public ClassContext(ClassBase @class, FullyQualifiedNamesCache fullyQualifiedNameCache) { this.@class = @class; this.namespaceNode = fullyQualifiedNameCache.FindNamespaceNodeFromRoot(this.Class.FullyQualifiedName); //Debug.Assert(this.namespaceNode != null); }