Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ public final class UserFacingMessages {

public static final class RuleDescriptions {
public static final String APEX_NULL_POINTER_EXCEPTION_RULE =
"Identfies Apex operations that dereference null objects and throw NullPointerExceptions.";
"Identifies Apex operations that dereference null objects and throw NullPointerExceptions.";
public static final String UNIMPLEMENTED_TYPE_RULE =
"Identifies abstract classes and interfaces that are non-global and don't have implementations or extensions.";
public static final String UNUSED_METHOD_RULE =
"Identifies methods that aren't invoked from recognized entry points.";
public static final String MULTIPLE_MASS_SCHEMA_LOOKUP_RULE =
"Detects mass schema lookups that can cause performance degradation if made more than once in a path. These methods are: Schema.getGlobalDescribe() and Schema.describeSObjects(...). Flagged lookups include those within a loop or multiple invocations in a path.";
}

public static final class RuleViolationTemplates {
Expand Down Expand Up @@ -75,4 +77,10 @@ public static final class CompilationErrors {
"Graph engine encountered compilation errors. Fix the errors in %s and retry.";
public static final String EXCEPTION_FORMAT_TEMPLATE = "%s, Caused by:\n%s";
}

public static final class MultipleMassSchemaLookupRuleTemplates {
public static final String MESSAGE_TEMPLATE = "%s was %s at %s:%d.";
public static final String OCCURRENCE_LOOP_TEMPLATE = "called inside a %s";
public static final String OCCURRENCE_MULTIPLE_TEMPLATE = "preceded by a call to %s";
}
Comment on lines +81 to +85
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved UI text.

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,7 @@
import com.salesforce.graph.ApexPath;
import com.salesforce.graph.symbols.DefaultNoOpScopeVisitor;
import com.salesforce.graph.symbols.SymbolProvider;
import com.salesforce.graph.vertex.AssignmentExpressionVertex;
import com.salesforce.graph.vertex.BaseSFVertex;
import com.salesforce.graph.vertex.BlockStatementVertex;
import com.salesforce.graph.vertex.CatchBlockStatementVertex;
import com.salesforce.graph.vertex.DmlDeleteStatementVertex;
import com.salesforce.graph.vertex.DmlInsertStatementVertex;
import com.salesforce.graph.vertex.DmlMergeStatementVertex;
import com.salesforce.graph.vertex.DmlUndeleteStatementVertex;
import com.salesforce.graph.vertex.DmlUpdateStatementVertex;
import com.salesforce.graph.vertex.DmlUpsertStatementVertex;
import com.salesforce.graph.vertex.ElseWhenBlockVertex;
import com.salesforce.graph.vertex.EmptyReferenceExpressionVertex;
import com.salesforce.graph.vertex.ExpressionStatementVertex;
import com.salesforce.graph.vertex.FieldDeclarationStatementsVertex;
import com.salesforce.graph.vertex.FieldDeclarationVertex;
import com.salesforce.graph.vertex.FieldVertex;
import com.salesforce.graph.vertex.ForEachStatementVertex;
import com.salesforce.graph.vertex.ForLoopStatementVertex;
import com.salesforce.graph.vertex.IfBlockStatementVertex;
import com.salesforce.graph.vertex.IfElseBlockStatementVertex;
import com.salesforce.graph.vertex.LiteralExpressionVertex;
import com.salesforce.graph.vertex.MethodCallExpressionVertex;
import com.salesforce.graph.vertex.MethodVertex;
import com.salesforce.graph.vertex.ModifierNodeVertex;
import com.salesforce.graph.vertex.NewKeyValueObjectExpressionVertex;
import com.salesforce.graph.vertex.NewListLiteralExpressionVertex;
import com.salesforce.graph.vertex.NewObjectExpressionVertex;
import com.salesforce.graph.vertex.ParameterVertex;
import com.salesforce.graph.vertex.PrefixExpressionVertex;
import com.salesforce.graph.vertex.ReferenceExpressionVertex;
import com.salesforce.graph.vertex.ReturnStatementVertex;
import com.salesforce.graph.vertex.SoqlExpressionVertex;
import com.salesforce.graph.vertex.StandardConditionVertex;
import com.salesforce.graph.vertex.SuperMethodCallExpressionVertex;
import com.salesforce.graph.vertex.SwitchStatementVertex;
import com.salesforce.graph.vertex.ThrowStatementVertex;
import com.salesforce.graph.vertex.TryCatchFinallyBlockStatementVertex;
import com.salesforce.graph.vertex.ValueWhenBlockVertex;
import com.salesforce.graph.vertex.VariableDeclarationStatementsVertex;
import com.salesforce.graph.vertex.VariableDeclarationVertex;
import com.salesforce.graph.vertex.VariableExpressionVertex;
import com.salesforce.graph.vertex.*;

/**
* A visitor that does nothing, useful for deriving visitors. Delegates to {@link
Expand Down Expand Up @@ -284,6 +244,9 @@ public boolean visit(VariableExpressionVertex.Single vertex, SymbolProvider symb
@Override
public void afterVisit(BaseSFVertex vertex, SymbolProvider symbols) {}

@Override
public void afterVisit(DoLoopStatementVertex vertex, SymbolProvider symbols) {}

@Override
public void afterVisit(DmlDeleteStatementVertex vertex, SymbolProvider symbols) {}

Expand All @@ -302,6 +265,12 @@ public void afterVisit(DmlUpdateStatementVertex vertex, SymbolProvider symbols)
@Override
public void afterVisit(DmlUpsertStatementVertex vertex, SymbolProvider symbols) {}

@Override
public void afterVisit(ForEachStatementVertex vertex, SymbolProvider symbols) {}

@Override
public void afterVisit(ForLoopStatementVertex vertex, SymbolProvider symbols) {}

@Override
public void afterVisit(FieldDeclarationVertex vertex, SymbolProvider symbols) {}

Expand All @@ -322,4 +291,7 @@ public void afterVisit(StandardConditionVertex.Positive vertex, SymbolProvider s

@Override
public void afterVisit(ThrowStatementVertex vertex, SymbolProvider symbols) {}

@Override
public void afterVisit(WhileLoopStatementVertex vertex, SymbolProvider symbols) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,7 @@

import com.salesforce.graph.ApexPath;
import com.salesforce.graph.symbols.SymbolProvider;
import com.salesforce.graph.vertex.AssignmentExpressionVertex;
import com.salesforce.graph.vertex.BaseSFVertex;
import com.salesforce.graph.vertex.BlockStatementVertex;
import com.salesforce.graph.vertex.CatchBlockStatementVertex;
import com.salesforce.graph.vertex.DmlDeleteStatementVertex;
import com.salesforce.graph.vertex.DmlInsertStatementVertex;
import com.salesforce.graph.vertex.DmlMergeStatementVertex;
import com.salesforce.graph.vertex.DmlUndeleteStatementVertex;
import com.salesforce.graph.vertex.DmlUpdateStatementVertex;
import com.salesforce.graph.vertex.DmlUpsertStatementVertex;
import com.salesforce.graph.vertex.ElseWhenBlockVertex;
import com.salesforce.graph.vertex.EmptyReferenceExpressionVertex;
import com.salesforce.graph.vertex.ExpressionStatementVertex;
import com.salesforce.graph.vertex.FieldDeclarationStatementsVertex;
import com.salesforce.graph.vertex.FieldDeclarationVertex;
import com.salesforce.graph.vertex.FieldVertex;
import com.salesforce.graph.vertex.ForEachStatementVertex;
import com.salesforce.graph.vertex.ForLoopStatementVertex;
import com.salesforce.graph.vertex.IfBlockStatementVertex;
import com.salesforce.graph.vertex.IfElseBlockStatementVertex;
import com.salesforce.graph.vertex.LiteralExpressionVertex;
import com.salesforce.graph.vertex.MethodCallExpressionVertex;
import com.salesforce.graph.vertex.MethodVertex;
import com.salesforce.graph.vertex.ModifierNodeVertex;
import com.salesforce.graph.vertex.NewKeyValueObjectExpressionVertex;
import com.salesforce.graph.vertex.NewListLiteralExpressionVertex;
import com.salesforce.graph.vertex.NewObjectExpressionVertex;
import com.salesforce.graph.vertex.ParameterVertex;
import com.salesforce.graph.vertex.PrefixExpressionVertex;
import com.salesforce.graph.vertex.ReferenceExpressionVertex;
import com.salesforce.graph.vertex.ReturnStatementVertex;
import com.salesforce.graph.vertex.SoqlExpressionVertex;
import com.salesforce.graph.vertex.StandardConditionVertex;
import com.salesforce.graph.vertex.SuperMethodCallExpressionVertex;
import com.salesforce.graph.vertex.SwitchStatementVertex;
import com.salesforce.graph.vertex.ThrowStatementVertex;
import com.salesforce.graph.vertex.TryCatchFinallyBlockStatementVertex;
import com.salesforce.graph.vertex.ValueWhenBlockVertex;
import com.salesforce.graph.vertex.VariableDeclarationStatementsVertex;
import com.salesforce.graph.vertex.VariableDeclarationVertex;
import com.salesforce.graph.vertex.VariableExpressionVertex;
import com.salesforce.graph.vertex.*;

/**
* Visits vertices in a particular path. Return true if the visitor does not want to visit the
Expand Down Expand Up @@ -147,6 +107,8 @@ void recursionDetected(

void afterVisit(BaseSFVertex vertex, SymbolProvider symbols);

void afterVisit(DoLoopStatementVertex vertex, SymbolProvider symbols);

void afterVisit(DmlDeleteStatementVertex vertex, SymbolProvider symbols);

void afterVisit(DmlInsertStatementVertex vertex, SymbolProvider symbols);
Expand All @@ -159,6 +121,10 @@ void recursionDetected(

void afterVisit(DmlUpsertStatementVertex vertex, SymbolProvider symbols);

void afterVisit(ForEachStatementVertex vertex, SymbolProvider symbols);

void afterVisit(ForLoopStatementVertex vertex, SymbolProvider symbols);

void afterVisit(FieldDeclarationVertex vertex, SymbolProvider symbols);

void afterVisit(MethodCallExpressionVertex vertex, SymbolProvider symbols);
Expand All @@ -172,4 +138,6 @@ void recursionDetected(
void afterVisit(StandardConditionVertex.Positive vertex, SymbolProvider symbols);

void afterVisit(ThrowStatementVertex vertex, SymbolProvider symbols);

void afterVisit(WhileLoopStatementVertex vertex, SymbolProvider symbols);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.salesforce.rules;

import com.salesforce.config.UserFacingMessages;
import com.salesforce.graph.ApexPath;
import com.salesforce.graph.vertex.BaseSFVertex;
import com.salesforce.rules.multiplemassschemalookup.MultipleMassSchemaLookupRuleHandler;
import java.util.ArrayList;
import java.util.List;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;

/** Rule to detect possible performance degradations while invoking Schema.getGlobalDescribe(). */
public class MultipleMassSchemaLookupRule extends AbstractPathTraversalRule {

private final MultipleMassSchemaLookupRuleHandler ruleHandler;

private MultipleMassSchemaLookupRule() {
ruleHandler = MultipleMassSchemaLookupRuleHandler.getInstance();
}

@Override
public boolean test(BaseSFVertex vertex) {
return ruleHandler.test(vertex);
}

@Override
protected List<RuleThrowable> _run(GraphTraversalSource g, ApexPath path, BaseSFVertex vertex) {
List<RuleThrowable> violations = new ArrayList<>();

violations.addAll(ruleHandler.detectViolations(g, path, vertex));

return violations;
}

@Override
protected int getSeverity() {
return SEVERITY.HIGH.code;
}

@Override
protected String getDescription() {
return UserFacingMessages.RuleDescriptions.MULTIPLE_MASS_SCHEMA_LOOKUP_RULE;
}

@Override
protected String getCategory() {
return CATEGORY.PERFORMANCE.name;
}

@Override
protected boolean isEnabled() {
return false;
}

public static MultipleMassSchemaLookupRule getInstance() {
return MultipleMassSchemaLookupRule.LazyHolder.INSTANCE;
}

private static final class LazyHolder {
// Postpone initialization until first use
private static final MultipleMassSchemaLookupRule INSTANCE =
new MultipleMassSchemaLookupRule();
}
}
23 changes: 19 additions & 4 deletions sfge/src/main/java/com/salesforce/rules/PathBasedRuleRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.salesforce.graph.vertex.SFVertex;
import com.salesforce.rules.fls.apex.operations.FlsViolationInfo;
import com.salesforce.rules.fls.apex.operations.FlsViolationMessageUtil;
import com.salesforce.rules.multiplemassschemalookup.MultipleMassSchemaLookupInfo;
import com.salesforce.rules.ops.ProgressListener;
import com.salesforce.rules.ops.ProgressListenerProvider;
import java.util.*;
Expand Down Expand Up @@ -119,7 +120,8 @@ private void executeRulesOnPaths(List<ApexPath> paths) {
// This set holds the violations whose information couldn't be fully processed at creation
// time, and
// require post-processing.
final HashSet<FlsViolationInfo> incompleteThrowables = new HashSet<>();
final HashSet<FlsViolationInfo> flsViolationInfos = new HashSet<>();
final HashSet<MultipleMassSchemaLookupInfo> mmsLookupInfos = new HashSet<>();
// For each path...
for (ApexPath path : paths) {
// If the path's metadata is present...
Expand All @@ -142,7 +144,10 @@ private void executeRulesOnPaths(List<ApexPath> paths) {
// to the list of such
// objects.
if (ruleThrowable instanceof FlsViolationInfo) {
incompleteThrowables.add((FlsViolationInfo) ruleThrowable);
flsViolationInfos.add((FlsViolationInfo) ruleThrowable);
} else if (ruleThrowable instanceof MultipleMassSchemaLookupInfo) {
// FIXME: PR incoming with refactors to this portion
mmsLookupInfos.add((MultipleMassSchemaLookupInfo) ruleThrowable);
Comment on lines 146 to +150
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's some duplication here that can be streamlined.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm assuming the subsequent PR will involve refactoring this so it doesn't require a Set of MMS violations specifically?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I'm trying to move the ownership of conversion to the ViolationInfo object so that this portion can work on the higher-level interface.

} else if (ruleThrowable instanceof Violation) {
// If the violation is done, it can just go directly into the results
// set.
Expand All @@ -155,7 +160,8 @@ private void executeRulesOnPaths(List<ApexPath> paths) {
}
}

convertToViolations(incompleteThrowables);
convertFlsInfoToViolations(flsViolationInfos);
convertMmsInfoToViolations(mmsLookupInfos);

if (!foundVertex) {
// If no vertices were found, we should log something so that information isn't lost,
Expand All @@ -167,7 +173,16 @@ private void executeRulesOnPaths(List<ApexPath> paths) {
}
}

private void convertToViolations(HashSet<FlsViolationInfo> flsViolationInfos) {
private void convertMmsInfoToViolations(HashSet<MultipleMassSchemaLookupInfo> mmsLookupInfos) {
for (MultipleMassSchemaLookupInfo mmsLookupInfo : mmsLookupInfos) {
Violation.RuleViolation violation = mmsLookupInfo.convert();
violation.setPropertiesFromRule(MultipleMassSchemaLookupRule.getInstance());
violations.add(violation);
}
}

// TODO: Restructure to make this logic work on other types of violation info too
private void convertFlsInfoToViolations(HashSet<FlsViolationInfo> flsViolationInfos) {
// Consolidate/regroup FLS violations across paths so that there are no
// duplicates with different field sets for the same source/sink/dmlOperation
final HashSet<FlsViolationInfo> consolidatedFlsViolationInfos =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.salesforce.rules.multiplemassschemalookup;

import com.salesforce.config.UserFacingMessages;
import com.salesforce.graph.vertex.MethodCallExpressionVertex;
import com.salesforce.graph.vertex.SFVertex;

/** Utility to help with violation message creation on AvoidMassSchemaLookupRule */
public final class MassSchemaLookupInfoUtil {
private MassSchemaLookupInfoUtil() {}

public static String getMessage(MultipleMassSchemaLookupInfo info) {
return getMessage(
info.getSinkVertex().getFullMethodName(),
info.getRepetitionType(),
getOccurrenceInfoValue(info.getRepetitionType(), info.getRepetitionVertex()),
info.getRepetitionVertex().getDefiningType(),
info.getRepetitionVertex().getBeginLine());
}

public static String getMessage(
String sinkMethodName,
RuleConstants.RepetitionType repetitionType,
String occurrenceInfoValue,
String occurrenceClassName,
int occurrenceLine) {
final String occurrenceMessage = getOccurrenceMessage(repetitionType, occurrenceInfoValue);

return String.format(
UserFacingMessages.MultipleMassSchemaLookupRuleTemplates.MESSAGE_TEMPLATE,
sinkMethodName,
occurrenceMessage,
occurrenceClassName,
occurrenceLine);
}

private static String getOccurrenceInfoValue(
RuleConstants.RepetitionType repetitionType, SFVertex repetitionVertex) {
if (RuleConstants.RepetitionType.MULTIPLE.equals(repetitionType)) {
// Use method name on template message
return ((MethodCallExpressionVertex) repetitionVertex).getFullMethodName();
} else {
// Use Loop type on template message
return repetitionVertex.getLabel();
}
}

private static String getOccurrenceMessage(
RuleConstants.RepetitionType repetitionType, String value) {
return repetitionType.getMessage(value);
}
}
Loading