| /* |
| * Copyright 2017 The Bazel Authors. All rights reserved. |
| * |
| * 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. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.google.idea.blaze.base.lang.buildfile.validation; |
| |
| import com.google.common.base.Joiner; |
| import com.google.idea.blaze.base.lang.buildfile.language.semantics.AttributeDefinition; |
| import com.google.idea.blaze.base.lang.buildfile.language.semantics.BuildLanguageSpec; |
| import com.google.idea.blaze.base.lang.buildfile.language.semantics.BuildLanguageSpecProvider; |
| import com.google.idea.blaze.base.lang.buildfile.language.semantics.RuleDefinition; |
| import com.google.idea.blaze.base.lang.buildfile.psi.Argument; |
| import com.google.idea.blaze.base.lang.buildfile.psi.Expression; |
| import com.google.idea.blaze.base.lang.buildfile.psi.FuncallExpression; |
| import com.google.idea.blaze.base.lang.buildfile.psi.util.PsiUtils; |
| import com.intellij.psi.PsiElement; |
| import java.util.Set; |
| import java.util.TreeSet; |
| |
| /** Validation of known rule types. */ |
| public class BuiltInRuleAnnotator extends BuildAnnotator { |
| |
| @Override |
| public void visitFuncallExpression(FuncallExpression node) { |
| BuildLanguageSpec spec = |
| BuildLanguageSpecProvider.getInstance().getLanguageSpec(node.getProject()); |
| if (spec == null) { |
| return; |
| } |
| String ruleName = node.getFunctionName(); |
| RuleDefinition rule = spec.getRule(ruleName); |
| if (rule == null) { |
| return; |
| } |
| if (node.getReferencedElement() != null) { |
| // this has been locally overridden, so don't attempt validation |
| return; |
| } |
| Set<String> missingAttributes = new TreeSet<>(rule.mandatoryAttributes.keySet()); |
| for (Argument arg : node.getArguments()) { |
| if (arg instanceof Argument.StarStar) { |
| missingAttributes.clear(); |
| continue; |
| } |
| String name = arg.getName(); |
| if (name == null) { |
| continue; |
| } |
| AttributeDefinition attribute = rule.getAttribute(name); |
| if (attribute == null) { |
| markError( |
| arg, String.format("Unrecognized attribute '%s' for rule type '%s'", name, ruleName)); |
| continue; |
| } |
| missingAttributes.remove(name); |
| Expression argValue = arg.getValue(); |
| if (argValue == null) { |
| continue; |
| } |
| PsiElement rootElement = PsiUtils.getReferencedTargetValue(argValue); |
| if (!BuildElementValidation.possiblyValidType(rootElement, attribute.type)) { |
| markError( |
| arg, |
| String.format( |
| "Invalid value for attribute '%s'. Expected a value of type '%s'", |
| name, attribute.type)); |
| } |
| } |
| if (!missingAttributes.isEmpty()) { |
| markError( |
| node, |
| String.format( |
| "Target missing required attribute(s): %s", Joiner.on(',').join(missingAttributes))); |
| } |
| } |
| } |