From 773be4fc26fdc380e98c706fc4513ed477c1cd54 Mon Sep 17 00:00:00 2001 From: dragonmacher <48328597+dragonmacher@users.noreply.github.com> Date: Thu, 11 Jun 2026 12:20:17 -0400 Subject: [PATCH] GP-6691 - Find Uses of - Fixed flaws in finding structure field uses in the DataTypeReference API --- .../ghidra/app/services/FieldMatcher.java | 6 +- .../finder/AnonymousVariableAccessDR.java | 34 +- .../DecompilerDataTypeReferenceFinder.java | 43 +- .../datatype/finder/DecompilerReference.java | 3 +- .../datatype/finder/VariableAccessDR.java | 424 ++++++++++++++---- .../extension/datatype/finder/VariableDR.java | 1 + 6 files changed, 394 insertions(+), 117 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/services/FieldMatcher.java b/Ghidra/Features/Base/src/main/java/ghidra/app/services/FieldMatcher.java index 6e041c0157..4edfae1d22 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/services/FieldMatcher.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/services/FieldMatcher.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -94,7 +94,7 @@ public class FieldMatcher { } return dataType.getName() + " at " + fieldOffsets.toString(); } - return dataType.getName(); + return dataType.getName() + " "; } private String generateCompositeFieldNameByOffset() { diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/AnonymousVariableAccessDR.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/AnonymousVariableAccessDR.java index db8d25152c..7840b23f05 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/AnonymousVariableAccessDR.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/AnonymousVariableAccessDR.java @@ -17,12 +17,15 @@ package ghidra.app.extension.datatype.finder; import java.util.List; +import docking.widgets.search.SearchLocationContext; import ghidra.app.decompiler.ClangFieldToken; import ghidra.app.decompiler.ClangLine; import ghidra.app.services.DataTypeReference; import ghidra.app.services.FieldMatcher; +import ghidra.program.model.address.Address; import ghidra.program.model.data.Composite; import ghidra.program.model.data.DataType; +import ghidra.program.model.listing.Function; /** * This class represents the use of a field of a {@link Composite} data type where there is no @@ -84,12 +87,18 @@ public class AnonymousVariableAccessDR extends VariableAccessDR { // referring to the field and not the composite. // if (fieldMatcher.isIgnored()) { + if (matchesFieldType) { - // no field name and the search type matches this reference's field type - String fieldName = variable.getName(); - results.add(createReference(variable, fieldName)); + // no field to match and the search type matches this composite's field type + String fieldName = null; + DataTypeReference ref = createReferenceToVariable(variable, fieldName); + results.add(ref); } - // else there is no field and the search type does not match the reference's type + + // else there is no field to match and the search type matched the composite type; as + // mentioned in the comment above, we are ignoring the match on the composite, as + // that is handled by the VariableAccessDR, which gets created before this object as the + // tokens are being parsed. return; } @@ -99,11 +108,20 @@ public class AnonymousVariableAccessDR extends VariableAccessDR { // The client has requested a particular field of the parent composite. We only have a // match if the parent type matches and the field name/offset matches. // - String text = field.getText(); + String fieldName = field.getText(); int offset = field.getOffset(); - if (matchesCompositeType && fieldMatcher.matches(text, offset)) { - results.add(new DataTypeReference(compositeType, fieldMatcher.getFieldName(), - getFunction(), getAddress(), getContext())); + if (!fieldMatcher.matches(fieldName, offset)) { + return; // the field does not match } + + if (matchesCompositeType) { + Function function = getFunction(); + Address address = getAddress(); + SearchLocationContext context = getContext(); + results.add( + new DataTypeReference(compositeType, fieldName, function, address, context)); + } + // else, matches the composite's field type, but not the parent type; this fails the + // requirement that the composite type must match when searching for a field } } diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerDataTypeReferenceFinder.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerDataTypeReferenceFinder.java index e583baae85..01ed823fa4 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerDataTypeReferenceFinder.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerDataTypeReferenceFinder.java @@ -478,8 +478,12 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde DtrfDbg.println(function, "\tcreating an anonymous variable access: " + line); - // this can happen when a field is used anonymously, such as directly - // after a nested array index operation + // This can happen when a field is used anonymously, such as directly + // after a nested array index operation. 'anonymous' here means that there + // is no named variable in the decompiler that is being dereferenced. In + // addition to seeing this for array accesses, we will also see this for + // nested composite access, like foo->bar->baz. 'baz' is being accessed + // anonymously. results.add(new AnonymousVariableAccessDR(line, field)); continue; } @@ -497,15 +501,38 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde return false; // should not happen } + /* + Unusual Code: The data type of 'field' is usually the parent structure that contains + the field (not always though?). Also, apparently sometimes the data type of the + 'variable' in 'access' is that of the field being accessed instead of the parent + structure. I'm guessing these conditions are bugs. Normally, the data type of + 'variable' should be the parent containing type and the data type of 'field' should + be contained in the parent type. + + This is used to determine when parent->child relationship is not correct so that we + can create an special access type to represent that. + */ + + DataType parentType = field.getDataType(); + DataType variableType = variable.getDataType(); + if (DecompilerReference.isEqual(parentType, variableType)) { + return false; // the types match; this is the normal flow + } + // Note: the field's type is that of the parent structure, not the field. We want the // field's type, so we must retrieve that. - DataType fieldDt = DecompilerReference.getFieldDataType(field); + DataType fieldType = DecompilerReference.getFieldDataType(field); + + // check for the unusual case where the 'variable' type may be that of the field + if (DecompilerReference.isEqual(variableType, fieldType)) { + + // Note: it would be nice to find a case(s) to verify this path + + return false; // the types match; this is the normal flow + } + + return true; - // unusual code: getDataType() on the variable may return the type of the field being - // accessed. Contrastingly, getDataType() on the field may return the - // type of the parent structure. - DataType variableDt = variable.getDataType(); - return !DecompilerReference.isEqual(variableDt, fieldDt); } private VariableAccessDR getLastAccess(List variables) { diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerReference.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerReference.java index f6f4b41fd1..4311fa6f81 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerReference.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerReference.java @@ -88,8 +88,7 @@ public abstract class DecompilerReference { } protected SearchLocationContext getContext() { - SearchLocationContext context = getContext(variable); - return context; + return getContext(variable); } protected SearchLocationContext getContext(DecompilerVariable var) { diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/VariableAccessDR.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/VariableAccessDR.java index 54ce0c662b..8def7a3074 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/VariableAccessDR.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/VariableAccessDR.java @@ -61,106 +61,310 @@ public class VariableAccessDR extends DecompilerReference { public void accumulateMatches(DataType dt, FieldMatcher fieldMatcher, List results) { - if (fields.isEmpty()) { - DecompilerVariable var = getMatch(dt, fieldMatcher, variable, null); - if (var != null) { - String fieldName = null; - DataTypeReference ref = createReference(var, fieldName); - results.add(ref); - } + // simplify how we search depending on whether a field is required and if we have fields + if (fieldMatcher.isIgnored()) { + accumulateMatchesForTypeOnly(dt, results); return; } - // - // Walk each pair of type and variables in order to see if any of them match our - // criteria - // - DecompilerVariable start = variable; + if (fields.isEmpty()) { + // We have a valid field matcher, but no fields. Look for special case direct field + // usage, such as an enum name used inline, without its containing class. + accumulateMatchesDirectFields(dt, fieldMatcher, results); + return; + } + + /* + This variable may have fields being accessed. We need to check for the data type usage + in this variable and each of the fields that we find. We need to make sure the fields + match as well. For the given text below we will ask the following questions: + + Foo->bar->baz + + - Does Foo match the type and 'bar' match the field matcher? + - Does bar match the type and 'baz' match the field matcher? + - Does baz match the type? + + + Known Special Cases: + + A) Foo is reported as the same type as bar: + i. both are actually the desired type + ii. the field is not really the desired type + B) Foo's type is not a Foo, bar's type is Foo instead + + */ + + DecompilerVariable parent = variable; for (DecompilerVariable field : fields) { - DecompilerVariable next = field; - DecompilerVariable var = getMatch(dt, fieldMatcher, start, next); - if (var != null) { - DataTypeReference ref = createReference(var, next); + // 1) See if the variable matches the type + DataTypeReference ref = createStandardReference(dt, parent, field, fieldMatcher); + if (ref != null) { results.add(ref); + parent = field; + continue; } - start = next; + // 2) See if both types match (case A from above) + // (At this point, either the parent type does not match, or the parent and the field + // share the same type.) + ref = createReferenceWhenVariableAndFieldMatch(dt, parent, field, fieldMatcher); + if (ref != null) { + results.add(ref); + parent = field; + continue; + } + + // 3) See if the field matches the type (case B from above) + // (At this point the variable doesn't match, or the field doesn't match.) + ref = createFieldReference(dt, parent, field, fieldMatcher); + if (ref != null) { + results.add(ref); + parent = field; + continue; + } } - if (fieldMatcher.isIgnored()) { + // Note: since the last field in the loop above does not itself have a field, then it + // cannot match the requirement of a parent type followed by a field match. + } + + private void accumulateMatchesForTypeOnly(DataType dt, List results) { + + if (fields.isEmpty()) { + // No fields to check. See if this class's variable is a match. + DecompilerVariable var = getMatchingVariable(dt, variable); + if (var != null) { + String fieldName = null; + DataTypeReference ref = createReferenceToVariable(var, fieldName); + results.add(ref); + } return; } - DecompilerVariable var = getMatch(dt, fieldMatcher, start, null); + // This class's variable and each field may match the type. In this loop we will check each + // combination of a data type and the field, making a reference for all matches. Each pass + // through the loop makes the next field become the parent for the following field. The last + // field is handled below the loop. + DecompilerVariable parent = variable; + for (DecompilerVariable field : fields) { + DecompilerVariable var = getMatchingVariable(dt, parent); + parent = field; + if (var == null) { + continue; // the current 'parent' does not match + } + + // Note: handle the bug condition where the variable and the field both have the type of + // interest. + DataTypeReference ref; + DecompilerVariable preferred = updateVarForBugCondition(var, field); + if (preferred == var) { + String fieldName = field.getName(); + ref = createReferenceToVariable(var, fieldName); + } + else { // preferred == field + + // if we encounter the bug condition where the field is the actual type we seek, + // then don't use the field name, as the field is the reference. + String fieldName = null; + ref = createReferenceToField(preferred, fieldName); + } + + results.add(ref); + } + + // Handle the last field. In this case, we cannot have a parent and field match, only a + // match on the data type with no field. + DecompilerVariable var = getMatchingVariable(dt, parent); if (var != null) { - DataTypeReference ref = createReference(var, fieldMatcher.getFieldName()); + String fieldName = null; + DataTypeReference ref = createReferenceToVariable(var, fieldName); results.add(ref); } } + private DecompilerVariable updateVarForBugCondition(DecompilerVariable var, + DecompilerVariable field) { + + if (isFieldTheBetterMatch(var, field)) { + // assume a real match for a self-referencing structure + return field; + } + + return var; + } + + private boolean isFieldTheBetterMatch(DecompilerVariable var, DecompilerVariable field) { + + // This is an odd case where both the parent and the field have the same type. This may be + // valid, such as when a structure contains a reference to itself. But, this also can + // happen when the decompiler makes mistakes when setting the data type. + + // This type may not be the actual field type. We check on that below when these types are + // the same. + DataType fdt = field.getDataType(); + DataType vdt = var.getDataType(); + if (!isEqual(vdt, fdt)) { + return false; // the original var is the correct variable + } + + // We have the same type. If the 2 types are really the same (self-refernecing structure), + // then we want a match for the parent to the field. If the types are not really the same, + // then for now assume the field itself is the correct match. + + // Note: this had been using 'sourceToken', which doesn't seem right. If we find missed + // cases where 'field.variable' is not working, then revisit using 'sourceToken' as a + // fallback check. + //ClangFieldToken fieldToken = (ClangFieldToken) sourceToken; + if (!(field.variable instanceof ClangFieldToken fieldToken)) { + return false; // cannot check the field's type + } + + DataType actualFieldDt = DecompilerReference.getFieldDataType(fieldToken); + boolean matchesFieldType = isEqual(vdt, actualFieldDt); + if (matchesFieldType) { + // assume a real match for a self-referencing structure + return true; + } + + return false; + } + + private void accumulateMatchesDirectFields(DataType dt, FieldMatcher fieldMatcher, + List results) { + + DecompilerVariable var = getMatch(dt, fieldMatcher, variable); + if (var != null) { + String fieldName = fieldMatcher.getFieldName(); + DataTypeReference ref = createReferenceToField(var, fieldName); + results.add(ref); + } + } + + private DataTypeReference createStandardReference(DataType dt, DecompilerVariable parent, + DecompilerVariable field, FieldMatcher fieldMatcher) { + + // a standard reference is the case where the parent is the type we seek and the field + // matches the matcher + DecompilerVariable var = getMatchingVariable(dt, parent); + if (var == null) { + return null; + } + + DataType vdt = var.getDataType(); + DataType fdt = field.getDataType(); + if (isEqual(vdt, fdt)) { + return null; // this may be an error case; handled by a different method later + } + + String fieldName = field.getName(); + int offset = field.getOffset(); + if (fieldMatcher.matches(fieldName, offset)) { + return createReference(var, field, fieldMatcher); + } + + return null; + } + + private DataTypeReference createFieldReference(DataType dt, DecompilerVariable parent, + DecompilerVariable field, FieldMatcher fieldMatcher) { + + // Note: this method should only be called if we did not create a 'standard reference', + // which is when the parent type matches 'dt' and the field type does not match. Getting + // into this method means either the parent type did not match, or the parent and field both + // match the type. + + /* + This is the case where the parent variable was not the correct type, but we need to see + if the field itself is the type we seek. I believe this comment describes this case: + + // check for the case where the given 'field' may be the type we seek, such as in an if + // statement like this: + // if (color == RED) + // where 'RED' is the variable we are checking + */ + HighVariable highVariable = parent.variable.getHighVariable(); + if (highVariable == null) { + return null; // not sure of the significance of this check + } + + if (!matchesParentType(field, dt)) { + return null; + } + + String fieldName = field.getName(); + int offset = field.getOffset(); + if (fieldMatcher.matches(fieldName, offset)) { + // the field matches the type and the name + return createReference(field, field, fieldMatcher); + } + return null; + } + + private DataTypeReference createReferenceWhenVariableAndFieldMatch(DataType dt, + DecompilerVariable parent, DecompilerVariable field, FieldMatcher fieldMatcher) { + + // This is an odd case where both the parent and the field have the same type. This may be + // valid, such as when a structure contains a reference to itself. But, this also can + // happen when the decompiler makes mistakes when setting the data type. + DecompilerVariable var = getMatchingVariable(dt, parent); + if (var == null) { + return null; + } + + String name = field.getName(); + int offset = field.getOffset(); + if (!fieldMatcher.matches(name, offset)) { + return null; + } + + if (isFieldTheBetterMatch(var, field)) { + // this implies a self-referential structure; create a reference to the field + return createReference(field, field, fieldMatcher); + } + + // At this point, the parent matches the data type, but the field itself is not actually + // a data type match. Also, since we checked above, we know the field name matches. Create + // a standard reference. + return createReference(var, field, fieldMatcher); + } + private DecompilerVariable getMatch(DataType dt, FieldMatcher fieldMatcher, - DecompilerVariable var, DecompilerVariable potentialField) { + DecompilerVariable var) { String indent = "\t\t\t"; - // Note: for now, I ignore the precedence of casting; if any cast type is a match, then - // signal hooray - boolean searchForField = !fieldMatcher.isIgnored(); - DecompilerVariable fieldVar = searchForField ? potentialField : null; - DecompilerVariable match = getMatchingVarialbe(dt, var, fieldVar); + DecompilerVariable match = getMatchingVariable(dt, var); if (match == null) { DtrfDbg.println(getFunction(), this, indent + "NO MATCHING VARIABLE"); return null; // wrong type, nothing to do } - // Matches on the type, does the field match? - if (fieldMatcher.isIgnored()) { - DtrfDbg.println(getFunction(), this, - indent + "field macher is ignored; returning match"); - return match; // no field to match - } - - if (potentialField == null) { - - DtrfDbg.println(getFunction(), this, - indent + "No potential field to match; name / offset match?"); - - // check for the case where we have not been passed a 'potential field', but the given - // 'var' is itself may be the field we seek, such as in an if statement like this: - // if (color == RED) - // where 'RED' is the variable we are checking - String name = var.getName(); - int offset = var.getOffset(); - if (fieldMatcher.matches(name, offset)) { - StringUtilities.indentLines(var.toString(), indent + '\t'); - DtrfDbg.println(getFunction(), this, - indent + "\tfield matcher matched on variable: " + var); - return var; - } - - DtrfDbg.println(getFunction(), this, indent + "\tNO FIELD MATCHER MATCH"); - return null; // we seek a field, but there is none - } - - DtrfDbg.println(getFunction(), this, indent + "Checking 'potential field' match..."); - - String name = potentialField.getName(); - int offset = potentialField.getOffset(); + // check for the case where we have not been passed a 'potential field', but the given + // 'var' is itself may be the field we seek, such as in an if statement like this: + // if (color == RED) + // where 'RED' is the variable we are checking + String name = var.getName(); + int offset = var.getOffset(); if (fieldMatcher.matches(name, offset)) { - DtrfDbg.println(getFunction(), this, indent + "\tMATCHED"); - return match; + StringUtilities.indentLines(var.toString(), indent + '\t'); + DtrfDbg.println(getFunction(), this, + indent + "\tfield matcher matched on variable: " + var); + return var; } - DtrfDbg.println(getFunction(), this, indent + "\tNO MATCH"); - return null; + + DtrfDbg.println(getFunction(), this, indent + "\tNO FIELD MATCHER MATCH"); + return null; // we seek a field, but there is none } - private DecompilerVariable getMatchingVarialbe(DataType dt, DecompilerVariable var, - DecompilerVariable potentialField) { + // return the first matching cast for the given variable, if any + private DecompilerVariable getFirstMatchingCast(DataType dt, DecompilerVariable var) { String indent = "\t\t\t"; - DtrfDbg.println(getFunction(), this, indent + "Checking for matching variable; any casts?"); + DtrfDbg.println(getFunction(), this, indent + + "Checking for matching variable (no fields); any casts?"); List castVariables = var.getCasts(); for (DecompilerVariable cast : castVariables) { if (matchesType(cast, dt)) { @@ -168,6 +372,20 @@ public class VariableAccessDR extends DecompilerReference { return cast; } } + return null; + } + + private DecompilerVariable getMatchingVariable(DataType dt, DecompilerVariable var) { + + String indent = "\t\t\t"; + + // Note: for now, we ignore the precedence of casting; if any cast type is a match, then + // signal hooray + DecompilerVariable cast = getFirstMatchingCast(dt, var); + if (cast != null) { + // assume that any match in the cast implies we will not match a variable access + return cast; + } String dtString = dt == null ? "null" : dt.toString(); DtrfDbg.println(getFunction(), this, @@ -178,29 +396,6 @@ public class VariableAccessDR extends DecompilerReference { DtrfDbg.println(getFunction(), this, indent + "MATCHED type: "); return var; } - - DtrfDbg.println(getFunction(), this, - indent + "Type did not match; checking High Variable: "); - - // - // Unusual Code Alert! - // It is a bit odd to check the field when you are looking for the type that contains the - // field. BUT, in the Decompiler, SOMETIMES the 'field' happens to have the data type of - // the thing that contains it. So, if you have: - // foo.bar - // then the 'bar' field will have a data type of Foo. Unfortunately, this is not always - // the case. For now, if there is a high variable, we need to check the field. Sad face - // emoji. - // - HighVariable highVariable = var.variable.getHighVariable(); - if (highVariable != null) { - if (matchesParentType(potentialField, dt)) { - DtrfDbg.println(getFunction(), this, indent + "MATCHED on parent type: " + dt); - return potentialField; - } - } - - DtrfDbg.println(getFunction(), this, indent + "NOT MATCHED"); return null; } @@ -227,37 +422,70 @@ public class VariableAccessDR extends DecompilerReference { if (varType == null) { // it seems odd to me that there is no type, but I have seen this in the case // statement of a switch - DtrfDbg.println(getFunction(), this, indent + "ypes Match? no variable TYPE to check"); + DtrfDbg.println(getFunction(), this, indent + "Types Match? no variable TYPE to check"); return false; } boolean matches = isEqual(varType, dt); return matches; } - protected DataTypeReference createReference(DecompilerVariable var, String fieldName) { - + protected DataTypeReference createReferenceToVariable(DecompilerVariable var, + String fieldName) { DataType dataType = var.getDataType(); - SearchLocationContext context = getContext(var); + SearchLocationContext context = getContextForVariable(var); Function function = var.getFunction(); Address address = getAddress(var); return new DataTypeReference(dataType, fieldName, function, address, context); } - private DataTypeReference createReference(DecompilerVariable var, DecompilerVariable field) { + protected DataTypeReference createReferenceToField(DecompilerVariable field, + String fieldName) { + DataType dataType = field.getDataType(); + SearchLocationContext context = getContextForField(field); + Function function = field.getFunction(); + Address address = getAddress(field); + return new DataTypeReference(dataType, fieldName, function, address, context); + } + + private DataTypeReference createReference(DecompilerVariable var, DecompilerVariable field, + FieldMatcher fieldMatcher) { DataType dataType = var.getDataType(); - SearchLocationContext context = getContext(var); + SearchLocationContext context = getContext(var, fieldMatcher); Function function = var.getFunction(); Address address = getAddress(var); - return new DataTypeReference(dataType, field.getName(), function, address, context); + String fieldName = field != null ? field.getName() : null; + return new DataTypeReference(dataType, fieldName, function, address, context); } @Override protected SearchLocationContext getContext(DecompilerVariable var) { + // Default to highlighting the field that is being called on the given variable. Clients of + // this method may choose to highlight the variable itself, in which case they should call + // getContextForVariable(var). + return getContextForField(var); + } + + private SearchLocationContext getContextForVariable(DecompilerVariable var) { + return super.getContext(var); + } + + protected SearchLocationContext getContextForField(DecompilerVariable var) { DecompilerVariable field = findFieldFor(var); SearchLocationContext context = super.getContext(field); return context; } + protected SearchLocationContext getContext(DecompilerVariable var, FieldMatcher fieldMatcher) { + if (fieldMatcher.isIgnored()) { + // when there is not field to match, we only want to highlight the variable itself + return super.getContext(var); + } + + // assume that a valid field matcher means that we have already matched before this call, + // so use the matching field for the context highlight + return getContextForField(var); + } + private DecompilerVariable findFieldFor(DecompilerVariable var) { // @@ -308,6 +536,10 @@ public class VariableAccessDR extends DecompilerReference { @Override public String toString() { + if (variable == null) { + return ""; + } + String subFieldsString = fields.isEmpty() ? "" : "\tsub fields: " + StringUtilities.toStringWithIndent(fields) + ",\n"; diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/VariableDR.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/VariableDR.java index 6cffe1f212..8c4384db09 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/VariableDR.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/VariableDR.java @@ -78,4 +78,5 @@ public abstract class VariableDR extends DecompilerReference { results.add(new DataTypeReference(dataType, fieldName, function, address, context)); } } + }