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

CheckStyle-Rule-Extension: 6, 7, 8 #4339

Merged
merged 48 commits into from
Jul 23, 2019
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
67fb33d
Service Client Instantiation Check rule
mssfang Jul 2, 2019
e9db7bb
add Extenal Dependentcy Exposed checks and checkstyle xml
mssfang Jul 3, 2019
8dd060b
Merge branch 'master' of https://github.com/Azure/azure-sdk-for-java …
mssfang Jul 3, 2019
d8633dc
revert back pom file
mssfang Jul 3, 2019
c428780
Merge branch 'master' of https://github.com/Azure/azure-sdk-for-java …
mssfang Jul 5, 2019
ab7e6a2
refactor 1
mssfang Jul 5, 2019
5c9652e
Use CheckUtil to get modifier values
mssfang Jul 5, 2019
74fc583
remove an extra useless boolean variable and remove unsued import
mssfang Jul 5, 2019
a03a4a4
refactor 2
mssfang Jul 7, 2019
49ebc74
Merge branch 'master' of https://github.com/Azure/azure-sdk-for-java …
mssfang Jul 7, 2019
3519b5e
Merge branch 'master' of https://github.com/Azure/azure-sdk-for-java …
mssfang Jul 8, 2019
6daf85c
updates based on J's feedback
mssfang Jul 9, 2019
cf185d5
Merge branch 'master' of https://github.com/Azure/azure-sdk-for-java …
mssfang Jul 9, 2019
d4b2e37
revise of wording
mssfang Jul 9, 2019
36f5a17
Merge branch 'master' of https://github.com/Azure/azure-sdk-for-java …
mssfang Jul 9, 2019
664a950
draft version of rule 5, 6, 7
mssfang Jul 9, 2019
6e9ca65
refactor 1
mssfang Jul 9, 2019
183222a
refactor ServiceInterface annotation check
mssfang Jul 9, 2019
9fa4a16
refactor Service Client Builder check class
mssfang Jul 9, 2019
360699c
Merge branch 'master' of https://github.com/Azure/azure-sdk-for-java …
mssfang Jul 9, 2019
d2377c7
resolve conflict
mssfang Jul 10, 2019
a5aac06
Refactor ServiceClientBuilder and ServiceInterface checks
mssfang Jul 10, 2019
036787c
merge Service Client Method check to Service Client check
mssfang Jul 10, 2019
484dd98
suppression implementation and test but not test my test yet
mssfang Jul 10, 2019
228a3ad
add license title
mssfang Jul 10, 2019
a4720d0
ServiceInterface check tested. Looks good
mssfang Jul 10, 2019
df06858
tested ServiceClientBuilder check
mssfang Jul 11, 2019
8610ddf
revive wording
mssfang Jul 11, 2019
634c1f6
revive wording
mssfang Jul 11, 2019
f27f579
rm rule #5 since it is not ready for review
mssfang Jul 11, 2019
0180b9f
resolve conflict
mssfang Jul 11, 2019
ea745cf
without ServiceMethod but keep ServiceClient annotation
mssfang Jul 11, 2019
fe12876
remove debugging and comment for rule 5
mssfang Jul 11, 2019
8f0a8c9
rule 8 added
mssfang Jul 11, 2019
4af0d09
Merge branch 'master' of https://github.com/Azure/azure-sdk-for-java …
mssfang Jul 11, 2019
3ee8361
resolve conflict
mssfang Jul 11, 2019
2563084
refacor based on srnagar's feedback
mssfang Jul 11, 2019
128690b
add rule 9
mssfang Jul 11, 2019
b6e2347
only track 2 and correct path
mssfang Jul 11, 2019
aba32d2
updates ThrowClientLoggerCheck
mssfang Jul 12, 2019
f77acdb
move out ThrownClientLoggerCheck
mssfang Jul 12, 2019
d18e3a1
comment out ThrownClientLoggerCheck
mssfang Jul 12, 2019
bdb6cde
fixes for KV and no other method has prefix build other than buildCli…
mssfang Jul 12, 2019
e1334b1
resolve conflict
mssfang Jul 12, 2019
66e4594
Merge branch 'master' of https://github.com/Azure/azure-sdk-for-java …
mssfang Jul 18, 2019
408b77c
starting with build and ending with Client
mssfang Jul 18, 2019
e3842ad
revert kv changes and update limit character to 20
mssfang Jul 23, 2019
4b98e06
updates comments
mssfang Jul 23, 2019
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
@@ -0,0 +1,121 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.tools.checkstyle.checks;

