Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement logging warnings for deprecated param usages in funcion call and fix inconsistent behaviour in warnings in deprecated record fields #40545

Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a6538fc
Fix no warning in deprecated field-name
dulajdilshan May 31, 2023
5ca75c7
Add tests
dulajdilshan May 31, 2023
9d1530c
Fix naming grammar
dulajdilshan Jun 6, 2023
c1aae3e
Add more tests
dulajdilshan Jun 6, 2023
42b1c2c
Merge branch 'master' into fix/deprecate-record-field-40363
dulajdilshan Jun 28, 2023
9585a24
Improve tests
dulajdilshan Jul 4, 2023
93deae3
Merge branch 'master' into fix/deprecate-record-field-40363
dulajdilshan Jul 14, 2023
621b44b
Extract deprecate warning to a method
dulajdilshan Jul 14, 2023
a20ca5d
Implement deprecated usage warnings for func params
dulajdilshan Jul 14, 2023
f8469b7
Add more tests
dulajdilshan Jul 14, 2023
a05e734
Improve deprecate warning logic
dulajdilshan Jul 14, 2023
71b250f
Merge branch 'master' into fix/deprecate-record-field-40363
dulajdilshan Jul 25, 2023
4eda95c
Use referred type and refactor
dulajdilshan Jul 26, 2023
b3cfba1
Fix NPE if matching field not-found
dulajdilshan Jul 26, 2023
ed1f81c
Add deprecate warning for positional args for rest args
dulajdilshan Jul 28, 2023
0b9d0b0
Add more tests for deprecated param usages
dulajdilshan Jul 28, 2023
bb22c81
Merge branch 'master' into fix/deprecate-record-field-40363
dulajdilshan Aug 2, 2023
47e9001
Report deprecated usage warning for rest args
dulajdilshan Aug 2, 2023
964f8a3
Remove unreachable logic
dulajdilshan Aug 2, 2023
11543b7
Add support for complex deprecated scenes
dulajdilshan Aug 7, 2023
9225056
Replace with tag comparison
dulajdilshan Aug 7, 2023
9461771
Improved referredType usages
dulajdilshan Aug 7, 2023
eee15e9
Fixed unnecessary change in comment
dulajdilshan Aug 7, 2023
cae7e91
Fix checkstyle errors
dulajdilshan Aug 7, 2023
ca1a07a
Address review comments
dulajdilshan Aug 9, 2023
59a338d
Address review comments
dulajdilshan Aug 10, 2023
49f8e54
Remove null check for type
dulajdilshan Aug 10, 2023
a892bd5
Merge branch 'master' into fix/deprecate-record-field-40363
dulajdilshan Aug 10, 2023
5dc6764
Reduce indentation by adding continue;
dulajdilshan Aug 11, 2023
3f153e0
Merge branch 'master' into fix/deprecate-record-field-40363
dulajdilshan Aug 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.ballerinalang.model.tree.expressions.XMLNavigationAccess;
import org.ballerinalang.model.tree.statements.StatementNode;
import org.ballerinalang.model.tree.statements.VariableDefinitionNode;
import org.ballerinalang.model.types.TypeKind;
import org.ballerinalang.util.diagnostic.DiagnosticErrorCode;
import org.ballerinalang.util.diagnostic.DiagnosticHintCode;
import org.ballerinalang.util.diagnostic.DiagnosticWarningCode;
Expand Down Expand Up @@ -2233,21 +2234,59 @@
@Override
public void visit(BLangRecordLiteral recordLiteral, AnalyzerData data) {
List<RecordLiteralNode.RecordField> fields = recordLiteral.fields;
BType recLiteralType = recordLiteral.getBType();

for (RecordLiteralNode.RecordField field : fields) {
if (field.isKeyValueField()) {
analyzeExpr(((BLangRecordKeyValueField) field).valueExpr, data);

BVarSymbol fieldSymbol = ((BLangRecordKeyValueField) field).key.fieldSymbol;
if (fieldSymbol != null) {
dulajdilshan marked this conversation as resolved.
Show resolved Hide resolved
reportIfDeprecatedUsage(fieldSymbol, fieldSymbol.toString(),
recordLiteral, ((BLangRecordKeyValueField) field).pos);
}
} else if (field.getKind() == NodeKind.SIMPLE_VARIABLE_REF) {
analyzeExpr((BLangRecordLiteral.BLangRecordVarNameField) field, data);
BLangRecordLiteral.BLangRecordVarNameField recField
= (BLangRecordLiteral.BLangRecordVarNameField) field;
analyzeExpr(recField, data);

if (recLiteralType.tsymbol != null && recLiteralType.tsymbol.type != null
&& recLiteralType.tsymbol.type.getKind() == TypeKind.RECORD) {
dulajdilshan marked this conversation as resolved.
Show resolved Hide resolved

BRecordType recordType = (BRecordType) recLiteralType.tsymbol.type;
BField matchingField = recordType.getFields().get(recField.symbol.getName().getValue());

if (matchingField != null && matchingField.symbol != null) {
dulajdilshan marked this conversation as resolved.
Show resolved Hide resolved
reportIfDeprecatedUsage(matchingField.symbol, matchingField.name.getValue(),
recordLiteral, ((BLangRecordLiteral.BLangRecordVarNameField) field).pos);
}
}
} else {
analyzeExpr(((BLangRecordLiteral.BLangRecordSpreadOperatorField) field).expr, data);
BLangRecordLiteral.BLangRecordSpreadOperatorField spreadField
= (BLangRecordLiteral.BLangRecordSpreadOperatorField) field;
analyzeExpr(spreadField.expr, data);

BType spreadFieldType = Types.getReferredType(spreadField.expr.getBType());
BType contextType = Types.getReferredType(recLiteralType);

if (spreadFieldType != null && spreadFieldType.getKind() == TypeKind.RECORD
&& contextType.getKind() == TypeKind.RECORD) {
for (BField fieldEntry: ((BRecordType) spreadFieldType).getFields().values()) {
BRecordType recordType = (BRecordType) contextType;
BField matchingField = recordType.getFields().get(fieldEntry.getName().getValue());

if (matchingField != null && matchingField.symbol != null) {
reportIfDeprecatedUsage(matchingField.symbol, matchingField.name.getValue(),
recordLiteral, spreadField.expr.pos);
}
}
}
}
}

Set<Object> names = new HashSet<>();
Set<Object> neverTypedKeys = new HashSet<>();
BType literalBType = recordLiteral.getBType();
BType type = Types.getReferredType(literalBType);
BType type = Types.getReferredType(recLiteralType);
boolean isRecord = type.tag == TypeTags.RECORD;
boolean isOpenRecord = isRecord && !((BRecordType) type).sealed;

Expand Down Expand Up @@ -2465,10 +2504,8 @@
BLangExpression expr = fieldAccessExpr.expr;
analyzeExpr(expr, data);
BSymbol symbol = fieldAccessExpr.symbol;
if (symbol != null && Symbols.isFlagOn(fieldAccessExpr.symbol.flags, Flags.DEPRECATED)) {
String deprecatedConstruct = generateDeprecatedConstructString(expr, fieldAccessExpr.field.toString(),
symbol);
dlog.warning(fieldAccessExpr.pos, DiagnosticWarningCode.USAGE_OF_DEPRECATED_CONSTRUCT, deprecatedConstruct);
if (symbol != null) {
reportIfDeprecatedUsage(symbol, fieldAccessExpr.field.toString(), expr, fieldAccessExpr.pos);
}
}

Expand Down Expand Up @@ -2496,6 +2533,7 @@
logDeprecatedWarningForInvocation(invocationExpr);
}
}
analyzeInvocationParams(invocationExpr, data);
}

