/// <summary> /// Checks the built-in types and empty strings within a document. /// </summary> /// <param name="document"> /// The document containing the tokens. /// </param> /// <param name="settings"> /// The current settings. /// </param> private void IterateTokenList(CsDocument document, Settings settings) { Param.AssertNotNull(document, "document"); Param.Ignore(settings); for (Node <CsToken> tokenNode = document.Tokens.First; tokenNode != null; tokenNode = tokenNode.Next) { CsToken token = tokenNode.Value; if (token.CsTokenClass == CsTokenClass.Type || token.CsTokenClass == CsTokenClass.GenericType) { // Check that the type is using the built-in types, if applicable. this.CheckBuiltInType(tokenNode, document); if (token.CsTokenClass == CsTokenClass.GenericType) { this.CheckShorthandForNullableTypes(tokenNode.Value); } } else if (token.CsTokenType == CsTokenType.String) { // Check that the string is not using the empty string "" syntax. this.CheckEmptyString(tokenNode); } else if (token.CsTokenClass == CsTokenClass.RegionDirective && settings.DoNotUseRegions) { Region region = (Region)token; if (region.Beginning && !region.Generated && !region.IsGeneratedCodeRegion) { // There should not be any regions in the code. this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.DoNotUseRegions); } } else if (settings.AvoidStringFormatUseStringInterpolation && token.CsTokenType == CsTokenType.Other && token.Text == "Format") { // Check this rule only if project target framework version is greater or equal 4.6 if (document.SourceCode.Project.TargetFrameworkVersion >= 4.6) { MemberAccessExpression expression = token.Parent?.Parent as MemberAccessExpression; // Check if literal expression is not null and check text to avoid user's custom code like enumeration. if (expression != null && expression.Text == "string.Format") { CsToken region = (CsToken)token; if (!region.Generated) { // There should not be any regions in the code. this.AddViolation(token.FindParentElement(), token.LineNumber, Rules.AvoidStringFormatUseStringInterpolation); } } } } } }
/// <summary> /// Reads a member access expression. /// </summary> /// <param name="leftSide"> /// The left side of the expression. /// </param> /// <param name="previousPrecedence"> /// The precedence of the previous expression. /// </param> /// <param name="unsafeCode"> /// Indicates whether the code is marked as unsafe. /// </param> /// <returns> /// Returns the expression. /// </returns> private MemberAccessExpression GetMemberAccessExpression(Expression leftSide, ExpressionPrecedence previousPrecedence, bool unsafeCode) { Param.AssertNotNull(leftSide, "leftSide"); Param.Ignore(previousPrecedence); Param.Ignore(unsafeCode); MemberAccessExpression expression = null; OperatorType operatorType; MemberAccessExpression.Operator expressionOperatorType; ExpressionPrecedence precedence; Reference<ICodePart> expressionReference = new Reference<ICodePart>(); // The next symbol must one of the member access types. Symbol symbol = this.GetNextSymbol(expressionReference); if (symbol.SymbolType == SymbolType.Dot) { operatorType = OperatorType.MemberAccess; expressionOperatorType = MemberAccessExpression.Operator.Dot; precedence = ExpressionPrecedence.Primary; } else if (symbol.SymbolType == SymbolType.Pointer) { operatorType = OperatorType.Pointer; expressionOperatorType = MemberAccessExpression.Operator.Pointer; precedence = ExpressionPrecedence.Primary; } else if (symbol.SymbolType == SymbolType.QualifiedAlias) { operatorType = OperatorType.QualifiedAlias; expressionOperatorType = MemberAccessExpression.Operator.QualifiedAlias; precedence = ExpressionPrecedence.Global; } else { Debug.Fail("Unexpected operator type"); throw new InvalidOperationException(); } // Check the precedence. A member access has primary precedence. if (CheckPrecedence(previousPrecedence, precedence)) { // Add this to the document. this.tokens.Add(this.GetOperatorToken(operatorType, expressionReference)); // Get the member being accessed. This must be a literal. LiteralExpression member = this.GetLiteralExpression(expressionReference, unsafeCode); if (member == null) { throw this.CreateSyntaxException(); } // Create the token list. CsTokenList partialTokens = new CsTokenList(this.tokens, leftSide.Tokens.First, this.tokens.Last); // Create the expression. expression = new MemberAccessExpression(partialTokens, expressionOperatorType, leftSide, member); expressionReference.Target = expression; } return expression; }
/// <summary> /// The save. /// </summary> /// <param name="operator"> /// The operator. /// </param> private void Save(MemberAccessExpression.Operator @operator) { var operatorString = string.Empty; switch (@operator) { case MemberAccessExpression.Operator.Dot: operatorString = "."; break; case MemberAccessExpression.Operator.Pointer: operatorString = "->"; break; case MemberAccessExpression.Operator.QualifiedAlias: operatorString = "::"; break; default: break; } this.cppWriter.Write(operatorString); }
// todo: reduce it private MemberAccessExpression.Operator GetActualMemberAccess( MemberAccessExpression memberAccessExpression, TypeResolver typeResolverReference, IEnumerable<ExpressionReturnTypeResolver.ResolvedContextItem> resolvedCodeElements, bool memberWasFound) { if (memberAccessExpression.OperatorType == MemberAccessExpression.Operator.Dot) { if (typeResolverReference.IsResolved && (typeResolverReference.IsClassName || typeResolverReference.IsEnum)) { return MemberAccessExpression.Operator.QualifiedAlias; } if (resolvedCodeElements.Any()) { var first = resolvedCodeElements.First(); var typeDefinitionMetadataICodeElementAdapterBase = first.Type as TypeDefinitionMetadataICodeElementAdapterBase; if (typeDefinitionMetadataICodeElementAdapterBase != null && typeDefinitionMetadataICodeElementAdapterBase.IsValueType) { return MemberAccessExpression.Operator.Dot; } return MemberAccessExpression.Operator.Pointer; } if (memberAccessExpression.LeftHandSide is LiteralExpression && typeResolverReference.IsResolved && !typeResolverReference.IsFieldOrVariable) { return MemberAccessExpression.Operator.QualifiedAlias; } var current = memberAccessExpression; while (current != null) { if (current.LeftHandSide is MethodInvocationExpression) { return MemberAccessExpression.Operator.Dot; } current = current.LeftHandSide as MemberAccessExpression; } return memberWasFound ? MemberAccessExpression.Operator.Dot : MemberAccessExpression.Operator.QualifiedAlias; } return memberAccessExpression.OperatorType; }
/// <summary> /// The save. /// </summary> /// <param name="memberAccessExpression"> /// The member access expression. /// </param> private void Save(MemberAccessExpression memberAccessExpression) { // todo: investigate why it returns multiple result with the same methods var expressionReturnTypeResolver = new ExpressionReturnTypeResolver(this); var resolvedCodeElements = expressionReturnTypeResolver.Resolve(memberAccessExpression.LeftHandSide); var members = resolvedCodeElements.ToList(); var resolvedTypeReference = new TypeResolver(memberAccessExpression.LeftHandSide.Text, this); if (resolvedTypeReference.IsClassName || resolvedTypeReference.IsEnum) { bool isFullyQualified = memberAccessExpression.LeftHandSide.Text.StartsWith(resolvedTypeReference.Namespace); this.cppWriter.Write( isFullyQualified ? resolvedTypeReference.GetCXFullyQualifiedName(SavingOptions.None) : resolvedTypeReference.GetCxName(SavingOptions.None)); } else { @switch(memberAccessExpression.LeftHandSide); } this.Save(this.GetActualMemberAccess(memberAccessExpression, resolvedTypeReference, members, expressionReturnTypeResolver.IsMemberFound)); @switch(memberAccessExpression.RightHandSide); }