import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;

import java.util.Stack;

/**
* The @ServiceClientBuilder class should have the following rules:
* 1) All service client builder should be named <ServiceName>ClientBuilder and annotated with @ServiceClientBuilder.
* 2) No other method have prefix 'build' other than 'buildClient' or 'buildAsyncClient'.
*/
public class ServiceClientBuilderCheck extends AbstractCheck {
private static final String SERVICE_CLIENT_BUILDER = "ServiceClientBuilder";
private static final String BUILD_CLIENT = "buildClient";
private static final String BUILD_ASYNC_CLIENT = "buildAsyncClient";

private Stack<Boolean> hasServiceClientBuilderAnnotationStack = new Stack();
private boolean hasServiceClientBuilderAnnotation;

@Override
public int[] getDefaultTokens() {
return getRequiredTokens();
}

@Override
public int[] getAcceptableTokens() {
return getRequiredTokens();
}

@Override
public int[] getRequiredTokens() {
return new int[] {
TokenTypes.CLASS_DEF,
TokenTypes.METHOD_DEF
};
}

@Override
public void leaveToken(DetailAST token) {
switch (token.getType()) {
case TokenTypes.CLASS_DEF:
mssfang marked this conversation as resolved.
Show resolved Hide resolved
// end of CLASS_DEF node, reset the value back to previous state
hasServiceClientBuilderAnnotation = hasServiceClientBuilderAnnotationStack.pop();
break;
default:
// Checkstyle complains if there's no default block in switch
break;
}
}

@Override
public void visitToken(DetailAST token) {
switch (token.getType()) {
case TokenTypes.CLASS_DEF:
// Save the state of variable 'hasServiceClientBuilderAnnotation' to limit the scope of accessibility
hasServiceClientBuilderAnnotationStack.push(hasServiceClientBuilderAnnotation);

final DetailAST serviceClientAnnotationBuilderToken = getServiceClientBuilderAnnotation(token);
final String className = token.findFirstToken(TokenTypes.IDENT).getText();

hasServiceClientBuilderAnnotation = serviceClientAnnotationBuilderToken != null;
if (hasServiceClientBuilderAnnotation) {
// Don't need to check if the 'serviceClients' exist. It is required when using @ServiceClientBuilder

// HAS @ServiceClientBuilder annotation but NOT named the class <ServiceName>ClientBuilder
if (!className.endsWith("ClientBuilder")) {
log(token, String.format("Class annotated with @ServiceClientBuilder ''%s'' should be named <ServiceName>ClientBuilder.", className));
}
} else {
// No @ServiceClientBuilder annotation but HAS named the class <ServiceName>ClientBuilder
if (className.endsWith("ClientBuilder")) {
log(token, String.format("Class ''%s'' should be annotated with @ServiceClientBuilder.", className));
}
}
break;
case TokenTypes.METHOD_DEF:
if (!hasServiceClientBuilderAnnotation) {
mssfang marked this conversation as resolved.
Show resolved Hide resolved
return;
}

final String methodName = token.findFirstToken(TokenTypes.IDENT).getText();
// method name has prefix 'build' but not 'buildClient' or 'buildAsyncClient'
if (methodName.startsWith("build") && !BUILD_ASYNC_CLIENT.equals(methodName) && !BUILD_CLIENT.equals(methodName)) {
mssfang marked this conversation as resolved.
Show resolved Hide resolved
log(token, String.format(
"@ServiceClientBuilder class should not have a method name, '''' starting with ''build'' " +
"other than ''buildClient'' or ''buildAsyncClient''." , methodName));
}

break;
default:
// Checkstyle complains if there's no default block in switch
break;
}
}

/**
* Checks if the class is annotated with @ServiceClientBuilder.
*
* @param classDefToken the CLASS_DEF AST node
* @return the annotation node if the class is annotated with @ServiceClientBuilder, null otherwise.
*/
private DetailAST getServiceClientBuilderAnnotation(DetailAST classDefToken) {
final DetailAST modifiersToken = classDefToken.findFirstToken(TokenTypes.MODIFIERS);

if (!modifiersToken.branchContains(TokenTypes.ANNOTATION)) {
return null;
}

DetailAST annotationToken = modifiersToken.findFirstToken(TokenTypes.ANNOTATION);
if (!SERVICE_CLIENT_BUILDER.equals(annotationToken.findFirstToken(TokenTypes.IDENT).getText())) {
return null;
}

return annotationToken;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.tools.checkstyle.checks;

import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;

import java.util.regex.Pattern;

/**
* The @ServiceInterface class should have the following rules:
* 1) The annotation property 'name' should be non-empty
* 2) The length of value of property 'name' should be less than 10 characters and without space
*/
public class ServiceInterfaceCheck extends AbstractCheck {
@Override
public int[] getDefaultTokens() {
return getRequiredTokens();
}

@Override
public int[] getAcceptableTokens() {
return getRequiredTokens();
}

@Override
public int[] getRequiredTokens() {
return new int[] {
TokenTypes.INTERFACE_DEF
};
}

@Override
public void visitToken(DetailAST token) {
switch (token.getType()) {
case TokenTypes.INTERFACE_DEF:
mssfang marked this conversation as resolved.
Show resolved Hide resolved
checkServiceInterface(token);
break;
default:
// Checkstyle complains if there's no default block in switch
break;
}
}

/**
* The @ServiceInterface class should have the following rules:
* 1) The annotation property 'name' should be non-empty
* 2) The length of value of property 'name' should be less than 10 characters and without space
*
* @param interfaceDefToken INTERFACE_DEF AST node
*/
private void checkServiceInterface(DetailAST interfaceDefToken) {
DetailAST serviceInterfaceAnnotationNode = null;
String nameValue = null;

DetailAST modifiersToken = interfaceDefToken.findFirstToken(TokenTypes.MODIFIERS);
// Find the @ServiceInterface and the property 'name' and the corresponding value
for (DetailAST ast = modifiersToken.getFirstChild(); ast != null; ast = ast.getNextSibling()) {
mssfang marked this conversation as resolved.
Show resolved Hide resolved
// We care about only the ANNOTATION type
if (ast.getType() != TokenTypes.ANNOTATION) {
continue;
}
// Skip if not @ServiceInterface annotation
if (!"ServiceInterface".equals(ast.findFirstToken(TokenTypes.IDENT).getText())) {
continue;
}

// Get the @ServiceInterface annotation node
serviceInterfaceAnnotationNode = ast;

// Get the 'name' property value of @ServiceInterface
// @ServiceInterface requires 'name' property
DetailAST annotationMemberValuePairToken = ast.findFirstToken(TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR);
if ("name".equals(annotationMemberValuePairToken.findFirstToken(TokenTypes.IDENT).getText())) {
nameValue = getNamePropertyValue(annotationMemberValuePairToken.findFirstToken(TokenTypes.EXPR));
break;
}
}

// Checks the rules:
// Skip the check if no @ServiceInterface annotation found
if (serviceInterfaceAnnotationNode == null) {
return;
}

// 'name' is required at @ServiceInterface
// 'name' should not be empty, no Space allowed and the length should less than or equal to 10 characters
Pattern serviceNamePattern = Pattern.compile("^[a-zA-Z0-9]{1,10}$");
if (!serviceNamePattern.matcher(nameValue).find()) {
log(serviceInterfaceAnnotationNode, String.format(
"The ''name'' property of @ServiceInterface, ''%s'' should be non-empty, alphanumeric and not more than 10 characters",
nameValue));
}
}

/**
* Get the name property value from the EXPR node
*
* @param exprToken EXPR
* @return null if EXPR node doesn't exist or no STRING_LITERAL. Otherwise, returns the value of the property.
*/
private String getNamePropertyValue(DetailAST exprToken) {
if (exprToken == null) {
return null;
}

final DetailAST nameValueToken = exprToken.findFirstToken(TokenTypes.STRING_LITERAL);
if (nameValueToken == null) {
return null;
}

String nameValue = nameValueToken.getText();

// remove the beginning and ending double quote
return nameValue.replaceAll("^\"|\"$", "");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,23 @@
<!-- Custom checkstyle rules only check track 2 libraries -->
<suppress checks="com.azure.tools.checkstyle.checks.ExternalDependencyExposedCheck" files=".*[/\\]com[/\\]microsoft[/\\].*"/>
<suppress checks="com.azure.tools.checkstyle.checks.ServiceClientInstantiationCheck" files=".*[/\\]com[/\\]microsoft[/\\].*"/>
<suppress checks="com.azure.tools.checkstyle.checks.ThrownClientLoggerCheck" files=".*[/\\]com[/\\]microsoft[/\\].*"/>

<!-- Custom checkstyle rules that don't apply to files under test or implementation package -->
<suppress checks="com.azure.tools.checkstyle.checks.ExternalDependencyExposedCheck" files="(.*[/\\]src[/\\]test[/\\]java[/\\].*)|(.*[/\\]implementation[/\\].*)\.java"/>
<suppress checks="com.azure.tools.checkstyle.checks.ServiceClientInstantiationCheck" files="(.*[/\\]src[/\\]test[/\\]java[/\\].*)|(.*[/\\]implementation[/\\].*)\.java"/>
<suppress checks="com.azure.tools.checkstyle.checks.ServiceClientBuilderCheck" files="(.*[/\\]src[/\\]test[/\\]java[/\\].*)|(.*[/\\]implementation[/\\].*)\.java"/>
<suppress checks="com.azure.tools.checkstyle.checks.ServiceInterfaceCheck" files="(.*[/\\]src[/\\]test[/\\]java[/\\].*)|(.*[/\\]implementation[/\\].*)\.java"/>

<!-- OpenCensus should only be depended on from within the tracing-opencensus module -->
<suppress checks="IllegalImport" files=".*[/\\]com[/\\]azure[/\\]tracing[/\\]opentelemetry[/\\]*"/>

<!-- Any code in any package, it should never be a 'throw' keyword in the client library codebase except for in the client logger -->
<!-- <suppress checks="com.azure.tools.checkstyle.checks.ThrownClientLoggerCheck" files=".*[/\\]com[/\\]azure[/\\]core[/\\]util[/\\]logging[/\\]*"/>-->

<!-- Suppression for throws IOException Class, Will remove these classes after be able to define the error is IOException -->
<!-- <suppress checks="com.azure.tools.checkstyle.checks.ThrownClientLoggerCheck" files=".*[/\\]src[/\\]test[/\\]java[/\\]com[/\\]azure[/\\]core[/\\]implementation[/\\](.*RestProxyStressTests[/\\].*)|(.*util[/\\]FluxUtilTests[/\\].*)\.java"/>-->

<!-- Storage still depends on 1.0.0-preview.2 azure core. suppress this rule for now until storage has upgraded to preview-3 azure core-->
<suppress checks="com.azure.tools.checkstyle.checks.ServiceClientBuilderCheck" files=".*[/\\]storage[/\\].*\.java"/>
</suppressions>
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ page at http://checkstyle.sourceforge.net/config.html -->
<module name="AvoidStarImport"/>
<module name="IllegalImport">
<property name="regexp" value="true"/>
<property name="illegalPkgs" value="(com\.)?sun\.\w*"/>
<property name="illegalPkgs" value="^(com\.)?sun\.\w*, ^io\.opencensus"/>
</module>
<module name="RedundantImport"/>
<module name="UnusedImports"/>
Expand Down Expand Up @@ -290,5 +290,22 @@ page at http://checkstyle.sourceforge.net/config.html -->
We should only return types, and accept argument types, that are from the com.azure namespace.
All other types should have suppression added if required. -->
<module name="com.azure.tools.checkstyle.checks.ExternalDependencyExposedCheck"/>

<!--CUSTOM CHECKS-->
<!-- The @ServiceClientBuilder class should have the following rules:
1) All service client builder should be named <ServiceName>ClientBuilder and annotated with @ServiceClientBuilder.
2) Has a method 'buildClient()' to build a synchronous client,
3) Has a method 'buildAsyncClient()' to build an asynchronous client -->
<module name="com.azure.tools.checkstyle.checks.ServiceClientBuilderCheck"/>

<!--CUSTOM CHECKS-->
<!-- The @ServiceInterface class should have the following rules:
1) The annotation property 'name' should be non-empty
2) The length of value of property 'name' should be less than 10 characters and without space -->
<module name="com.azure.tools.checkstyle.checks.ServiceInterfaceCheck"/>

<!--CUSTOM CHECKS-->
<!-- Must use 'logger.logAndThrow' but not directly calling 'throw exception' -->
<!-- <module name="com.azure.tools.checkstyle.checks.ThrownClientLoggerCheck"/>-->
</module>
</module>
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
* This is package-private so that these REST calls are transparent to the user.
*/
@Host("{url}")
@ServiceInterface(name = "KeyVaultSecrets")
@ServiceInterface(name = "KVSecrets")
mssfang marked this conversation as resolved.
Show resolved Hide resolved
interface SecretService {

@Put("secrets/{secret-name}")
Expand Down