@Override
Expand Down Expand Up @@ -3875,6 +3913,57 @@
&& data.withinTransactionScope;
}

private void analyzeInvocationParams(BLangInvocation iExpr, AnalyzerData data) {
if (iExpr.symbol == null) {
return;
}

BType invocableType = Types.getReferredType(iExpr.symbol.type);
if (invocableType.tag != TypeTags.INVOKABLE) {
return;

Check warning on line 3923 in compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/CodeAnalyzer.java

View check run for this annotation

Codecov / codecov/patch

compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/CodeAnalyzer.java#L3923

Added line #L3923 was not covered by tests
dulajdilshan marked this conversation as resolved.
Show resolved Hide resolved
}

BInvokableSymbol invokableSymbol = ((BInvokableSymbol) iExpr.symbol);
int parameterCountForPositionalArgs = ((BInvokableType) invocableType).getParameterTypes().size();

int i = 0;
boolean foundNamedArg = false;
for (BLangExpression expr : iExpr.argExprs) {
switch (expr.getKind()) {
case NAMED_ARGS_EXPR:
foundNamedArg = true;
BLangNamedArgsExpression namedArgsExpression = (BLangNamedArgsExpression) expr;
if (namedArgsExpression.varSymbol != null) {
reportIfDeprecatedUsage(namedArgsExpression.varSymbol, namedArgsExpression.varSymbol.toString(),
expr, expr.pos);
}
i++;
break;
case REST_ARGS_EXPR:
if (foundNamedArg) {
continue;

Check warning on line 3944 in compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/CodeAnalyzer.java

View check run for this annotation

Codecov / codecov/patch

compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/CodeAnalyzer.java#L3944

Added line #L3944 was not covered by tests
dulajdilshan marked this conversation as resolved.
Show resolved Hide resolved
}
analyzeExpr(expr, data);
break;
default: // positional args
if (foundNamedArg) {
continue;

Check warning on line 3950 in compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/CodeAnalyzer.java

View check run for this annotation

Codecov / codecov/patch

compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/CodeAnalyzer.java#L3950

Added line #L3950 was not covered by tests
}
if (i < parameterCountForPositionalArgs) {
BVarSymbol paramSymbol = invokableSymbol.params.get(i);
if (paramSymbol != null && Symbols.isFlagOn(paramSymbol.flags, Flags.DEPRECATED)) {
logDeprecatedWaring(paramSymbol.toString(), paramSymbol, expr.pos);
}
if (Symbols.isFlagOn(invokableSymbol.params.get(i).flags, Flags.INCLUDED)) {
analyzeExpr(expr, data);
}
}
i++;

}
}
}

private void validateModuleInitFunction(BLangFunction funcNode) {
if (funcNode.attachedFunction || !Names.USER_DEFINED_INIT_SUFFIX.value.equals(funcNode.name.value)) {
return;
Expand Down Expand Up @@ -3925,6 +4014,16 @@
return errorType;
}

private void reportIfDeprecatedUsage(BSymbol constructSymbol,
String constructName,
BLangExpression expr,
Location usagePos) {
if (Symbols.isFlagOn(constructSymbol.flags, Flags.DEPRECATED)) {
dlog.warning(usagePos, DiagnosticWarningCode.USAGE_OF_DEPRECATED_CONSTRUCT,
generateDeprecatedConstructString(expr, constructName, constructSymbol));
}
}

/**
* This class contains the state machines for a set of workers.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,13 @@ public void testDeprecationAnnotation() {
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Object3.fieldOne' is deprecated", 194, 9);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Object3.t' is deprecated", 195, 9);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'add1' is deprecated", 200, 13);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'x' is deprecated", 200, 18);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'y' is deprecated", 200, 21);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'z' is deprecated", 200, 24);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'add2' is deprecated", 201, 13);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'x' is deprecated", 201, 18);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'y' is deprecated", 201, 21);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'z' is deprecated", 201, 24);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Object1' is deprecated", 202, 5);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'z' is deprecated", 213, 13);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Foo' is deprecated", 216, 38);
Expand All @@ -86,20 +92,45 @@ public void testDeprecationAnnotation() {
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Person.name' is deprecated", 287, 16);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Person.getName' is deprecated", 288, 16);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'myFunction' is deprecated", 298, 5);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Employee.name' is deprecated", 316, 26);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Employee.job' is deprecated", 316, 49);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Job.experience' is deprecated", 316, 68);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Employee.name' is deprecated", 317, 9);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Employee.job' is deprecated", 319, 9);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Employee.job' is deprecated", 320, 9);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Employee.job' is deprecated", 321, 9);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Job.experiance' is deprecated", 321, 9);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Job.experience' is deprecated", 321, 9);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Employee2.name' is deprecated", 343, 28);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Employee2.job' is deprecated", 343, 51);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Job.experience' is deprecated", 343, 70);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Employee2.name' is deprecated", 344, 9);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Employee2.job' is deprecated", 346, 9);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'name' is deprecated", 354, 17);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'name' is deprecated", 357, 9);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'name' is deprecated", 370, 17);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'name' is deprecated", 373, 9);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Employee5.address' is deprecated",
393, 28);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Employee5.address' is deprecated",
394, 9);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Employee5.address' is deprecated",
395, 9);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'line02' is deprecated", 395, 9);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Job.experience' is deprecated", 414, 9);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Job.experience' is deprecated", 420, 9);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Job.experience' is deprecated", 426, 12);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Company.name' is deprecated", 433, 9);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Company.city' is deprecated", 434, 9);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Company.country' is deprecated", 435, 12);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'x2' is deprecated", 446, 15);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'x2' is deprecated", 447, 15);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'x2' is deprecated", 448, 20);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Company.city' is deprecated", 451, 9);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Company.country' is deprecated", 452, 9);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'Company.name' is deprecated", 453, 9);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'city' is deprecated", 458, 9);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'country' is deprecated", 459, 9);
BAssertUtil.validateWarning(compileResult, i++, "usage of construct 'name' is deprecated", 460, 9);
Assert.assertEquals(compileResult.getWarnCount(), i);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,16 +309,16 @@ type Employee record {|
type Job record {|
string title;
@deprecated
int experiance;
int experience;
|};

public function testDeprecatedRecordFields() {
Employee employee = {name: "John", id: 112, job: {title: "SE", experiance: 2}};
Employee employee = {name: "John", id: 112, job: {title: "SE", experience: 2}};
_ = employee.name; // warning
_ = employee.id;
_ = employee.job; // warning
_ = employee.job.title; // warning
_ = employee.job.experiance; // warning
_ = employee.job.experience; // warning
}

# Employee2 record
Expand All @@ -340,7 +340,7 @@ type Employee2 record {|
|};

public function testDeprecatedRecordFieldsWithDocumentation() {
Employee2 employee2 = {name: "John", id: 112, job: {title: "SE", experiance: 2}};
Employee2 employee2 = {name: "John", id: 112, job: {title: "SE", experience: 2}};
_ = employee2.name; // warning
_ = employee2.id;
_ = employee2.job; // warning
Expand Down Expand Up @@ -390,7 +390,73 @@ type Employee5 record {
};

public function testDeprecatedAnonStructAsStructField() {
Employee5 employee5 = {address: {}};
Employee5 employee5 = {address: {}}; // warning
_ = employee5.address; // warning
_ = employee5.address.line02; // warning
}

type Company record {
int companyId;

@deprecated
string name;

@deprecated
string city;

@deprecated
string country;
};

public function testDeprecatedAnonRecordFieldInInitialization() {
Job _ = {
title: "SE",
experience: 1 // warning
};

int experience = 2;
Job _ = {
title: "SE",
experience // warning
};

var details = {experience: 2};
Job _ = {
title: "SE",
...details // warning
};

string city = "Berlin";
var companyDetails = {country: "Germany"};
Company _ = {
companyId: 1,
name: "Foo", // warning
city: city, // warning
...companyDetails // warning
};
}

function fooFn(int x1, @deprecated int x2){
}

function barFn(string s, *Company company) {
}

public function testDeprecatedParamUsages() {
fooFn(10, 11);
fooFn(12, x2 = 13);
fooFn(x1 = 14, x2 = 15);
barFn("bar", {
companyId: 1,
city: "Berlin", // Warning
country: "Germany", // Warning
name: "Bar" // Warning
});
barFn(
"bar",
companyId = 1,
city = "Berlin", // Warning
country = "Germany", // Warning
name = "Bar" // Warning
dulajdilshan marked this conversation as resolved.
Show resolved Hide resolved
);
}
Loading